diff --git a/kernel-modules/mlnx-ofa_kernel/centos/build_srpm.data b/kernel-modules/mlnx-ofa_kernel/centos/build_srpm.data new file mode 100644 index 00000000..b2fc668e --- /dev/null +++ b/kernel-modules/mlnx-ofa_kernel/centos/build_srpm.data @@ -0,0 +1,3 @@ +COPY_LIST="$PKG_BASE/files/modules-load.conf" +TIS_PATCH_VER=1 +BUILD_IS_SLOW=3 diff --git a/kernel-modules/mlnx-ofa_kernel/centos/meta_patches/0001-Support-TiS-system.patch b/kernel-modules/mlnx-ofa_kernel/centos/meta_patches/0001-Support-TiS-system.patch new file mode 100644 index 00000000..4c3572be --- /dev/null +++ b/kernel-modules/mlnx-ofa_kernel/centos/meta_patches/0001-Support-TiS-system.patch @@ -0,0 +1,196 @@ +From 88ea759637bfe3359375e86dd698a1efbf274784 Mon Sep 17 00:00:00 2001 +Message-Id: <88ea759637bfe3359375e86dd698a1efbf274784.1527782348.git.Jim.Somerville@windriver.com> +From: eric zhang +Date: Wed, 14 Mar 2018 15:52:15 -0400 +Subject: [PATCH 1/1] Support TiS system + +This patch added the following to support TiS system: +1. Support package versioning for TiS format +2. Add rt support +3. Compile fix in dcbnl.h +4. Fix compile for some ks stuff +5. Fix compile neuter some stats output +6. Load all kernel modules +7. Sign kernel module + +Signed-off-by: Allain Legacy +Signed-off-by: Jim Somerville +Signed-off-by: Kam Nasim +Signed-off-by: eric zhang +--- + SPECS/mlnx-ofa_kernel.spec | 49 +++++++++++++++++++++++++++++++--------------- + 1 file changed, 33 insertions(+), 16 deletions(-) + +diff --git a/SPECS/mlnx-ofa_kernel.spec b/SPECS/mlnx-ofa_kernel.spec +index 33fe911..7a9253d 100644 +--- a/SPECS/mlnx-ofa_kernel.spec ++++ b/SPECS/mlnx-ofa_kernel.spec +@@ -27,8 +27,14 @@ + # + + # KMP is disabled by default +-%{!?KMP: %global KMP 0} ++%if "%{_tis_build_type}" == "rt" ++%define bt_ext -rt ++%else ++%undefine bt_ext ++%endif + ++%{!?KMP: %global KMP 0} ++BuildRequires: kernel%{?bt_ext}-devel, openssl + %global WITH_SYSTEMD %(if ( test -d "%{_unitdir}" > /dev/null); then echo -n '1'; else echo -n '0'; fi) + + %{!?configure_options: %global configure_options --with-core-mod --with-user_mad-mod --with-user_access-mod --with-addr_trans-mod --with-mlx4-mod --with-mlx4_en-mod --with-mlx5-mod --with-mlxfw-mod --with-ipoib-mod} +@@ -41,12 +47,11 @@ + %global BLUENIX %(if (grep -qiE "Bluenix" /etc/issue /etc/*release* 2>/dev/null); then echo -n '1'; else echo -n '0'; fi) + %global XENSERVER65 %(if (grep -qiE "XenServer.*6\.5" /etc/issue /etc/*release* 2>/dev/null); then echo -n '1'; else echo -n '0'; fi) + +-%{!?KVERSION: %global KVERSION %(uname -r)} ++%{!?KVERSION: %global KVERSION %(rpm -q kernel%{?bt_ext}-devel | sort --version-sort | tail -1 | sed 's/kernel%{?bt_ext}-devel-//')} + %global kernel_version %{KVERSION} + %global krelver %(echo -n %{KVERSION} | sed -e 's/-/_/g') + # take path to kernel sources if provided, otherwise look in default location (for non KMP rpms). +-%{!?K_SRC: %global K_SRC /lib/modules/%{KVERSION}/build} +- ++%{!?K_SRC: %global K_SRC /usr/src/kernels/%{KVERSION}} + # Select packages to build + + # Kernel module packages to be included into kernel-ib +@@ -62,7 +67,9 @@ + + %{!?KERNEL_SOURCES: %global KERNEL_SOURCES /lib/modules/%{KVERSION}/source} + +-%{!?_name: %global _name mlnx-ofa_kernel} ++ ++%define _basename mlnx-ofa_kernel ++%define _name %{_basename}%{?bt_ext} + %{!?_version: %global _version 4.3} + %{!?_release: %global _release OFED.4.3.3.0.2.1.gcf60532} + %global _kmp_rel %{_release}%{?_kmp_build_num}%{?_dist} +@@ -74,11 +81,16 @@ + Summary: Infiniband HCA Driver + Name: %{_name} + Version: %{_version} +-Release: %{_release}%{?_dist} ++Release: %{_release}%{?_tis_dist}.%{tis_patch_ver} + License: GPLv2 + Url: http://www.mellanox.com/ + Group: System Environment/Base +-Source: %{_name}-%{_version}.tgz ++Source: %{_basename}-%{_version}.tgz ++Source100: modules-load.conf ++Patch01: 0001-neuter-HAVE_IEEE_GETQCN.patch ++Patch02: 0001-neuter-a-bunch-of-ks-stuff.patch ++Patch03: 0001-neuter-some-stats-output.patch ++ + BuildRoot: %{?build_root:%{build_root}}%{!?build_root:/var/tmp/OFED} + Vendor: Mellanox Technologies + Obsoletes: kernel-ib +@@ -133,7 +145,6 @@ EOF) + %global kernel_release() %{KVERSION} + %global flavors_to_build default + %package -n %{non_kmp_pname} +-Requires: %{utils_pname} + Requires: coreutils + Requires: pciutils + Requires: grep +@@ -160,7 +171,7 @@ Obsoletes: mlnx-en-doc + Obsoletes: mlnx-en-debuginfo + Obsoletes: mlnx-en-sources + Version: %{_version} +-Release: %{_release}.kver.%{krelver} ++Release: %{_release}%{?_tis_dist}.%{tis_patch_ver} + Summary: Infiniband Driver and ULPs kernel modules + Group: System Environment/Libraries + %description -n %{non_kmp_pname} +@@ -172,7 +183,7 @@ The driver sources are located at: http://www.mellanox.com/downloads/ofed/mlnx-o + %package -n %{devel_pname} + Version: %{_version} + # build KMP rpms? +-Release: %{_release}%{?_dist} ++Release: %{_release}%{?_dist}%{?_tis_dist}.%{tis_patch_ver} + Obsoletes: kernel-ib-devel + Obsoletes: compat-rdma-devel + Obsoletes: kernel-ib +@@ -209,13 +220,12 @@ The driver sources are located at: http://www.mellanox.com/downloads/ofed/mlnx-o + else \ + echo -n '0'; fi) + +-%if "%{WITH_MOD_SIGN}" == "1" + # call module sign script + %global __modsign_install_post \ + %{_builddir}/$NAME-$VERSION/source/ofed_scripts/tools/sign-modules %{buildroot}/lib/modules/ %{kernel_source default} || exit 1 \ + %{nil} + +-%global __debug_package 1 ++%define debug_package %{nil} + %global buildsubdir %{_name}-%{version} + # Disgusting hack alert! We need to ensure we sign modules *after* all + # invocations of strip occur, which is in __debug_install_post if +@@ -228,7 +238,6 @@ The driver sources are located at: http://www.mellanox.com/downloads/ofed/mlnx-o + %{__modsign_install_post} \ + %{nil} + +-%endif # end of setup module sign scripts + # + %if "%{_vendor}" == "suse" + %debug_package +@@ -259,12 +268,16 @@ The driver sources are located at: http://www.mellanox.com/downloads/ofed/mlnx-o + %{!?install_mod_dir: %global install_mod_dir updates} + + %prep +-%setup -n %{_name}-%{_version} ++%setup -n %{_basename}-%{_version} + set -- * + mkdir source + mv "$@" source/ + mkdir obj + ++%patch01 -p1 ++%patch02 -p1 ++%patch03 -p1 ++ + %build + export EXTRA_CFLAGS='-DVERSION=\"%version\"' + export INSTALL_MOD_DIR=%{install_mod_dir} +@@ -279,7 +292,6 @@ for flavor in %flavors_to_build; do + find compat -type f -exec touch -t 200012201010 '{}' \; || true + ./configure --build-dummy-mods --prefix=%{_prefix} --kernel-version $KVERSION --kernel-sources $KSRC --modules-dir $LIB_MOD_DIR $CONF_OPTIONS %{?_smp_mflags} + make %{?_smp_mflags} kernel +- make build_py_scripts + cd - + done + +@@ -288,9 +300,11 @@ touch ofed-files + export RECORD_PY_FILES=1 + export INSTALL_MOD_PATH=%{buildroot} + export INSTALL_MOD_DIR=%{install_mod_dir} +-export NAME=%{name} ++export NAME=%{_basename} + export VERSION=%{version} + export PREFIX=%{_prefix} ++export MODULE_SIGN_PRIV_KEY=/usr/src/kernels/%{KVERSION}/signing_key.priv ++export MODULE_SIGN_PUB_KEY=/usr/src/kernels/%{KVERSION}/signing_key.x509 + for flavor in %flavors_to_build; do + export KSRC=%{kernel_source $flavor} + export KVERSION=%{kernel_release $KSRC} +@@ -340,6 +354,8 @@ echo "override ${mod_name} * weak-updates/%{_name}${mod_path}" >> %{buildroot}%{ + echo "override ${mod_name} * extra/%{_name}${mod_path}" >> %{buildroot}%{_sysconfdir}/depmod.d/zz01-%{_name}-${mod_name}.conf + done + %endif ++%{__install} -d %{buildroot}%{_sysconfdir}/modules-load.d ++%{__install} -m 644 %{SOURCE100} %{buildroot}%{_sysconfdir}/modules-load.d/mlnx.conf + %endif + + # copy sources +@@ -680,6 +696,7 @@ fi + %config(noreplace) %{_sysconfdir}/depmod.d/zz01-%{_name}-*.conf + %endif + %endif ++%{_sysconfdir}/modules-load.d/mlnx.conf + %endif + + %files -n %{devel_pname} +-- +1.8.3.1 + diff --git a/kernel-modules/mlnx-ofa_kernel/centos/meta_patches/PATCH_ORDER b/kernel-modules/mlnx-ofa_kernel/centos/meta_patches/PATCH_ORDER new file mode 100644 index 00000000..99ee2d18 --- /dev/null +++ b/kernel-modules/mlnx-ofa_kernel/centos/meta_patches/PATCH_ORDER @@ -0,0 +1 @@ +0001-Support-TiS-system.patch diff --git a/kernel-modules/mlnx-ofa_kernel/centos/patches/0001-neuter-HAVE_IEEE_GETQCN.patch b/kernel-modules/mlnx-ofa_kernel/centos/patches/0001-neuter-HAVE_IEEE_GETQCN.patch new file mode 100644 index 00000000..54421348 --- /dev/null +++ b/kernel-modules/mlnx-ofa_kernel/centos/patches/0001-neuter-HAVE_IEEE_GETQCN.patch @@ -0,0 +1,26 @@ +From e1b8dd12605b5654bd94011cb6c587fd0c0bc9af Mon Sep 17 00:00:00 2001 +From: Jim Somerville +Date: Tue, 20 Dec 2016 11:58:26 -0500 +Subject: [PATCH 2/3] neuter HAVE_IEEE_GETQCN + +Signed-off-by: Jim Somerville +--- + source/include/linux/dcbnl.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/source/include/linux/dcbnl.h b/source/include/linux/dcbnl.h +index 2c8ca62..eabf6da 100644 +--- a/source/include/linux/dcbnl.h ++++ b/source/include/linux/dcbnl.h +@@ -5,7 +5,7 @@ + + #include_next + +-#ifndef HAVE_IEEE_GETQCN ++#if 0 + + #ifndef HAVE_STRUCT_IEEE_QCN + enum dcbnl_cndd_states { +-- +1.8.3.1 + diff --git a/kernel-modules/mlnx-ofa_kernel/centos/patches/0001-neuter-a-bunch-of-ks-stuff.patch b/kernel-modules/mlnx-ofa_kernel/centos/patches/0001-neuter-a-bunch-of-ks-stuff.patch new file mode 100644 index 00000000..f1bd7a20 --- /dev/null +++ b/kernel-modules/mlnx-ofa_kernel/centos/patches/0001-neuter-a-bunch-of-ks-stuff.patch @@ -0,0 +1,47 @@ +From 63f73a02fd73f6a01bce1fbf65ae2a6a628f0626 Mon Sep 17 00:00:00 2001 +From: Jim Somerville +Date: Tue, 20 Dec 2016 16:22:47 -0500 +Subject: [PATCH 1/3] neuter a bunch of ks stuff + +Signed-off-by: Jim Somerville +--- + source/include/linux/compat-3.17.h | 2 +- + source/include/linux/compat-4.0.h | 2 ++ + 2 files changed, 3 insertions(+), 1 deletion(-) + +diff --git a/source/include/linux/compat-3.17.h b/source/include/linux/compat-3.17.h +index 6a5ff3b..ad799f0 100644 +--- a/source/include/linux/compat-3.17.h ++++ b/source/include/linux/compat-3.17.h +@@ -6,7 +6,7 @@ + + #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,17,0)) + +-#ifndef HAVE_KTIME_GET_REAL_NS ++#if 0 + #include + #include + static inline u64 ktime_get_real_ns(void) { +diff --git a/source/include/linux/compat-4.0.h b/source/include/linux/compat-4.0.h +index b3c37aa..900d49a 100644 +--- a/source/include/linux/compat-4.0.h ++++ b/source/include/linux/compat-4.0.h +@@ -6,6 +6,7 @@ + #if (LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0)) + #include + ++#if 0 + #define kstrdup_const LINUX_BACKPORT(kstrdup_const) + static inline const char *kstrdup_const(const char *s, gfp_t gfp) + { +@@ -21,6 +22,7 @@ static inline void kfree_const(const void *x) + kfree(x); + } + #endif ++#endif + #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0)) */ + + #endif /* LINUX_4_0_COMPAT_H */ +-- +1.8.3.1 + diff --git a/kernel-modules/mlnx-ofa_kernel/centos/patches/0001-neuter-some-stats-output.patch b/kernel-modules/mlnx-ofa_kernel/centos/patches/0001-neuter-some-stats-output.patch new file mode 100644 index 00000000..ca709b98 --- /dev/null +++ b/kernel-modules/mlnx-ofa_kernel/centos/patches/0001-neuter-some-stats-output.patch @@ -0,0 +1,31 @@ +From 12de070f8f73794e45ee6956c9957d0ff0c5133f Mon Sep 17 00:00:00 2001 +From: Jim Somerville +Date: Tue, 20 Dec 2016 16:56:01 -0500 +Subject: [PATCH 3/3] neuter some stats output + +Signed-off-by: Jim Somerville +--- + source/drivers/net/ethernet/mellanox/mlx4/en_sysfs.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/source/drivers/net/ethernet/mellanox/mlx4/en_sysfs.c b/source/drivers/net/ethernet/mellanox/mlx4/en_sysfs.c +index cb7c829..21f5fc6 100644 +--- a/source/drivers/net/ethernet/mellanox/mlx4/en_sysfs.c ++++ b/source/drivers/net/ethernet/mellanox/mlx4/en_sysfs.c +@@ -186,11 +186,13 @@ static ssize_t mlx4_en_show_qcnstats(struct device *d, + len += sprintf(buf + len, "%s %d %s", "priority", i, ": "); + len += sprintf(buf + len, "%lld ", qcn_stats.rppp_rp_centiseconds[i]); + len += sprintf(buf + len, "%u ", qcn_stats.rppp_created_rps[i]); ++#if 0 + len += sprintf(buf + len, "%u ", qcn_stats.ignored_cnm[i]); + len += sprintf(buf + len, "%u ", qcn_stats.estimated_total_rate[i]); + len += sprintf(buf + len, "%u ", qcn_stats.cnms_handled_successfully[i]); + len += sprintf(buf + len, "%u ", qcn_stats.min_total_limiters_rate[i]); + len += sprintf(buf + len, "%u ", qcn_stats.max_total_limiters_rate[i]); ++#endif + len += sprintf(buf + len, "%s", "|"); + } + len += sprintf(buf + len, "\n"); +-- +1.8.3.1 + diff --git a/kernel-modules/mlnx-ofa_kernel/centos/srpm_path b/kernel-modules/mlnx-ofa_kernel/centos/srpm_path new file mode 100644 index 00000000..2b3d165e --- /dev/null +++ b/kernel-modules/mlnx-ofa_kernel/centos/srpm_path @@ -0,0 +1 @@ +repo:stx/downloads/mlnx-ofa_kernel-4.3-OFED.4.3.3.0.2.1.gcf60532.src.rpm diff --git a/kernel-modules/mlnx-ofa_kernel/files/modules-load.conf b/kernel-modules/mlnx-ofa_kernel/files/modules-load.conf new file mode 100644 index 00000000..0e35f5f6 --- /dev/null +++ b/kernel-modules/mlnx-ofa_kernel/files/modules-load.conf @@ -0,0 +1,22 @@ +mlx_compat +ib_cm +ib_core +ib_ucm +ib_uverbs +iw_cm +rdma_cm +rdma_ucm +mlx4_ib +mlx5_ib +rdma_rxe +ib_iser +ib_isert +ib_srp +mlx4_core +mlx4_en +mlx5_core +nvme-rdma +nvmet-rdma +rpcrdma +svcrdma +xprtrdma diff --git a/kernel-rt/centos/build_srpm.data b/kernel-rt/centos/build_srpm.data new file mode 100644 index 00000000..540aa5a4 --- /dev/null +++ b/kernel-rt/centos/build_srpm.data @@ -0,0 +1,4 @@ +COPY_LIST="files/*" +TIS_PATCH_VER=42 +BUILD_IS_BIG=10 +BUILD_IS_SLOW=12 diff --git a/kernel-rt/centos/meta_patches/Build-logic-and-sources-for-TiC.patch b/kernel-rt/centos/meta_patches/Build-logic-and-sources-for-TiC.patch new file mode 100644 index 00000000..a6d85e32 --- /dev/null +++ b/kernel-rt/centos/meta_patches/Build-logic-and-sources-for-TiC.patch @@ -0,0 +1,527 @@ +From 779c7986900ecc5dcc736c46b80690df8626746b Mon Sep 17 00:00:00 2001 +From: Jim Somerville +Date: Mon, 23 Apr 2018 15:18:45 -0400 +Subject: [PATCH 1/5] Build logic and sources for TiC + +Signed-off-by: Jim Somerville +--- + SPECS/kernel-rt.spec | 279 +++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 272 insertions(+), 7 deletions(-) + +diff --git a/SPECS/kernel-rt.spec b/SPECS/kernel-rt.spec +index dc814f7..4dc3361 100644 +--- a/SPECS/kernel-rt.spec ++++ b/SPECS/kernel-rt.spec +@@ -17,23 +17,33 @@ Summary: The Linux Realtime kernel + + # The Build ID + # %%define buildid .local ++%define buildid .tis.%{tis_patch_ver} + + # For a kernel released for public testing, released_kernel should be 1. + # For internal testing builds during development, it should be 0. + %global released_kernel 1 + ++# If we want to sign the kernel and modules, do_sign should be 1. ++# To speed up builds (don't sign) use 0. ++%global do_sign 1 ++ + # conditional with/without variables + # Note that the logic here is inverted; a bcond_without implies + # that the variable is on by default (since you use --without to turn it off) + # Likewise a bcond_with implies the variable is off by default (turned on by --with) + %bcond_without rt + %bcond_without doc +-%bcond_without debug +-%bcond_with headers ++%bcond_with debug ++%bcond_without headers + %bcond_with vanilla +-%bcond_without trace ++%bcond_with trace + %bcond_with perf +-%bcond_without debuginfo ++%bcond_with debuginfo ++%bcond_without tools ++ ++# Architectures we build tools/cpupower on ++%define cpupowerarchs x86_64 ppc64 ppc64le ++ + + # What parts do we want to build? We must build at least one kernel. + # These are the kernels that are built IF the architecture allows it. +@@ -53,7 +63,11 @@ Summary: The Linux Realtime kernel + # Verbose output? + # %%global verbose V=1 + ++%if %{do_sign} + %global signmodules 1 ++%else ++%global signmodules 0 ++%endif + + # if patch fuzzy patch applying will be forbidden + %global with_fuzzy_patches 0 +@@ -84,7 +98,7 @@ Summary: The Linux Realtime kernel + %global with_sparse %{?_with_sparse: 1} %{?!_with_sparse: 0} + + %global pkg_release_simple %{rhel_build}.%{rttag}.%{rtbuild} +-%global pkg_release %{rhel_build}.%{rttag}.%{rtbuild}%{?buildid}%{?dist} ++%global pkg_release %{pkg_release_simple}%{?dist}%{?buildid} + + %global KVERREL %{rpmversion}-%{pkg_release}.%{_target_cpu} + +@@ -290,9 +304,14 @@ BuildRequires: xmlto, asciidoc + BuildRequires: openssl + BuildRequires: hmaccalc + BuildRequires: elfutils-libelf-devel ++%if %{do_sign} + %ifarch x86_64 + BuildRequires: pesign >= 0.109-4 + %endif ++%endif ++%if %{with_tools} ++BuildRequires: pciutils-devel gettext ncurses-devel ++%endif + %if %{with_sparse} + BuildRequires: sparse >= 0.4.1 + %endif +@@ -340,12 +359,26 @@ Source25: merge.pl + Source27: sanity_check.py + Source29: extrakeys.pub + ++Source37: centos.cer ++Source38: ima_signing_key.pub ++%if %{?released_kernel} ++%define pesign_name redhatsecureboot301 ++%else ++%define pesign_name redhatsecureboot003 ++%endif + + ### Configuration files + Source50: kernel-%{version}-x86_64-rt.config + Source51: kernel-%{version}-x86_64-rt-trace.config + Source52: kernel-%{version}-x86_64-rt-debug.config + ++# Sources for kernel-rt-tools ++Source2000: cpupower.service ++Source2001: cpupower.config ++ ++Source30000: kernel-3.10.0-x86_64-rt.config.tis_extra ++Source30001: kernel-3.10.0-x86_64-rt-debug.config.tis_extra ++Source30002: kernel-3.10.0-x86_64-rt-trace.config.tis_extra + ### Started using a unified SRPM + + # Sources for kernel modprobe config files +@@ -373,6 +406,7 @@ This kernel has been compiled with the RT patch applied and is intended + for use in deterministic response-time situations + + ++%if %{builddoc} + %package doc + Summary: Various documentation bits found in the kernel source + Group: Documentation +@@ -384,13 +418,14 @@ device drivers shipped with it are documented in these files. + + You will want to install this package if you need a reference to the + options that can be passed to Linux kernel modules at load time. +- ++%endif + + %package headers + Summary: Header files for the Linux kernel for use by glibc + Group: Development/System + Obsoletes: glibc-kernheaders < 3.0-46 + Provides: glibc-kernheaders = 3.0-46 ++Provides: kernel-headers + %description headers + Kernel-headers includes the C header files that specify the interface + between the Linux kernel and userspace libraries and programs. The +@@ -438,6 +473,7 @@ AutoReq: no\ + This package provides KVM modules for package %{name}%{?1:-%{1}}.\ + %{nil} + ++%if %{builddebuginfo} + # + # This macro creates a kernel-rt--kvm-debuginfo package. + # %%kernel_kvm_debuginfo_package +@@ -453,7 +489,9 @@ This package provides debug information for package %{name}%{?1:-%{1}}.\ + This is required to use SystemTap with %{name}%{?1:-%{1}}-%{KVERREL}.\ + %{expand:%%global debuginfo_args %{?debuginfo_args} -p '/.*/%%{KVERREL}%{?1:\.%{1}}/.*|/.*%%{KVERREL}%{?1:\.%{1}}(\.debug)?' -o debuginfo%{?1}-kvm.list}\ + %{nil} ++%endif + ++%if %{builddebuginfo} + # + # This macro creates a kernel--debuginfo package. + # %%kernel_debuginfo_package +@@ -470,6 +508,7 @@ This package provides debug information for package %{name}%{?1:-%{1}}.\ + This is required to use SystemTap with %{name}%{?1:-%{1}}-%{KVERREL}.\ + %{expand:%%global debuginfo_args %{?debuginfo_args} -p '/.*/%%{KVERREL}%{?1:\.%{1}}/.*|/.*%%{KVERREL}%{?1:\.%{1}}(\.debug)?' -o debuginfo%{?1}.list}\ + %{nil} ++%endif + + # + # This macro creates a kernel--devel package. +@@ -483,6 +522,7 @@ Provides: installonlypkg(kernel-rt-devel) = %{version}-%{release}%{?1:.%{1}}\ + Provides: kernel-rt%{?1:-%{1}}-devel-%{_target_cpu} = %{version}-%{release}\ + Provides: kernel-rt-devel-%{_target_cpu} = %{version}-%{release}%{?1:.%{1}}\ + Provides: kernel-rt-devel-uname-r = %{KVERREL}%{?1:.%{1}}\ ++Provides: kernel-devel = %{version}-%{release}%{?1:.%{1}}\ + AutoReqProv: no\ + Requires(pre): /usr/bin/find\ + %description -n kernel-rt%{?variant}%{?1:-%{1}}-devel\ +@@ -495,6 +535,7 @@ against the %{?2:%{2} }kernel package.\ + # %%define variant_summary The Linux kernel compiled for + # %%kernel_variant_package [-n ] + # ++%if %{builddebuginfo} + %define kernel_variant_package(n:) \ + %package %1\ + Summary: %{variant_summary}\ +@@ -505,15 +546,29 @@ Group: System Environment/Kernel\ + %{expand:%%kernel_kvm_package %1}\ + %{expand:%%kernel_kvm_debuginfo_package %1}\ + %{nil} ++%else ++%define kernel_variant_package(n:) \ ++%package %1\ ++Summary: %{variant_summary}\ ++Group: System Environment/Kernel\ ++%kernel_reqprovconf\ ++%{expand:%%kernel_devel_package %1 %{!?-n:%1}%{?-n:%{-n*}}}\ ++%{expand:%%kernel_kvm_package %1}\ ++%{nil} ++%endif + + + # First the auxiliary packages of the main kernel package. + %kernel_devel_package ++%if %{builddebuginfo} + %kernel_debuginfo_package ++%endif + + # create the production kvm module package + %kernel_kvm_package ++%if %{builddebuginfo} + %kernel_kvm_debuginfo_package ++%endif + + # Now, each variant package. + +@@ -563,6 +618,54 @@ It should only be installed when trying to gather additional information + on kernel bugs. + %endif + ++%if %{with_tools} ++ ++%package -n kernel-rt-tools ++Summary: Assortment of tools for the Linux kernel ++Group: Development/System ++License: GPLv2 ++Provides: cpupowerutils-rt = 1:009-0.6.p1 ++Obsoletes: cpupowerutils-rt < 1:009-0.6.p1 ++Provides: cpufreq-utils-rt = 1:009-0.6.p1 ++Provides: cpufrequtils-rt = 1:009-0.6.p1 ++Obsoletes: cpufreq-utils-rt < 1:009-0.6.p1 ++Obsoletes: cpufrequtils-rt < 1:009-0.6.p1 ++Obsoletes: cpuspeed-rt < 1:2.0 ++Requires: kernel-rt-tools-libs = %{version}-%{release} ++%description -n kernel-rt-tools ++This package contains the tools/ directory from the kernel source ++and the supporting documentation. ++ ++%package -n kernel-rt-tools-libs ++Summary: Libraries for the kernels-tools ++Group: Development/System ++License: GPLv2 ++%description -n kernel-rt-tools-libs ++This package contains the libraries built from the tools/ directory ++from the kernel source. ++ ++%package -n kernel-rt-tools-libs-devel ++Summary: Assortment of tools for the Linux kernel ++Group: Development/System ++License: GPLv2 ++Requires: kernel-rt-tools = %{version}-%{release} ++Provides: cpupowerutils-devel = 1:009-0.6.p1.1 ++Obsoletes: cpupowerutils-devel <= 1:009-0.6.p1 ++Requires: kernel-rt-tools-libs = %{version}-%{release} ++Provides: kernel-rt-tools-devel ++%description -n kernel-rt-tools-libs-devel ++This package contains the development files for the tools/ directory from ++the kernel source. ++ ++%endif # with_tools ++ ++%if %{do_sign} ++%package unsigned ++Summary: Unsigned build of the Linux kernel ++%description unsigned ++Contains an unsigned version of the Linux kernel ++%endif # do_sign ++ + %prep + ## ApplyPatch routine + patch_command='patch -p1 -F1 -s' +@@ -607,6 +710,12 @@ cp -rl vanilla-%{kversion} linux-%{kversion}.%{_target_cpu} + + cd linux-%{kversion}.%{_target_cpu} + ++# Copy any TiS-specific config changes ++cp $RPM_SOURCE_DIR/kernel-%{version}-*.config.tis_extra . ++ ++# Copy TiS specific IMA public key ++cp %{SOURCE38} . ++ + ## Apply Patches here + ApplyPatch linux-kernel-test.patch + +@@ -630,6 +739,15 @@ for i in *.config + do + mv $i .config + Arch=`head -1 .config | cut -b 3-` ++ ++ # Handle Titanium Cloud customizations. Use -n to match oldnoconfig below. We want this before ++ # the make line below so that the one below removes any dependencies of ones that we ++ # turn off here. We also want it before "make listnewconfig" so that we can set the ++ # config option for new configs introduced in the Titanium Cloud patches. ++ if [ -f ${i}.tis_extra ]; then ++ scripts/kconfig/merge_config.sh -m -n .config ${i}.tis_extra ++ fi ++ + make %{?cross_opts} ARCH=$Arch listnewconfig | grep -E '^CONFIG_' >.newoptions || true + %if %{listnewconfig_fail} + if [ -s .newoptions ]; then +@@ -764,10 +882,14 @@ BuildKernel() { + cp arch/$Arch/boot/zImage.stub $RPM_BUILD_ROOT/%{image_install_path}/zImage.stub-$KernelVer || : + fi + # EFI SecureBoot signing, x86_64-only ++%if %{do_sign} ++ cp $KernelImage vmlinuz.unsigned ++ $CopyKernel vmlinuz.unsigned $RPM_BUILD_ROOT/%{image_install_path}/vmlinuz.unsigned + %ifarch x86_64 +- %pesign -s -i $KernelImage -o $KernelImage.signed -a %{SOURCE13} -c %{SOURCE14} -n %{pesign_name} ++ %pesign -s -i $KernelImage -o $KernelImage.signed -a %{SOURCE37} -c %{SOURCE37} -n %{pesign_name} + mv $KernelImage.signed $KernelImage + %endif ++%endif + $CopyKernel $KernelImage $RPM_BUILD_ROOT/%{image_install_path}/$InstallName-$KernelVer + chmod 755 $RPM_BUILD_ROOT/%{image_install_path}/$InstallName-$KernelVer + +@@ -910,6 +1032,12 @@ BuildKernel() { + cp signing_key.priv signing_key.priv.sign${Flavour:+.${Flavour}} + cp signing_key.x509 signing_key.x509.sign${Flavour:+.${Flavour}} + ++ # WRS: Copy these keys as part of the devel package ++ # The Module signing keys are to ensure that only Out-of-tree ++ # built against the Titanium Kernel get signed and loaded sans warnings ++ cp signing_key.priv ${RPM_BUILD_ROOT}/lib/modules/${KernelVer}/build/ ++ cp signing_key.x509 ${RPM_BUILD_ROOT}/lib/modules/${KernelVer}/build/ ++ + # remove files that will be auto generated by depmod at rpm -i time + for i in alias alias.bin builtin.bin ccwmap dep dep.bin ieee1394map inputmap isapnpmap ofmap pcimap seriomap symbols symbols.bin usbmap softdep devname + do +@@ -925,6 +1053,15 @@ BuildKernel() { + + install -Dm644 %{SOURCE1000} $RPM_BUILD_ROOT%{_sysconfdir}/modprobe.d/dccp-blacklist.conf + ++ # create the kABI metadata for use in packaging ++ # NOTENOTE: the name symvers is used by the rpm backend ++ # NOTENOTE: to discover and run the /usr/lib/rpm/fileattrs/kabi.attr ++ # NOTENOTE: script which dynamically adds exported kernel symbol ++ # NOTENOTE: checksums to the rpm metadata provides list. ++ # NOTENOTE: if you change the symvers name, update the backend too ++ echo "**** GENERATING kernel ABI metadata ****" ++ gzip -c9 < Module.symvers > $RPM_BUILD_ROOT/boot/symvers-$KernelVer.gz ++ + # prune junk from kernel-devel + find $RPM_BUILD_ROOT/usr/src/kernels -name ".*.cmd" -exec rm -f {} \; + } +@@ -972,6 +1109,31 @@ BuildKernel %make_target %kernel_image vanilla + BuildKernel %make_target %kernel_image + %endif + ++%if %{with_tools} ++%ifarch %{cpupowerarchs} ++# cpupower ++# make sure version-gen.sh is executable. ++chmod +x tools/power/cpupower/utils/version-gen.sh ++make %{?cross_opts} %{?_smp_mflags} -C tools/power/cpupower CPUFREQ_BENCH=false ++%ifarch x86_64 ++ pushd tools/power/cpupower/debug/x86_64 ++ make %{?_smp_mflags} centrino-decode powernow-k8-decode ++ popd ++%endif ++%ifarch x86_64 ++ pushd tools/power/x86/x86_energy_perf_policy/ ++ make ++ popd ++ pushd tools/power/x86/turbostat ++ make ++ popd ++%endif #turbostat/x86_energy_perf_policy ++%endif ++pushd tools ++make tmon ++popd ++%endif ++ + %if %{builddoc} + # Make the HTML and man pages. + make -j1 htmldocs mandocs || %{doc_build_fail} +@@ -1004,6 +1166,7 @@ popd + # if it isn't. + + %ifnarch noarch ++%if %{do_sign} + %define __modsign_install_post \ + if [ "%{with_rt}" -ne "0" ]; then \ + Arch=`head -1 configs/kernel-%{version}-%{_target_cpu}-rt.config | cut -b 3-` \ +@@ -1022,6 +1185,24 @@ popd + %{modsign_cmd} $RPM_BUILD_ROOT/lib/modules/%{KVERREL}.${AAA} || exit 1 \ + done \ + %{nil} ++%else ++%define __modsign_install_post \ ++ if [ "%{with_rt}" -ne "0" ]; then \ ++ Arch=`head -1 configs/kernel-%{version}-%{_target_cpu}-rt.config | cut -b 3-` \ ++ rm -rf .tmp_versions \ ++ mv .tmp_versions.sign .tmp_versions \ ++ mv signing_key.priv.sign signing_key.priv \ ++ mv signing_key.x509.sign signing_key.x509 \ ++ fi\ ++ for AAA in %{?with_trace:trace} %{?with_debug:debug} %{?with_vanilla:vanilla}; do \ ++ Arch=`head -1 configs/kernel-%{version}-%{_target_cpu}-rt-${AAA}.config | cut -b 3-` \ ++ rm -rf .tmp_versions \ ++ mv .tmp_versions.sign.${AAA} .tmp_versions \ ++ mv signing_key.priv.sign.${AAA} signing_key.priv \ ++ mv signing_key.x509.sign.${AAA} signing_key.x509 \ ++ done \ ++%{nil} ++%endif + %endif + + ### +@@ -1111,6 +1292,39 @@ mkdir -p $RPM_BUILD_ROOT%{_datadir}/doc/perf + %endif # buildperf + %endif + ++%if %{with_tools} ++%ifarch %{cpupowerarchs} ++make -C tools/power/cpupower DESTDIR=$RPM_BUILD_ROOT libdir=%{_libdir} mandir=%{_mandir} CPUFREQ_BENCH=false install ++rm -f %{buildroot}%{_libdir}/*.{a,la} ++%find_lang cpupower ++mv cpupower.lang ../ ++%ifarch x86_64 ++ pushd tools/power/cpupower/debug/x86_64 ++ install -m755 centrino-decode %{buildroot}%{_bindir}/centrino-decode ++ install -m755 powernow-k8-decode %{buildroot}%{_bindir}/powernow-k8-decode ++ popd ++%endif ++chmod 0755 %{buildroot}%{_libdir}/libcpupower.so* ++mkdir -p %{buildroot}%{_unitdir} %{buildroot}%{_sysconfdir}/sysconfig ++install -m644 %{SOURCE2000} %{buildroot}%{_unitdir}/cpupower.service ++install -m644 %{SOURCE2001} %{buildroot}%{_sysconfdir}/sysconfig/cpupower ++%ifarch %{ix86} x86_64 ++ mkdir -p %{buildroot}%{_mandir}/man8 ++ pushd tools/power/x86/x86_energy_perf_policy ++ make DESTDIR=%{buildroot} install ++ popd ++ pushd tools/power/x86/turbostat ++ make DESTDIR=%{buildroot} install ++ popd ++%endif #turbostat/x86_energy_perf_policy ++pushd tools/thermal/tmon ++make INSTALL_ROOT=%{buildroot} install ++popd ++%endif ++ ++%endif ++ ++ + %if %{buildheaders} + # Install kernel headers + make ARCH=%{hdrarch} INSTALL_HDR_PATH=$RPM_BUILD_ROOT/usr headers_install +@@ -1153,6 +1367,14 @@ rm -rf $RPM_BUILD_ROOT + ### scripts + ### + ++%if %{with_tools} ++%post -n kernel-rt-tools ++/sbin/ldconfig ++ ++%postun -n kernel-rt-tools ++/sbin/ldconfig ++%endif ++ + # + # This macro defines a %%post script for a kernel*-devel package. + # %%kernel_devel_post [] +@@ -1316,6 +1538,43 @@ fi + %endif + %endif + ++ ++%if %{with_tools} ++%files -n kernel-rt-tools -f cpupower.lang ++%defattr(-,root,root) ++%ifarch %{cpupowerarchs} ++%{_bindir}/cpupower ++%ifarch x86_64 ++%{_bindir}/centrino-decode ++%{_bindir}/powernow-k8-decode ++%endif ++%{_unitdir}/cpupower.service ++%{_mandir}/man[1-8]/cpupower* ++%config(noreplace) %{_sysconfdir}/sysconfig/cpupower ++%ifarch %{ix86} x86_64 ++%{_bindir}/x86_energy_perf_policy ++%{_mandir}/man8/x86_energy_perf_policy* ++%{_bindir}/turbostat ++%{_mandir}/man8/turbostat* ++%endif ++%endif ++%{_bindir}/tmon ++ ++%ifarch %{cpupowerarchs} ++%files -n kernel-rt-tools-libs ++%defattr(-,root,root) ++%{_libdir}/libcpupower.so.0 ++%{_libdir}/libcpupower.so.0.0.0 ++ ++%files -n kernel-rt-tools-libs-devel ++%defattr(-,root,root) ++%{_libdir}/libcpupower.so ++%{_includedir}/cpufreq.h ++%endif ++ ++%endif # with_tools ++ ++ + # This is %{image_install_path} on an arch where that includes ELF files, + # or empty otherwise. + %global elf_image_install_path %{?kernel_image_elf:%{image_install_path}} +@@ -1332,6 +1591,7 @@ fi + /%{image_install_path}/%{?-k:%{-k*}}%{!?-k:vmlinuz}-%{KVERREL}%{?2:.%{2}}\ + /%{image_install_path}/.vmlinuz-%{KVERREL}%{?2:.%{2}}.hmac\ + /boot/System.map-%{KVERREL}%{?2:.%{2}}\ ++/boot/symvers-%{KVERREL}%{?2:.%{2}}.gz\ + /boot/config-%{KVERREL}%{?2:.%{2}}\ + %exclude /lib/modules/%{KVERREL}%{?2:.%{2}}/kernel/arch/x86/kvm\ + %exclude /lib/modules/%{KVERREL}%{?2:.%{2}}/kernel/drivers/gpu/drm/i915/gvt\ +@@ -1410,6 +1670,11 @@ fi + %kernel_variant_files %{buildvanilla} vanilla + %endif + ++%if %{do_sign} ++%files unsigned ++/boot/vmlinuz.unsigned ++%endif # do_sign ++ + %changelog + * Mon Jun 18 2018 Luis Claudio R. Goncalves [3.10.0-862.6.3.rt56.811.el7] + - [rt] Update source tree to match RHEL 7.5 tree [1549768 1462329] +-- +2.7.4 + diff --git a/kernel-rt/centos/meta_patches/Compile-issues.patch b/kernel-rt/centos/meta_patches/Compile-issues.patch new file mode 100644 index 00000000..42bc8e52 --- /dev/null +++ b/kernel-rt/centos/meta_patches/Compile-issues.patch @@ -0,0 +1,34 @@ +From 72f81cd0693c1abfdb5d86bf207cfcd3201646d7 Mon Sep 17 00:00:00 2001 +Message-Id: <72f81cd0693c1abfdb5d86bf207cfcd3201646d7.1528231893.git.Jim.Somerville@windriver.com> +From: Jim Somerville +Date: Tue, 5 Jun 2018 16:51:28 -0400 +Subject: [PATCH 1/1] Compile issues + +Signed-off-by: Jim Somerville +--- + SPECS/kernel-rt.spec | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/SPECS/kernel-rt.spec b/SPECS/kernel-rt.spec +index ab6daf3..3c0e4b4 100644 +--- a/SPECS/kernel-rt.spec ++++ b/SPECS/kernel-rt.spec +@@ -420,6 +420,7 @@ Patch1029: dpt_i2o-fix-build-warning.patch + # DRBD was choking on write same + Patch1030: turn-off-write-same-in-smartqpi-driver.patch + Patch1031: restrict-iSCSI-kthreads-to-CPUs-in-cpu_kthread_mask.patch ++Patch1032: fix-compilation-issues.patch + + BuildRoot: %{_tmppath}/kernel-%{KVERREL}-root + +@@ -784,6 +785,7 @@ ApplyPatch aic94xx-Skip-reading-user-settings-if-flash-is-not-f.patch + ApplyPatch dpt_i2o-fix-build-warning.patch + ApplyPatch turn-off-write-same-in-smartqpi-driver.patch + ApplyPatch restrict-iSCSI-kthreads-to-CPUs-in-cpu_kthread_mask.patch ++ApplyPatch fix-compilation-issues.patch + + # move off upstream version mechanism + if [ -e localversion-rt ]; then +-- +1.8.3.1 + diff --git a/kernel-rt/centos/meta_patches/Kernel-source-patches-for-TiC.patch b/kernel-rt/centos/meta_patches/Kernel-source-patches-for-TiC.patch new file mode 100644 index 00000000..04c04ae3 --- /dev/null +++ b/kernel-rt/centos/meta_patches/Kernel-source-patches-for-TiC.patch @@ -0,0 +1,100 @@ +From 4412b4d092c2f38feed1d0f8ea1a69c799315663 Mon Sep 17 00:00:00 2001 +Message-Id: <4412b4d092c2f38feed1d0f8ea1a69c799315663.1528227675.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Jim Somerville +Date: Mon, 23 Apr 2018 15:19:36 -0400 +Subject: [PATCH 2/3] Kernel source patches for TiC + +Signed-off-by: Jim Somerville +--- + SPECS/kernel-rt.spec | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 66 insertions(+) + +diff --git a/SPECS/kernel-rt.spec b/SPECS/kernel-rt.spec +index 905ae52..15114e6 100644 +--- a/SPECS/kernel-rt.spec ++++ b/SPECS/kernel-rt.spec +@@ -386,6 +386,40 @@ Source1000: modprobe-dccp-blacklist.conf + + # Empty final patch file to facilitate testing of kernel patches + Patch999999: linux-kernel-test.patch ++Patch1000: debrand-single-cpu.patch ++Patch1001: debrand-rh_taint.patch ++Patch1002: debrand-rh-i686-cpu.patch ++Patch1003: affine-compute-kernel-threads.patch ++Patch1004: Affine-irqs-and-workqueues-with-kthread_cpus.patch ++Patch1005: CGTS-3744-route-do-not-cache-fib-route-info-on-local.patch ++Patch1006: cma-add-placement-specifier-for-cma-kernel-parameter.patch ++Patch1007: intel-iommu-allow-ignoring-Ethernet-device-RMRR-with.patch ++Patch1008: Make-kernel-start-eth-devices-at-offset.patch ++Patch1009: memblock-introduce-memblock_alloc_range.patch ++Patch1010: Notification-of-death-of-arbitrary-processes.patch ++Patch1011: PCI-Add-ACS-quirk-for-Intel-Fortville-NICs.patch ++Patch1012: x86-enable-DMA-CMA-with-swiotlb.patch ++Patch1013: Add-missing-ifdef-around-max-latency-variable.patch ++Patch1014: Enable-building-mpt2sas-and-mpt3sas-as-builtin-for-C.patch ++Patch1015: Enable-building-kernel-with-CONFIG_BLK_DEV_NBD.patch ++Patch1016: x86-make-dma_alloc_coherent-return-zeroed-memory-if-.patch ++Patch1017: rcu-Don-t-wake-rcuc-X-kthreads-on-NOCB-CPUs.patch ++Patch1018: Porting-Cacheinfo-from-Kernel-4.10.17.patch ++Patch1019: Fix-cacheinfo-compilation-issues-for-3.10.patch ++Patch1020: cpuidle-menu-stop-seeking-deeper-idle-if-current-sta.patch ++Patch1021: cpuidle-menu-add-per-CPU-PM-QoS-resume-latency-consi.patch ++Patch1022: CPU-PM-expose-pm_qos_resume_latency-for-CPUs.patch ++Patch1023: cpuidle-menu-Avoid-taking-spinlock-for-accessing-QoS.patch ++Patch1024: US101216-IMA-support-in-Titanium-kernel.patch ++Patch1025: US103091-IMA-System-Configuration.patch ++Patch1026: timer-Reduce-timer-migration-overhead-if-disabled.patch ++Patch1027: timer-Minimize-nohz-off-overhead.patch ++# Fix compile warnings that break the build ++Patch1028: aic94xx-Skip-reading-user-settings-if-flash-is-not-f.patch ++Patch1029: dpt_i2o-fix-build-warning.patch ++# DRBD was choking on write same ++Patch1030: turn-off-write-same-in-smartqpi-driver.patch ++Patch1031: restrict-iSCSI-kthreads-to-CPUs-in-cpu_kthread_mask.patch + + BuildRoot: %{_tmppath}/kernel-%{KVERREL}-root + +@@ -718,6 +752,38 @@ cp %{SOURCE38} . + + ## Apply Patches here + ApplyPatch linux-kernel-test.patch ++ApplyPatch debrand-single-cpu.patch ++ApplyPatch debrand-rh_taint.patch ++ApplyPatch debrand-rh-i686-cpu.patch ++ApplyPatch affine-compute-kernel-threads.patch ++ApplyPatch Affine-irqs-and-workqueues-with-kthread_cpus.patch ++ApplyPatch CGTS-3744-route-do-not-cache-fib-route-info-on-local.patch ++ApplyPatch cma-add-placement-specifier-for-cma-kernel-parameter.patch ++ApplyPatch intel-iommu-allow-ignoring-Ethernet-device-RMRR-with.patch ++ApplyPatch Make-kernel-start-eth-devices-at-offset.patch ++ApplyPatch memblock-introduce-memblock_alloc_range.patch ++ApplyPatch PCI-Add-ACS-quirk-for-Intel-Fortville-NICs.patch ++ApplyPatch Notification-of-death-of-arbitrary-processes.patch ++ApplyPatch x86-enable-DMA-CMA-with-swiotlb.patch ++ApplyPatch Add-missing-ifdef-around-max-latency-variable.patch ++ApplyPatch Enable-building-mpt2sas-and-mpt3sas-as-builtin-for-C.patch ++ApplyPatch Enable-building-kernel-with-CONFIG_BLK_DEV_NBD.patch ++ApplyPatch x86-make-dma_alloc_coherent-return-zeroed-memory-if-.patch ++ApplyPatch rcu-Don-t-wake-rcuc-X-kthreads-on-NOCB-CPUs.patch ++ApplyPatch Porting-Cacheinfo-from-Kernel-4.10.17.patch ++ApplyPatch Fix-cacheinfo-compilation-issues-for-3.10.patch ++ApplyPatch cpuidle-menu-stop-seeking-deeper-idle-if-current-sta.patch ++ApplyPatch cpuidle-menu-add-per-CPU-PM-QoS-resume-latency-consi.patch ++ApplyPatch CPU-PM-expose-pm_qos_resume_latency-for-CPUs.patch ++ApplyPatch cpuidle-menu-Avoid-taking-spinlock-for-accessing-QoS.patch ++ApplyPatch US101216-IMA-support-in-Titanium-kernel.patch ++ApplyPatch US103091-IMA-System-Configuration.patch ++ApplyPatch timer-Reduce-timer-migration-overhead-if-disabled.patch ++ApplyPatch timer-Minimize-nohz-off-overhead.patch ++ApplyPatch aic94xx-Skip-reading-user-settings-if-flash-is-not-f.patch ++ApplyPatch dpt_i2o-fix-build-warning.patch ++ApplyPatch turn-off-write-same-in-smartqpi-driver.patch ++ApplyPatch restrict-iSCSI-kthreads-to-CPUs-in-cpu_kthread_mask.patch + + # move off upstream version mechanism + if [ -e localversion-rt ]; then +-- +1.8.3.1 + diff --git a/kernel-rt/centos/meta_patches/Lower-the-compiler-version-requirement.patch b/kernel-rt/centos/meta_patches/Lower-the-compiler-version-requirement.patch new file mode 100644 index 00000000..404bf616 --- /dev/null +++ b/kernel-rt/centos/meta_patches/Lower-the-compiler-version-requirement.patch @@ -0,0 +1,29 @@ +From 2266f2e97fc39650ad6afb55cae3b9e11a4d1021 Mon Sep 17 00:00:00 2001 +Message-Id: <2266f2e97fc39650ad6afb55cae3b9e11a4d1021.1528227675.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Jim Somerville +Date: Tue, 29 May 2018 13:06:24 -0400 +Subject: [PATCH 3/3] Lower the compiler version requirement + +Signed-off-by: Jim Somerville +--- + SPECS/kernel-rt.spec | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/SPECS/kernel-rt.spec b/SPECS/kernel-rt.spec +index 15114e6..ab6daf3 100644 +--- a/SPECS/kernel-rt.spec ++++ b/SPECS/kernel-rt.spec +@@ -296,7 +296,7 @@ Provides: kernel-rt-%{_target_cpu} = %{rpmversion}-%{pkg_release} + # + BuildRequires: module-init-tools, patch >= 2.5.4, bash >= 2.03, sh-utils, tar + BuildRequires: xz, findutils, gzip, m4, perl, make >= 3.78, diffutils, gawk +-BuildRequires: gcc >= 4.8.5-28, binutils >= 2.25, redhat-rpm-config >= 9.1.0-55 ++BuildRequires: gcc >= 4.8.5-16, binutils >= 2.25, redhat-rpm-config >= 9.1.0-55 + BuildRequires: hostname, net-tools, patchutils, bc + %if %{builddoc} + BuildRequires: xmlto, asciidoc +-- +1.8.3.1 + diff --git a/kernel-rt/centos/meta_patches/Lower-the-linux-firmware-version-requirement.patch b/kernel-rt/centos/meta_patches/Lower-the-linux-firmware-version-requirement.patch new file mode 100644 index 00000000..400afe5c --- /dev/null +++ b/kernel-rt/centos/meta_patches/Lower-the-linux-firmware-version-requirement.patch @@ -0,0 +1,27 @@ +From ab1bce8c119b1f99f5b22eba8f419854262744f6 Mon Sep 17 00:00:00 2001 +Message-Id: +From: Jim Somerville +Date: Fri, 1 Jun 2018 16:17:43 -0400 +Subject: [PATCH 1/1] Lower the linux firmware version requirement + +Signed-off-by: Jim Somerville +--- + SPECS/kernel-rt.spec | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/SPECS/kernel-rt.spec b/SPECS/kernel-rt.spec +index 3c0e4b4..e614b70 100644 +--- a/SPECS/kernel-rt.spec ++++ b/SPECS/kernel-rt.spec +@@ -259,7 +259,7 @@ Provides: kernel-modeset = 1\ + Provides: kernel-uname-r = %{KVERREL}%{?1:.%{1}}\ + Requires(pre): %{kernel_prereq}\ + Requires(pre): %{initrd_prereq}\ +-Requires(pre): linux-firmware >= 20180113-61\ ++Requires(pre): linux-firmware >= 20170606-56\ + Requires(post): %{_sbindir}/new-kernel-pkg\ + Requires(post): system-release\ + Requires(preun): %{_sbindir}/new-kernel-pkg\ +-- +1.8.3.1 + diff --git a/kernel-rt/centos/meta_patches/PATCH_ORDER b/kernel-rt/centos/meta_patches/PATCH_ORDER new file mode 100644 index 00000000..7a3ea4aa --- /dev/null +++ b/kernel-rt/centos/meta_patches/PATCH_ORDER @@ -0,0 +1,5 @@ +Build-logic-and-sources-for-TiC.patch +Kernel-source-patches-for-TiC.patch +Lower-the-compiler-version-requirement.patch +Compile-issues.patch +Lower-the-linux-firmware-version-requirement.patch diff --git a/kernel-rt/centos/patches/Add-missing-ifdef-around-max-latency-variable.patch b/kernel-rt/centos/patches/Add-missing-ifdef-around-max-latency-variable.patch new file mode 100644 index 00000000..fd78fe9c --- /dev/null +++ b/kernel-rt/centos/patches/Add-missing-ifdef-around-max-latency-variable.patch @@ -0,0 +1,52 @@ +From d249a02d441998c43aeb1755c85cffb062628500 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Jim Somerville +Date: Thu, 22 Dec 2016 17:54:11 -0500 +Subject: [PATCH 14/32] Add missing ifdef around max latency variable + +Signed-off-by: Jim Somerville +--- + kernel/trace/trace_hwlat.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/kernel/trace/trace_hwlat.c b/kernel/trace/trace_hwlat.c +index 4b30d8b..1bb578c 100644 +--- a/kernel/trace/trace_hwlat.c ++++ b/kernel/trace/trace_hwlat.c +@@ -167,7 +167,9 @@ void trace_hwlat_callback(bool enter) + */ + static int get_sample(void) + { ++#ifdef CONFIG_TRACER_MAX_TRACE + struct trace_array *tr = hwlat_trace; ++#endif + time_type start, t1, t2, last_t2; + s64 diff, total, last_total = 0; + u64 sample = 0; +@@ -254,9 +256,11 @@ static int get_sample(void) + s.nmi_count = nmi_count; + trace_hwlat_sample(&s); + ++#ifdef CONFIG_TRACER_MAX_TRACE + /* Keep a running maximum ever recorded hardware latency */ + if (sample > tr->max_latency) + tr->max_latency = sample; ++#endif + } + + out: +@@ -582,7 +586,9 @@ static int hwlat_tracer_init(struct trace_array *tr) + + disable_migrate = false; + hwlat_data.count = 0; ++#ifdef CONFIG_TRACER_MAX_TRACE + tr->max_latency = 0; ++#endif + save_tracing_thresh = tracing_thresh; + + /* tracing_thresh is in nsecs, we speak in usecs */ +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/Affine-irqs-and-workqueues-with-kthread_cpus.patch b/kernel-rt/centos/patches/Affine-irqs-and-workqueues-with-kthread_cpus.patch new file mode 100644 index 00000000..c32e5e08 --- /dev/null +++ b/kernel-rt/centos/patches/Affine-irqs-and-workqueues-with-kthread_cpus.patch @@ -0,0 +1,73 @@ +From 1584ae45f750efec21265fb0b8ac6a02975dfb76 Mon Sep 17 00:00:00 2001 +Message-Id: <1584ae45f750efec21265fb0b8ac6a02975dfb76.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Chris Friesen +Date: Tue, 24 Nov 2015 16:27:29 -0500 +Subject: [PATCH 05/32] Affine irqs and workqueues with kthread_cpus + +If the kthread_cpus boot arg is set it means we want to affine +kernel threads to the specified CPU mask as much as possible +in order to avoid doing work on other CPUs. + +In this commit we extend the meaning of that boot arg to also +apply to the CPU affinity of unbound and ordered workqueues. + +We also use the kthread_cpus value to determine the default irq +affinity. Specifically, as long as the previously-calculated +irq affinity intersects with the kthread_cpus affinity then we'll +use the intersection of the two as the default irq affinity. + +Signed-off-by: Chris Friesen +[VT: replacing spaces with tabs. Performed tests] +Signed-off-by: Vu Tran + +Signed-off-by: Jim Somerville +--- + kernel/irq/manage.c | 7 +++++++ + kernel/workqueue.c | 4 ++++ + 2 files changed, 11 insertions(+) + +diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c +index bd59426..bad147c 100644 +--- a/kernel/irq/manage.c ++++ b/kernel/irq/manage.c +@@ -404,6 +404,13 @@ setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask) + if (cpumask_intersects(mask, nodemask)) + cpumask_and(mask, mask, nodemask); + } ++ ++ /* This will narrow down the affinity further if we've specified ++ * a reduced cpu_kthread_mask in the boot args. ++ */ ++ if (cpumask_intersects(mask, cpu_kthread_mask)) ++ cpumask_and(mask, mask, cpu_kthread_mask); ++ + irq_do_set_affinity(&desc->irq_data, mask, false); + return 0; + } +diff --git a/kernel/workqueue.c b/kernel/workqueue.c +index 986e283..7160e71 100644 +--- a/kernel/workqueue.c ++++ b/kernel/workqueue.c +@@ -5466,6 +5466,8 @@ static int __init init_workqueues(void) + + BUG_ON(!(attrs = alloc_workqueue_attrs(GFP_KERNEL))); + attrs->nice = std_nice[i]; ++ /* If we've specified a kthread mask apply it here too. */ ++ cpumask_copy(attrs->cpumask, cpu_kthread_mask); + unbound_std_wq_attrs[i] = attrs; + + /* +@@ -5476,6 +5478,8 @@ static int __init init_workqueues(void) + BUG_ON(!(attrs = alloc_workqueue_attrs(GFP_KERNEL))); + attrs->nice = std_nice[i]; + attrs->no_numa = true; ++ /* If we've specified a kthread mask apply it here too. */ ++ cpumask_copy(attrs->cpumask, cpu_kthread_mask); + ordered_wq_attrs[i] = attrs; + } + +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/CGTS-3744-route-do-not-cache-fib-route-info-on-local.patch b/kernel-rt/centos/patches/CGTS-3744-route-do-not-cache-fib-route-info-on-local.patch new file mode 100644 index 00000000..68f8f8ea --- /dev/null +++ b/kernel-rt/centos/patches/CGTS-3744-route-do-not-cache-fib-route-info-on-local.patch @@ -0,0 +1,58 @@ +From a4302d3590621f64e639f8e075f423d5120c007e Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Allain Legacy +Date: Fri, 29 Jan 2016 12:13:40 -0500 +Subject: [PATCH 06/32] CGTS-3744: route: do not cache fib route info on local + routes with oif + +For local routes that require a particular output interface we do not want to +cache the result. Caching the result causes incorrect behaviour when there are +multiple source addresses on the interface. The end result being that if the +intended recipient is waiting on that interface for the packet he won't receive +it because it will be delivered on the loopback interface and the IP_PKTINFO +ipi_ifindex will be set to the loopback interface as well. + +This can be tested by running a program such as "dhcp_release" which attempts +to inject a packet on a particular interface so that it is received by another +program on the same board. The receiving process should see an IP_PKTINFO +ipi_ifndex value of the source interface (e.g., eth1) instead of the loopback +interface (e.g., lo). The packet will still appear on the loopback interface +in tcpdump but the important aspect is that the CMSG info is correct. + +Sample dhcp_release command line: + + dhcp_release eth1 192.168.204.222 02:11:33:22:44:66 + +Signed-off-by: Allain Legacy +Signed-off-by: Jim Somerville +--- + net/ipv4/route.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/net/ipv4/route.c b/net/ipv4/route.c +index f19aca2..5246096 100644 +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -2057,6 +2057,17 @@ static struct rtable *__mkroute_output(const struct fib_result *res, + */ + if (fi && res->prefixlen < 4) + fi = NULL; ++ } else if ((type == RTN_LOCAL) && (orig_oif != 0)) { ++ /* ++ * For local routes that require a particular output interface we do ++ * not want to cache the result. Caching the result causes incorrect ++ * behaviour when there are multiple source addresses on the interface. ++ * The end result being that if the intended recipient is waiting on ++ * that interface for the packet he won't receive it because it will be ++ * delivered on the loopback interface and the IP_PKTINFO ipi_ifindex ++ * will be set to the loopback interface as well. ++ */ ++ fi = NULL; + } + + fnhe = NULL; +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/CPU-PM-expose-pm_qos_resume_latency-for-CPUs.patch b/kernel-rt/centos/patches/CPU-PM-expose-pm_qos_resume_latency-for-CPUs.patch new file mode 100644 index 00000000..95aebf6d --- /dev/null +++ b/kernel-rt/centos/patches/CPU-PM-expose-pm_qos_resume_latency-for-CPUs.patch @@ -0,0 +1,63 @@ +From cb5a4baadb715440ce03e5e751f7125fc0d5427a Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Alex Shi +Date: Thu, 12 Jan 2017 21:27:03 +0800 +Subject: [PATCH 23/32] CPU / PM: expose pm_qos_resume_latency for CPUs + +[ commit 37efa4b41ffb31dcdfc3beb97d47992bb2a083e5 from linux-stable ] + +The cpu-dma PM QoS constraint impacts all the cpus in the system. There is no way +to let the user to choose a PM QoS constraint per cpu. + +The following patch exposes to the userspace a per cpu based sysfs file +in order to let the userspace to change the value of the PM QoS latency +constraint. + +This change is inoperative in its form and the cpuidle governors have to +take into account the per cpu latency constraint in addition to the +global cpu-dma latency constraint in order to operate properly. + +BTW +The pm_qos_resume_latency usage defined in +Documentation/ABI/testing/sysfs-devices-power +The /sys/devices/.../power/pm_qos_resume_latency_us attribute +contains the PM QoS resume latency limit for the given device, +which is the maximum allowed time it can take to resume the +device, after it has been suspended at run time, from a resume +request to the moment the device will be ready to process I/O, +in microseconds. If it is equal to 0, however, this means that +the PM QoS resume latency may be arbitrary. + +Signed-off-by: Alex Shi +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Alex Kozyrev +Signed-off-by: Jim Somerville +--- + drivers/base/cpu.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c +index 803d2a0..1d9bba3 100644 +--- a/drivers/base/cpu.c ++++ b/drivers/base/cpu.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + #include "base.h" + +@@ -318,6 +319,7 @@ int register_cpu(struct cpu *cpu, int num) + per_cpu(cpu_sys_devices, num) = &cpu->dev; + if (!error) + register_cpu_under_node(num, cpu_to_node(num)); ++ dev_pm_qos_expose_latency_limit(&cpu->dev, 0); + + #ifdef CONFIG_KEXEC + if (!error) +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/Enable-building-kernel-with-CONFIG_BLK_DEV_NBD.patch b/kernel-rt/centos/patches/Enable-building-kernel-with-CONFIG_BLK_DEV_NBD.patch new file mode 100644 index 00000000..3d570f6d --- /dev/null +++ b/kernel-rt/centos/patches/Enable-building-kernel-with-CONFIG_BLK_DEV_NBD.patch @@ -0,0 +1,36 @@ +From 615791de3136070eb8fc9100aa8b5ead6905e56a Mon Sep 17 00:00:00 2001 +Message-Id: <615791de3136070eb8fc9100aa8b5ead6905e56a.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Chris Friesen +Date: Wed, 11 Jan 2017 13:38:37 -0500 +Subject: [PATCH 16/32] Enable building kernel with CONFIG_BLK_DEV_NBD + +By default, the CentOS 7.3 kernel will fail to build if +CONFIG_BLK_DEV_NBD is enabled, either as module or builtin. + +The issue seems to be due to the use of REQ_TYPE_SPECIAL in the +NBD code. Switching it to use REQ_TYPE_DRV_PRIV instead makes the +problem go away. + +Signed-off-by: Jim Somerville +--- + drivers/block/nbd.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c +index a40a4f0..e0c6b62 100644 +--- a/drivers/block/nbd.c ++++ b/drivers/block/nbd.c +@@ -616,7 +616,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, + fsync_bdev(bdev); + mutex_lock(&nbd->tx_lock); + blk_rq_init(NULL, &sreq); +- sreq.cmd_type = REQ_TYPE_SPECIAL; ++ sreq.cmd_type = REQ_TYPE_DRV_PRIV; + nbd_cmd(&sreq) = NBD_CMD_DISC; + + /* Check again after getting mutex back. */ +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/Enable-building-mpt2sas-and-mpt3sas-as-builtin-for-C.patch b/kernel-rt/centos/patches/Enable-building-mpt2sas-and-mpt3sas-as-builtin-for-C.patch new file mode 100644 index 00000000..49c237ee --- /dev/null +++ b/kernel-rt/centos/patches/Enable-building-mpt2sas-and-mpt3sas-as-builtin-for-C.patch @@ -0,0 +1,34236 @@ +From a1917d52235bec6b7a7d9758e9b0311e24e810de Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Chris Friesen +Date: Mon, 9 Jan 2017 15:03:00 -0500 +Subject: [PATCH 15/32] Enable building mpt2sas and mpt3sas as builtin for + CentOS 7.3 + +In CentOS 7.3 the upstream mpt2sas/mpt3sas drivers are built from +essentially the same codebase with a flag defined to give the mpt2sas +behaviour. This is purely a problem in 7.3 due to how they have mangled +the drivers. More recently in the Linux kernel these have been merged +into a single driver, while previously in 7.2 they were two totally +separate drivers. + +The 7.3 code works fine when building as modules, but when building as +builtin it gives problems because KBUILD_MODNAME is not defined for files +that are included in multiple drivers. + +The workaround is to move the mpt2sas driver into its own directory, +copying the whole mpt3sas codebase into it. This required a number of +changes after duplicating the codebase: + +1) Add knowledge of the new "mpt2sas" directory to from drivers/scsi/Kconfig +and drivers/scsi/Makefile. + +2) Remove mention of mpt2sas from drivers/scsi/mpt3sas/Kconfig and +drivers/scsi/mpt3sas/Makefile. + +3) Remove mention of mpt3sas from drivers/scsi/mpt2sas/Kconfig and +drivers/scsi/mpt2sas/Makefile. + +4) In the mpt2sas directory, all functions/variables with "mpt3sas" in +the name which did not already have an mpt2sas equivalent were renamed +to instead use "mpt2sas". Thus "_mpt3sas_init" became "_mpt2sas_init". + +5) All other symbols that collided between the two modules were renamed +in the mpt2sas driver by appending "_mpt2sas". This included ones with +"mpt2sas" already in the name, so "mpt2sas_raid_template" became +"mpt2sas_raid_template_mpt2sas". (While the version in the mpt3sas +driver remained "mpt2sas_raid_template".) + +Signed-off-by: Jim Somerville +--- + drivers/scsi/Kconfig | 1 + + drivers/scsi/Makefile | 1 + + drivers/scsi/mpt2sas/Kconfig | 61 + + drivers/scsi/mpt2sas/Makefile | 12 + + drivers/scsi/mpt2sas/mpi/mpi2.h | 1243 ++++ + drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h | 3493 ++++++++++ + drivers/scsi/mpt2sas/mpi/mpi2_init.h | 581 ++ + drivers/scsi/mpt2sas/mpi/mpi2_ioc.h | 1860 +++++ + drivers/scsi/mpt2sas/mpi/mpi2_raid.h | 355 + + drivers/scsi/mpt2sas/mpi/mpi2_sas.h | 303 + + drivers/scsi/mpt2sas/mpi/mpi2_tool.h | 483 ++ + drivers/scsi/mpt2sas/mpi/mpi2_type.h | 57 + + drivers/scsi/mpt2sas/mpt3sas_base.c | 5713 ++++++++++++++++ + drivers/scsi/mpt2sas/mpt3sas_base.h | 1462 ++++ + drivers/scsi/mpt2sas/mpt3sas_config.c | 1716 +++++ + drivers/scsi/mpt2sas/mpt3sas_ctl.c | 3483 ++++++++++ + drivers/scsi/mpt2sas/mpt3sas_ctl.h | 423 ++ + drivers/scsi/mpt2sas/mpt3sas_debug.h | 206 + + drivers/scsi/mpt2sas/mpt3sas_scsih.c | 9356 ++++++++++++++++++++++++++ + drivers/scsi/mpt2sas/mpt3sas_transport.c | 2138 ++++++ + drivers/scsi/mpt2sas/mpt3sas_trigger_diag.c | 434 ++ + drivers/scsi/mpt2sas/mpt3sas_trigger_diag.h | 194 + + drivers/scsi/mpt2sas/mpt3sas_warpdrive.c | 344 + + drivers/scsi/mpt2sas/wrapper_mpt3sas_scsih.c | 4 + + drivers/scsi/mpt3sas/Kconfig | 20 - + drivers/scsi/mpt3sas/Makefile | 9 - + 26 files changed, 33923 insertions(+), 29 deletions(-) + create mode 100644 drivers/scsi/mpt2sas/Kconfig + create mode 100644 drivers/scsi/mpt2sas/Makefile + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2.h + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2_init.h + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2_ioc.h + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2_raid.h + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2_sas.h + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2_tool.h + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2_type.h + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_base.c + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_base.h + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_config.c + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_ctl.c + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_ctl.h + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_debug.h + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_scsih.c + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_transport.c + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_trigger_diag.c + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_trigger_diag.h + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_warpdrive.c + create mode 100644 drivers/scsi/mpt2sas/wrapper_mpt3sas_scsih.c + +diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig +index 4cb0e23..4d79408 100644 +--- a/drivers/scsi/Kconfig ++++ b/drivers/scsi/Kconfig +@@ -599,6 +599,7 @@ config SCSI_ARCMSR + module will be called arcmsr (modprobe arcmsr). + + source "drivers/scsi/megaraid/Kconfig.megaraid" ++source "drivers/scsi/mpt2sas/Kconfig" + source "drivers/scsi/mpt3sas/Kconfig" + source "drivers/scsi/smartpqi/Kconfig" + source "drivers/scsi/ufs/Kconfig" +diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile +index 95aed68..1a08fca 100644 +--- a/drivers/scsi/Makefile ++++ b/drivers/scsi/Makefile +@@ -109,6 +109,7 @@ obj-$(CONFIG_CXLFLASH) += cxlflash/ + obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o + obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/ + obj-$(CONFIG_MEGARAID_SAS) += megaraid/ ++obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas/ + obj-$(CONFIG_SCSI_MPT3SAS) += mpt3sas/ + obj-$(CONFIG_SCSI_UFSHCD) += ufs/ + obj-$(CONFIG_SCSI_ACARD) += atp870u.o +diff --git a/drivers/scsi/mpt2sas/Kconfig b/drivers/scsi/mpt2sas/Kconfig +new file mode 100644 +index 0000000..a53ee73 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/Kconfig +@@ -0,0 +1,61 @@ ++# ++# Kernel configuration file for the MPT2SAS ++# ++# This code is based on drivers/scsi/mpt3sas/Kconfig ++# Copyright (C) 2012-2014 LSI Corporation ++# (mailto:DL-MPTFusionLinux@lsi.com) ++ ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation; either version 2 ++# of the License, or (at your option) any later version. ++ ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++ ++# NO WARRANTY ++# THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++# LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++# solely responsible for determining the appropriateness of using and ++# distributing the Program and assumes all risks associated with its ++# exercise of rights under this Agreement, including but not limited to ++# the risks and costs of program errors, damage to or loss of data, ++# programs or equipment, and unavailability or interruption of operations. ++ ++# DISCLAIMER OF LIABILITY ++# NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++# DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++# USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++# HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++# USA. ++ ++config SCSI_MPT2SAS ++ tristate "LSI MPT Fusion SAS 2.0 Device Driver" ++ depends on PCI && SCSI ++ select SCSI_SAS_ATTRS ++ select RAID_ATTRS ++ ---help--- ++ This driver supports PCI-Express SAS 6Gb/s Host Adapters. ++ ++config SCSI_MPT2SAS_MAX_SGE ++ int "LSI MPT Fusion SAS 2.0 Max number of SG Entries (16 - 256)" ++ depends on PCI && SCSI && SCSI_MPT2SAS ++ default "128" ++ range 16 256 ++ ---help--- ++ This option allows you to specify the maximum number of scatter- ++ gather entries per I/O. The driver default is 128, which matches ++ MAX_PHYS_SEGMENTS in most kernels. However in SuSE kernels this ++ can be 256. However, it may decreased down to 16. Decreasing this ++ parameter will reduce memory requirements on a per controller instance. +diff --git a/drivers/scsi/mpt2sas/Makefile b/drivers/scsi/mpt2sas/Makefile +new file mode 100644 +index 0000000..5706ea4 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/Makefile +@@ -0,0 +1,12 @@ ++# mpt2-3sas makefile ++ ++obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas.o ++ ++obj-m += mpt2sas.o ++mpt2sas-y += mpt3sas_base.o \ ++ mpt3sas_config.o \ ++ wrapper_mpt3sas_scsih.o \ ++ mpt3sas_transport.o \ ++ mpt3sas_ctl.o \ ++ mpt3sas_trigger_diag.o \ ++ mpt3sas_warpdrive.o +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2.h b/drivers/scsi/mpt2sas/mpi/mpi2.h +new file mode 100644 +index 0000000..a9a659f +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2.h +@@ -0,0 +1,1243 @@ ++/* ++ * Copyright 2000-2015 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2.h ++ * Title: MPI Message independent structures and definitions ++ * including System Interface Register Set and ++ * scatter/gather formats. ++ * Creation Date: June 21, 2006 ++ * ++ * mpi2.h Version: 02.00.42 ++ * ++ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 ++ * prefix are for use only on MPI v2.5 products, and must not be used ++ * with MPI v2.0 products. Unless otherwise noted, names beginning with ++ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products. ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 06-04-07 02.00.01 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 06-26-07 02.00.02 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 08-31-07 02.00.03 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Moved ReplyPostHostIndex register to offset 0x6C of the ++ * MPI2_SYSTEM_INTERFACE_REGS and modified the define for ++ * MPI2_REPLY_POST_HOST_INDEX_OFFSET. ++ * Added union of request descriptors. ++ * Added union of reply descriptors. ++ * 10-31-07 02.00.04 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added define for MPI2_VERSION_02_00. ++ * Fixed the size of the FunctionDependent5 field in the ++ * MPI2_DEFAULT_REPLY structure. ++ * 12-18-07 02.00.05 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Removed the MPI-defined Fault Codes and extended the ++ * product specific codes up to 0xEFFF. ++ * Added a sixth key value for the WriteSequence register ++ * and changed the flush value to 0x0. ++ * Added message function codes for Diagnostic Buffer Post ++ * and Diagnsotic Release. ++ * New IOCStatus define: MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED ++ * Moved MPI2_VERSION_UNION from mpi2_ioc.h. ++ * 02-29-08 02.00.06 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 03-03-08 02.00.07 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 05-21-08 02.00.08 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added #defines for marking a reply descriptor as unused. ++ * 06-27-08 02.00.09 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 10-02-08 02.00.10 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Moved LUN field defines from mpi2_init.h. ++ * 01-19-09 02.00.11 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 05-06-09 02.00.12 Bumped MPI2_HEADER_VERSION_UNIT. ++ * In all request and reply descriptors, replaced VF_ID ++ * field with MSIxIndex field. ++ * Removed DevHandle field from ++ * MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR and made those ++ * bytes reserved. ++ * Added RAID Accelerator functionality. ++ * 07-30-09 02.00.13 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 10-28-09 02.00.14 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added MSI-x index mask and shift for Reply Post Host ++ * Index register. ++ * Added function code for Host Based Discovery Action. ++ * 02-10-10 02.00.15 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added define for MPI2_FUNCTION_PWR_MGMT_CONTROL. ++ * Added defines for product-specific range of message ++ * function codes, 0xF0 to 0xFF. ++ * 05-12-10 02.00.16 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added alternative defines for the SGE Direction bit. ++ * 08-11-10 02.00.17 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 11-10-10 02.00.18 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR define. ++ * 02-23-11 02.00.19 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added MPI2_FUNCTION_SEND_HOST_MESSAGE. ++ * 03-09-11 02.00.20 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 05-25-11 02.00.21 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 08-24-11 02.00.22 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 11-18-11 02.00.23 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Incorporating additions for MPI v2.5. ++ * 02-06-12 02.00.24 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 03-29-12 02.00.25 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added Hard Reset delay timings. ++ * 07-10-12 02.00.26 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 07-26-12 02.00.27 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 11-27-12 02.00.28 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 12-20-12 02.00.29 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET. ++ * 04-09-13 02.00.30 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 04-17-13 02.00.31 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 08-19-13 02.00.32 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 12-05-13 02.00.33 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 01-08-14 02.00.34 Bumped MPI2_HEADER_VERSION_UNIT ++ * 06-13-14 02.00.35 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 11-18-14 02.00.36 Updated copyright information. ++ * Bumped MPI2_HEADER_VERSION_UNIT. ++ * 03-16-15 02.00.37 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added Scratchpad registers to ++ * MPI2_SYSTEM_INTERFACE_REGS. ++ * Added MPI2_DIAG_SBR_RELOAD. ++ * 03-19-15 02.00.38 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 05-25-15 02.00.39 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 08-25-15 02.00.40 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 12-15-15 02.00.41 Bumped MPI_HEADER_VERSION_UNIT ++ * 01-01-16 02.00.42 Bumped MPI_HEADER_VERSION_UNIT ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_H ++#define MPI2_H ++ ++/***************************************************************************** ++* ++* MPI Version Definitions ++* ++*****************************************************************************/ ++ ++#define MPI2_VERSION_MAJOR_MASK (0xFF00) ++#define MPI2_VERSION_MAJOR_SHIFT (8) ++#define MPI2_VERSION_MINOR_MASK (0x00FF) ++#define MPI2_VERSION_MINOR_SHIFT (0) ++ ++/*major version for all MPI v2.x */ ++#define MPI2_VERSION_MAJOR (0x02) ++ ++/*minor version for MPI v2.0 compatible products */ ++#define MPI2_VERSION_MINOR (0x00) ++#define MPI2_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \ ++ MPI2_VERSION_MINOR) ++#define MPI2_VERSION_02_00 (0x0200) ++ ++/*minor version for MPI v2.5 compatible products */ ++#define MPI25_VERSION_MINOR (0x05) ++#define MPI25_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \ ++ MPI25_VERSION_MINOR) ++#define MPI2_VERSION_02_05 (0x0205) ++ ++/*minor version for MPI v2.6 compatible products */ ++#define MPI26_VERSION_MINOR (0x06) ++#define MPI26_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \ ++ MPI26_VERSION_MINOR) ++#define MPI2_VERSION_02_06 (0x0206) ++ ++/*Unit and Dev versioning for this MPI header set */ ++#define MPI2_HEADER_VERSION_UNIT (0x2A) ++#define MPI2_HEADER_VERSION_DEV (0x00) ++#define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) ++#define MPI2_HEADER_VERSION_UNIT_SHIFT (8) ++#define MPI2_HEADER_VERSION_DEV_MASK (0x00FF) ++#define MPI2_HEADER_VERSION_DEV_SHIFT (0) ++#define MPI2_HEADER_VERSION ((MPI2_HEADER_VERSION_UNIT << 8) | \ ++ MPI2_HEADER_VERSION_DEV) ++ ++/***************************************************************************** ++* ++* IOC State Definitions ++* ++*****************************************************************************/ ++ ++#define MPI2_IOC_STATE_RESET (0x00000000) ++#define MPI2_IOC_STATE_READY (0x10000000) ++#define MPI2_IOC_STATE_OPERATIONAL (0x20000000) ++#define MPI2_IOC_STATE_FAULT (0x40000000) ++ ++#define MPI2_IOC_STATE_MASK (0xF0000000) ++#define MPI2_IOC_STATE_SHIFT (28) ++ ++/*Fault state range for prodcut specific codes */ ++#define MPI2_FAULT_PRODUCT_SPECIFIC_MIN (0x0000) ++#define MPI2_FAULT_PRODUCT_SPECIFIC_MAX (0xEFFF) ++ ++/***************************************************************************** ++* ++* System Interface Register Definitions ++* ++*****************************************************************************/ ++ ++typedef volatile struct _MPI2_SYSTEM_INTERFACE_REGS { ++ U32 Doorbell; /*0x00 */ ++ U32 WriteSequence; /*0x04 */ ++ U32 HostDiagnostic; /*0x08 */ ++ U32 Reserved1; /*0x0C */ ++ U32 DiagRWData; /*0x10 */ ++ U32 DiagRWAddressLow; /*0x14 */ ++ U32 DiagRWAddressHigh; /*0x18 */ ++ U32 Reserved2[5]; /*0x1C */ ++ U32 HostInterruptStatus; /*0x30 */ ++ U32 HostInterruptMask; /*0x34 */ ++ U32 DCRData; /*0x38 */ ++ U32 DCRAddress; /*0x3C */ ++ U32 Reserved3[2]; /*0x40 */ ++ U32 ReplyFreeHostIndex; /*0x48 */ ++ U32 Reserved4[8]; /*0x4C */ ++ U32 ReplyPostHostIndex; /*0x6C */ ++ U32 Reserved5; /*0x70 */ ++ U32 HCBSize; /*0x74 */ ++ U32 HCBAddressLow; /*0x78 */ ++ U32 HCBAddressHigh; /*0x7C */ ++ U32 Reserved6[12]; /*0x80 */ ++ U32 Scratchpad[4]; /*0xB0 */ ++ U32 RequestDescriptorPostLow; /*0xC0 */ ++ U32 RequestDescriptorPostHigh; /*0xC4 */ ++ U32 AtomicRequestDescriptorPost;/*0xC8 */ ++ U32 Reserved7[13]; /*0xCC */ ++} MPI2_SYSTEM_INTERFACE_REGS, ++ *PTR_MPI2_SYSTEM_INTERFACE_REGS, ++ Mpi2SystemInterfaceRegs_t, ++ *pMpi2SystemInterfaceRegs_t; ++ ++/* ++ *Defines for working with the Doorbell register. ++ */ ++#define MPI2_DOORBELL_OFFSET (0x00000000) ++ ++/*IOC --> System values */ ++#define MPI2_DOORBELL_USED (0x08000000) ++#define MPI2_DOORBELL_WHO_INIT_MASK (0x07000000) ++#define MPI2_DOORBELL_WHO_INIT_SHIFT (24) ++#define MPI2_DOORBELL_FAULT_CODE_MASK (0x0000FFFF) ++#define MPI2_DOORBELL_DATA_MASK (0x0000FFFF) ++ ++/*System --> IOC values */ ++#define MPI2_DOORBELL_FUNCTION_MASK (0xFF000000) ++#define MPI2_DOORBELL_FUNCTION_SHIFT (24) ++#define MPI2_DOORBELL_ADD_DWORDS_MASK (0x00FF0000) ++#define MPI2_DOORBELL_ADD_DWORDS_SHIFT (16) ++ ++/* ++ *Defines for the WriteSequence register ++ */ ++#define MPI2_WRITE_SEQUENCE_OFFSET (0x00000004) ++#define MPI2_WRSEQ_KEY_VALUE_MASK (0x0000000F) ++#define MPI2_WRSEQ_FLUSH_KEY_VALUE (0x0) ++#define MPI2_WRSEQ_1ST_KEY_VALUE (0xF) ++#define MPI2_WRSEQ_2ND_KEY_VALUE (0x4) ++#define MPI2_WRSEQ_3RD_KEY_VALUE (0xB) ++#define MPI2_WRSEQ_4TH_KEY_VALUE (0x2) ++#define MPI2_WRSEQ_5TH_KEY_VALUE (0x7) ++#define MPI2_WRSEQ_6TH_KEY_VALUE (0xD) ++ ++/* ++ *Defines for the HostDiagnostic register ++ */ ++#define MPI2_HOST_DIAGNOSTIC_OFFSET (0x00000008) ++ ++#define MPI2_DIAG_SBR_RELOAD (0x00002000) ++ ++#define MPI2_DIAG_BOOT_DEVICE_SELECT_MASK (0x00001800) ++#define MPI2_DIAG_BOOT_DEVICE_SELECT_DEFAULT (0x00000000) ++#define MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW (0x00000800) ++ ++#define MPI2_DIAG_CLEAR_FLASH_BAD_SIG (0x00000400) ++#define MPI2_DIAG_FORCE_HCB_ON_RESET (0x00000200) ++#define MPI2_DIAG_HCB_MODE (0x00000100) ++#define MPI2_DIAG_DIAG_WRITE_ENABLE (0x00000080) ++#define MPI2_DIAG_FLASH_BAD_SIG (0x00000040) ++#define MPI2_DIAG_RESET_HISTORY (0x00000020) ++#define MPI2_DIAG_DIAG_RW_ENABLE (0x00000010) ++#define MPI2_DIAG_RESET_ADAPTER (0x00000004) ++#define MPI2_DIAG_HOLD_IOC_RESET (0x00000002) ++ ++/* ++ *Offsets for DiagRWData and address ++ */ ++#define MPI2_DIAG_RW_DATA_OFFSET (0x00000010) ++#define MPI2_DIAG_RW_ADDRESS_LOW_OFFSET (0x00000014) ++#define MPI2_DIAG_RW_ADDRESS_HIGH_OFFSET (0x00000018) ++ ++/* ++ *Defines for the HostInterruptStatus register ++ */ ++#define MPI2_HOST_INTERRUPT_STATUS_OFFSET (0x00000030) ++#define MPI2_HIS_SYS2IOC_DB_STATUS (0x80000000) ++#define MPI2_HIS_IOP_DOORBELL_STATUS MPI2_HIS_SYS2IOC_DB_STATUS ++#define MPI2_HIS_RESET_IRQ_STATUS (0x40000000) ++#define MPI2_HIS_REPLY_DESCRIPTOR_INTERRUPT (0x00000008) ++#define MPI2_HIS_IOC2SYS_DB_STATUS (0x00000001) ++#define MPI2_HIS_DOORBELL_INTERRUPT MPI2_HIS_IOC2SYS_DB_STATUS ++ ++/* ++ *Defines for the HostInterruptMask register ++ */ ++#define MPI2_HOST_INTERRUPT_MASK_OFFSET (0x00000034) ++#define MPI2_HIM_RESET_IRQ_MASK (0x40000000) ++#define MPI2_HIM_REPLY_INT_MASK (0x00000008) ++#define MPI2_HIM_RIM MPI2_HIM_REPLY_INT_MASK ++#define MPI2_HIM_IOC2SYS_DB_MASK (0x00000001) ++#define MPI2_HIM_DIM MPI2_HIM_IOC2SYS_DB_MASK ++ ++/* ++ *Offsets for DCRData and address ++ */ ++#define MPI2_DCR_DATA_OFFSET (0x00000038) ++#define MPI2_DCR_ADDRESS_OFFSET (0x0000003C) ++ ++/* ++ *Offset for the Reply Free Queue ++ */ ++#define MPI2_REPLY_FREE_HOST_INDEX_OFFSET (0x00000048) ++ ++/* ++ *Defines for the Reply Descriptor Post Queue ++ */ ++#define MPI2_REPLY_POST_HOST_INDEX_OFFSET (0x0000006C) ++#define MPI2_REPLY_POST_HOST_INDEX_MASK (0x00FFFFFF) ++#define MPI2_RPHI_MSIX_INDEX_MASK (0xFF000000) ++#define MPI2_RPHI_MSIX_INDEX_SHIFT (24) ++#define MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET (0x0000030C) /*MPI v2.5 only*/ ++ ++ ++/* ++ *Defines for the HCBSize and address ++ */ ++#define MPI2_HCB_SIZE_OFFSET (0x00000074) ++#define MPI2_HCB_SIZE_SIZE_MASK (0xFFFFF000) ++#define MPI2_HCB_SIZE_HCB_ENABLE (0x00000001) ++ ++#define MPI2_HCB_ADDRESS_LOW_OFFSET (0x00000078) ++#define MPI2_HCB_ADDRESS_HIGH_OFFSET (0x0000007C) ++ ++/* ++ *Offsets for the Scratchpad registers ++ */ ++#define MPI26_SCRATCHPAD0_OFFSET (0x000000B0) ++#define MPI26_SCRATCHPAD1_OFFSET (0x000000B4) ++#define MPI26_SCRATCHPAD2_OFFSET (0x000000B8) ++#define MPI26_SCRATCHPAD3_OFFSET (0x000000BC) ++ ++/* ++ *Offsets for the Request Descriptor Post Queue ++ */ ++#define MPI2_REQUEST_DESCRIPTOR_POST_LOW_OFFSET (0x000000C0) ++#define MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET (0x000000C4) ++#define MPI26_ATOMIC_REQUEST_DESCRIPTOR_POST_OFFSET (0x000000C8) ++ ++/*Hard Reset delay timings */ ++#define MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC (50000) ++#define MPI2_HARD_RESET_PCIE_RESET_READ_WINDOW_MICRO_SEC (255000) ++#define MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC (256000) ++ ++/***************************************************************************** ++* ++* Message Descriptors ++* ++*****************************************************************************/ ++ ++/*Request Descriptors */ ++ ++/*Default Request Descriptor */ ++typedef struct _MPI2_DEFAULT_REQUEST_DESCRIPTOR { ++ U8 RequestFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U16 LMID; /*0x04 */ ++ U16 DescriptorTypeDependent; /*0x06 */ ++} MPI2_DEFAULT_REQUEST_DESCRIPTOR, ++ *PTR_MPI2_DEFAULT_REQUEST_DESCRIPTOR, ++ Mpi2DefaultRequestDescriptor_t, ++ *pMpi2DefaultRequestDescriptor_t; ++ ++/*defines for the RequestFlags field */ ++#define MPI2_REQ_DESCRIPT_FLAGS_TYPE_MASK (0x1E) ++#define MPI2_REQ_DESCRIPT_FLAGS_TYPE_RSHIFT (1) ++#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO (0x00) ++#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET (0x02) ++#define MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY (0x06) ++#define MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE (0x08) ++#define MPI2_REQ_DESCRIPT_FLAGS_RAID_ACCELERATOR (0x0A) ++#define MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO (0x0C) ++ ++#define MPI2_REQ_DESCRIPT_FLAGS_IOC_FIFO_MARKER (0x01) ++ ++/*High Priority Request Descriptor */ ++typedef struct _MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR { ++ U8 RequestFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U16 LMID; /*0x04 */ ++ U16 Reserved1; /*0x06 */ ++} MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR, ++ *PTR_MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR, ++ Mpi2HighPriorityRequestDescriptor_t, ++ *pMpi2HighPriorityRequestDescriptor_t; ++ ++/*SCSI IO Request Descriptor */ ++typedef struct _MPI2_SCSI_IO_REQUEST_DESCRIPTOR { ++ U8 RequestFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U16 LMID; /*0x04 */ ++ U16 DevHandle; /*0x06 */ ++} MPI2_SCSI_IO_REQUEST_DESCRIPTOR, ++ *PTR_MPI2_SCSI_IO_REQUEST_DESCRIPTOR, ++ Mpi2SCSIIORequestDescriptor_t, ++ *pMpi2SCSIIORequestDescriptor_t; ++ ++/*SCSI Target Request Descriptor */ ++typedef struct _MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR { ++ U8 RequestFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U16 LMID; /*0x04 */ ++ U16 IoIndex; /*0x06 */ ++} MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR, ++ *PTR_MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR, ++ Mpi2SCSITargetRequestDescriptor_t, ++ *pMpi2SCSITargetRequestDescriptor_t; ++ ++/*RAID Accelerator Request Descriptor */ ++typedef struct _MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR { ++ U8 RequestFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U16 LMID; /*0x04 */ ++ U16 Reserved; /*0x06 */ ++} MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR, ++ *PTR_MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR, ++ Mpi2RAIDAcceleratorRequestDescriptor_t, ++ *pMpi2RAIDAcceleratorRequestDescriptor_t; ++ ++/*Fast Path SCSI IO Request Descriptor */ ++typedef MPI2_SCSI_IO_REQUEST_DESCRIPTOR ++ MPI25_FP_SCSI_IO_REQUEST_DESCRIPTOR, ++ *PTR_MPI25_FP_SCSI_IO_REQUEST_DESCRIPTOR, ++ Mpi25FastPathSCSIIORequestDescriptor_t, ++ *pMpi25FastPathSCSIIORequestDescriptor_t; ++ ++/*union of Request Descriptors */ ++typedef union _MPI2_REQUEST_DESCRIPTOR_UNION { ++ MPI2_DEFAULT_REQUEST_DESCRIPTOR Default; ++ MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR HighPriority; ++ MPI2_SCSI_IO_REQUEST_DESCRIPTOR SCSIIO; ++ MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR SCSITarget; ++ MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR RAIDAccelerator; ++ MPI25_FP_SCSI_IO_REQUEST_DESCRIPTOR FastPathSCSIIO; ++ U64 Words; ++} MPI2_REQUEST_DESCRIPTOR_UNION, ++ *PTR_MPI2_REQUEST_DESCRIPTOR_UNION, ++ Mpi2RequestDescriptorUnion_t, ++ *pMpi2RequestDescriptorUnion_t; ++ ++/*Atomic Request Descriptors */ ++ ++/* ++ * All Atomic Request Descriptors have the same format, so the following ++ * structure is used for all Atomic Request Descriptors: ++ * Atomic Default Request Descriptor ++ * Atomic High Priority Request Descriptor ++ * Atomic SCSI IO Request Descriptor ++ * Atomic SCSI Target Request Descriptor ++ * Atomic RAID Accelerator Request Descriptor ++ * Atomic Fast Path SCSI IO Request Descriptor ++ */ ++ ++/*Atomic Request Descriptor */ ++typedef struct _MPI26_ATOMIC_REQUEST_DESCRIPTOR { ++ U8 RequestFlags; /* 0x00 */ ++ U8 MSIxIndex; /* 0x01 */ ++ U16 SMID; /* 0x02 */ ++} MPI26_ATOMIC_REQUEST_DESCRIPTOR, ++ *PTR_MPI26_ATOMIC_REQUEST_DESCRIPTOR, ++ Mpi26AtomicRequestDescriptor_t, ++ *pMpi26AtomicRequestDescriptor_t; ++ ++/*for the RequestFlags field, use the same ++ *defines as MPI2_DEFAULT_REQUEST_DESCRIPTOR ++ */ ++ ++/*Reply Descriptors */ ++ ++/*Default Reply Descriptor */ ++typedef struct _MPI2_DEFAULT_REPLY_DESCRIPTOR { ++ U8 ReplyFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 DescriptorTypeDependent1; /*0x02 */ ++ U32 DescriptorTypeDependent2; /*0x04 */ ++} MPI2_DEFAULT_REPLY_DESCRIPTOR, ++ *PTR_MPI2_DEFAULT_REPLY_DESCRIPTOR, ++ Mpi2DefaultReplyDescriptor_t, ++ *pMpi2DefaultReplyDescriptor_t; ++ ++/*defines for the ReplyFlags field */ ++#define MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK (0x0F) ++#define MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS (0x00) ++#define MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY (0x01) ++#define MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS (0x02) ++#define MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER (0x03) ++#define MPI2_RPY_DESCRIPT_FLAGS_RAID_ACCELERATOR_SUCCESS (0x05) ++#define MPI25_RPY_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO_SUCCESS (0x06) ++#define MPI2_RPY_DESCRIPT_FLAGS_UNUSED (0x0F) ++ ++/*values for marking a reply descriptor as unused */ ++#define MPI2_RPY_DESCRIPT_UNUSED_WORD0_MARK (0xFFFFFFFF) ++#define MPI2_RPY_DESCRIPT_UNUSED_WORD1_MARK (0xFFFFFFFF) ++ ++/*Address Reply Descriptor */ ++typedef struct _MPI2_ADDRESS_REPLY_DESCRIPTOR { ++ U8 ReplyFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U32 ReplyFrameAddress; /*0x04 */ ++} MPI2_ADDRESS_REPLY_DESCRIPTOR, ++ *PTR_MPI2_ADDRESS_REPLY_DESCRIPTOR, ++ Mpi2AddressReplyDescriptor_t, ++ *pMpi2AddressReplyDescriptor_t; ++ ++#define MPI2_ADDRESS_REPLY_SMID_INVALID (0x00) ++ ++/*SCSI IO Success Reply Descriptor */ ++typedef struct _MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR { ++ U8 ReplyFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U16 TaskTag; /*0x04 */ ++ U16 Reserved1; /*0x06 */ ++} MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, ++ *PTR_MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, ++ Mpi2SCSIIOSuccessReplyDescriptor_t, ++ *pMpi2SCSIIOSuccessReplyDescriptor_t; ++ ++/*TargetAssist Success Reply Descriptor */ ++typedef struct _MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR { ++ U8 ReplyFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U8 SequenceNumber; /*0x04 */ ++ U8 Reserved1; /*0x05 */ ++ U16 IoIndex; /*0x06 */ ++} MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR, ++ *PTR_MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR, ++ Mpi2TargetAssistSuccessReplyDescriptor_t, ++ *pMpi2TargetAssistSuccessReplyDescriptor_t; ++ ++/*Target Command Buffer Reply Descriptor */ ++typedef struct _MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR { ++ U8 ReplyFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U8 VP_ID; /*0x02 */ ++ U8 Flags; /*0x03 */ ++ U16 InitiatorDevHandle; /*0x04 */ ++ U16 IoIndex; /*0x06 */ ++} MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR, ++ *PTR_MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR, ++ Mpi2TargetCommandBufferReplyDescriptor_t, ++ *pMpi2TargetCommandBufferReplyDescriptor_t; ++ ++/*defines for Flags field */ ++#define MPI2_RPY_DESCRIPT_TCB_FLAGS_PHYNUM_MASK (0x3F) ++ ++/*RAID Accelerator Success Reply Descriptor */ ++typedef struct _MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR { ++ U8 ReplyFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U32 Reserved; /*0x04 */ ++} MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR, ++ *PTR_MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR, ++ Mpi2RAIDAcceleratorSuccessReplyDescriptor_t, ++ *pMpi2RAIDAcceleratorSuccessReplyDescriptor_t; ++ ++/*Fast Path SCSI IO Success Reply Descriptor */ ++typedef MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR ++ MPI25_FP_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, ++ *PTR_MPI25_FP_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, ++ Mpi25FastPathSCSIIOSuccessReplyDescriptor_t, ++ *pMpi25FastPathSCSIIOSuccessReplyDescriptor_t; ++ ++/*union of Reply Descriptors */ ++typedef union _MPI2_REPLY_DESCRIPTORS_UNION { ++ MPI2_DEFAULT_REPLY_DESCRIPTOR Default; ++ MPI2_ADDRESS_REPLY_DESCRIPTOR AddressReply; ++ MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR SCSIIOSuccess; ++ MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR TargetAssistSuccess; ++ MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer; ++ MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR RAIDAcceleratorSuccess; ++ MPI25_FP_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR FastPathSCSIIOSuccess; ++ U64 Words; ++} MPI2_REPLY_DESCRIPTORS_UNION, ++ *PTR_MPI2_REPLY_DESCRIPTORS_UNION, ++ Mpi2ReplyDescriptorsUnion_t, ++ *pMpi2ReplyDescriptorsUnion_t; ++ ++/***************************************************************************** ++* ++* Message Functions ++* ++*****************************************************************************/ ++ ++#define MPI2_FUNCTION_SCSI_IO_REQUEST (0x00) ++#define MPI2_FUNCTION_SCSI_TASK_MGMT (0x01) ++#define MPI2_FUNCTION_IOC_INIT (0x02) ++#define MPI2_FUNCTION_IOC_FACTS (0x03) ++#define MPI2_FUNCTION_CONFIG (0x04) ++#define MPI2_FUNCTION_PORT_FACTS (0x05) ++#define MPI2_FUNCTION_PORT_ENABLE (0x06) ++#define MPI2_FUNCTION_EVENT_NOTIFICATION (0x07) ++#define MPI2_FUNCTION_EVENT_ACK (0x08) ++#define MPI2_FUNCTION_FW_DOWNLOAD (0x09) ++#define MPI2_FUNCTION_TARGET_ASSIST (0x0B) ++#define MPI2_FUNCTION_TARGET_STATUS_SEND (0x0C) ++#define MPI2_FUNCTION_TARGET_MODE_ABORT (0x0D) ++#define MPI2_FUNCTION_FW_UPLOAD (0x12) ++#define MPI2_FUNCTION_RAID_ACTION (0x15) ++#define MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH (0x16) ++#define MPI2_FUNCTION_TOOLBOX (0x17) ++#define MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR (0x18) ++#define MPI2_FUNCTION_SMP_PASSTHROUGH (0x1A) ++#define MPI2_FUNCTION_SAS_IO_UNIT_CONTROL (0x1B) ++#define MPI2_FUNCTION_IO_UNIT_CONTROL (0x1B) ++#define MPI2_FUNCTION_SATA_PASSTHROUGH (0x1C) ++#define MPI2_FUNCTION_DIAG_BUFFER_POST (0x1D) ++#define MPI2_FUNCTION_DIAG_RELEASE (0x1E) ++#define MPI2_FUNCTION_TARGET_CMD_BUF_BASE_POST (0x24) ++#define MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST (0x25) ++#define MPI2_FUNCTION_RAID_ACCELERATOR (0x2C) ++#define MPI2_FUNCTION_HOST_BASED_DISCOVERY_ACTION (0x2F) ++#define MPI2_FUNCTION_PWR_MGMT_CONTROL (0x30) ++#define MPI2_FUNCTION_SEND_HOST_MESSAGE (0x31) ++#define MPI2_FUNCTION_MIN_PRODUCT_SPECIFIC (0xF0) ++#define MPI2_FUNCTION_MAX_PRODUCT_SPECIFIC (0xFF) ++ ++/*Doorbell functions */ ++#define MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40) ++#define MPI2_FUNCTION_HANDSHAKE (0x42) ++ ++/***************************************************************************** ++* ++* IOC Status Values ++* ++*****************************************************************************/ ++ ++/*mask for IOCStatus status value */ ++#define MPI2_IOCSTATUS_MASK (0x7FFF) ++ ++/**************************************************************************** ++* Common IOCStatus values for all replies ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_SUCCESS (0x0000) ++#define MPI2_IOCSTATUS_INVALID_FUNCTION (0x0001) ++#define MPI2_IOCSTATUS_BUSY (0x0002) ++#define MPI2_IOCSTATUS_INVALID_SGL (0x0003) ++#define MPI2_IOCSTATUS_INTERNAL_ERROR (0x0004) ++#define MPI2_IOCSTATUS_INVALID_VPID (0x0005) ++#define MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006) ++#define MPI2_IOCSTATUS_INVALID_FIELD (0x0007) ++#define MPI2_IOCSTATUS_INVALID_STATE (0x0008) ++#define MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED (0x0009) ++#define MPI2_IOCSTATUS_INSUFFICIENT_POWER (0x000A) ++ ++/**************************************************************************** ++* Config IOCStatus values ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020) ++#define MPI2_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021) ++#define MPI2_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022) ++#define MPI2_IOCSTATUS_CONFIG_INVALID_DATA (0x0023) ++#define MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024) ++#define MPI2_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025) ++ ++/**************************************************************************** ++* SCSI IO Reply ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040) ++#define MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE (0x0042) ++#define MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043) ++#define MPI2_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044) ++#define MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045) ++#define MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046) ++#define MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047) ++#define MPI2_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048) ++#define MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049) ++#define MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004A) ++#define MPI2_IOCSTATUS_SCSI_IOC_TERMINATED (0x004B) ++#define MPI2_IOCSTATUS_SCSI_EXT_TERMINATED (0x004C) ++ ++/**************************************************************************** ++* For use by SCSI Initiator and SCSI Target end-to-end data protection ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_EEDP_GUARD_ERROR (0x004D) ++#define MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR (0x004E) ++#define MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR (0x004F) ++ ++/**************************************************************************** ++* SCSI Target values ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX (0x0062) ++#define MPI2_IOCSTATUS_TARGET_ABORTED (0x0063) ++#define MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064) ++#define MPI2_IOCSTATUS_TARGET_NO_CONNECTION (0x0065) ++#define MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006A) ++#define MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR (0x006D) ++#define MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA (0x006E) ++#define MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT (0x006F) ++#define MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT (0x0070) ++#define MPI2_IOCSTATUS_TARGET_NAK_RECEIVED (0x0071) ++ ++/**************************************************************************** ++* Serial Attached SCSI values ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED (0x0090) ++#define MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN (0x0091) ++ ++/**************************************************************************** ++* Diagnostic Buffer Post / Diagnostic Release values ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00A0) ++ ++/**************************************************************************** ++* RAID Accelerator values ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_RAID_ACCEL_ERROR (0x00B0) ++ ++/**************************************************************************** ++* IOCStatus flag to indicate that log info is available ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE (0x8000) ++ ++/**************************************************************************** ++* IOCLogInfo Types ++****************************************************************************/ ++ ++#define MPI2_IOCLOGINFO_TYPE_MASK (0xF0000000) ++#define MPI2_IOCLOGINFO_TYPE_SHIFT (28) ++#define MPI2_IOCLOGINFO_TYPE_NONE (0x0) ++#define MPI2_IOCLOGINFO_TYPE_SCSI (0x1) ++#define MPI2_IOCLOGINFO_TYPE_FC (0x2) ++#define MPI2_IOCLOGINFO_TYPE_SAS (0x3) ++#define MPI2_IOCLOGINFO_TYPE_ISCSI (0x4) ++#define MPI2_IOCLOGINFO_LOG_DATA_MASK (0x0FFFFFFF) ++ ++/***************************************************************************** ++* ++* Standard Message Structures ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++*Request Message Header for all request messages ++****************************************************************************/ ++ ++typedef struct _MPI2_REQUEST_HEADER { ++ U16 FunctionDependent1; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 FunctionDependent2; /*0x04 */ ++ U8 FunctionDependent3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++} MPI2_REQUEST_HEADER, *PTR_MPI2_REQUEST_HEADER, ++ MPI2RequestHeader_t, *pMPI2RequestHeader_t; ++ ++/**************************************************************************** ++* Default Reply ++****************************************************************************/ ++ ++typedef struct _MPI2_DEFAULT_REPLY { ++ U16 FunctionDependent1; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 FunctionDependent2; /*0x04 */ ++ U8 FunctionDependent3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U16 FunctionDependent5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_DEFAULT_REPLY, *PTR_MPI2_DEFAULT_REPLY, ++ MPI2DefaultReply_t, *pMPI2DefaultReply_t; ++ ++/*common version structure/union used in messages and configuration pages */ ++ ++typedef struct _MPI2_VERSION_STRUCT { ++ U8 Dev; /*0x00 */ ++ U8 Unit; /*0x01 */ ++ U8 Minor; /*0x02 */ ++ U8 Major; /*0x03 */ ++} MPI2_VERSION_STRUCT; ++ ++typedef union _MPI2_VERSION_UNION { ++ MPI2_VERSION_STRUCT Struct; ++ U32 Word; ++} MPI2_VERSION_UNION; ++ ++/*LUN field defines, common to many structures */ ++#define MPI2_LUN_FIRST_LEVEL_ADDRESSING (0x0000FFFF) ++#define MPI2_LUN_SECOND_LEVEL_ADDRESSING (0xFFFF0000) ++#define MPI2_LUN_THIRD_LEVEL_ADDRESSING (0x0000FFFF) ++#define MPI2_LUN_FOURTH_LEVEL_ADDRESSING (0xFFFF0000) ++#define MPI2_LUN_LEVEL_1_WORD (0xFF00) ++#define MPI2_LUN_LEVEL_1_DWORD (0x0000FF00) ++ ++/***************************************************************************** ++* ++* Fusion-MPT MPI Scatter Gather Elements ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* MPI Simple Element structures ++****************************************************************************/ ++ ++typedef struct _MPI2_SGE_SIMPLE32 { ++ U32 FlagsLength; ++ U32 Address; ++} MPI2_SGE_SIMPLE32, *PTR_MPI2_SGE_SIMPLE32, ++ Mpi2SGESimple32_t, *pMpi2SGESimple32_t; ++ ++typedef struct _MPI2_SGE_SIMPLE64 { ++ U32 FlagsLength; ++ U64 Address; ++} MPI2_SGE_SIMPLE64, *PTR_MPI2_SGE_SIMPLE64, ++ Mpi2SGESimple64_t, *pMpi2SGESimple64_t; ++ ++typedef struct _MPI2_SGE_SIMPLE_UNION { ++ U32 FlagsLength; ++ union { ++ U32 Address32; ++ U64 Address64; ++ } u; ++} MPI2_SGE_SIMPLE_UNION, ++ *PTR_MPI2_SGE_SIMPLE_UNION, ++ Mpi2SGESimpleUnion_t, ++ *pMpi2SGESimpleUnion_t; ++ ++/**************************************************************************** ++* MPI Chain Element structures - for MPI v2.0 products only ++****************************************************************************/ ++ ++typedef struct _MPI2_SGE_CHAIN32 { ++ U16 Length; ++ U8 NextChainOffset; ++ U8 Flags; ++ U32 Address; ++} MPI2_SGE_CHAIN32, *PTR_MPI2_SGE_CHAIN32, ++ Mpi2SGEChain32_t, *pMpi2SGEChain32_t; ++ ++typedef struct _MPI2_SGE_CHAIN64 { ++ U16 Length; ++ U8 NextChainOffset; ++ U8 Flags; ++ U64 Address; ++} MPI2_SGE_CHAIN64, *PTR_MPI2_SGE_CHAIN64, ++ Mpi2SGEChain64_t, *pMpi2SGEChain64_t; ++ ++typedef struct _MPI2_SGE_CHAIN_UNION { ++ U16 Length; ++ U8 NextChainOffset; ++ U8 Flags; ++ union { ++ U32 Address32; ++ U64 Address64; ++ } u; ++} MPI2_SGE_CHAIN_UNION, ++ *PTR_MPI2_SGE_CHAIN_UNION, ++ Mpi2SGEChainUnion_t, ++ *pMpi2SGEChainUnion_t; ++ ++/**************************************************************************** ++* MPI Transaction Context Element structures - for MPI v2.0 products only ++****************************************************************************/ ++ ++typedef struct _MPI2_SGE_TRANSACTION32 { ++ U8 Reserved; ++ U8 ContextSize; ++ U8 DetailsLength; ++ U8 Flags; ++ U32 TransactionContext[1]; ++ U32 TransactionDetails[1]; ++} MPI2_SGE_TRANSACTION32, ++ *PTR_MPI2_SGE_TRANSACTION32, ++ Mpi2SGETransaction32_t, ++ *pMpi2SGETransaction32_t; ++ ++typedef struct _MPI2_SGE_TRANSACTION64 { ++ U8 Reserved; ++ U8 ContextSize; ++ U8 DetailsLength; ++ U8 Flags; ++ U32 TransactionContext[2]; ++ U32 TransactionDetails[1]; ++} MPI2_SGE_TRANSACTION64, ++ *PTR_MPI2_SGE_TRANSACTION64, ++ Mpi2SGETransaction64_t, ++ *pMpi2SGETransaction64_t; ++ ++typedef struct _MPI2_SGE_TRANSACTION96 { ++ U8 Reserved; ++ U8 ContextSize; ++ U8 DetailsLength; ++ U8 Flags; ++ U32 TransactionContext[3]; ++ U32 TransactionDetails[1]; ++} MPI2_SGE_TRANSACTION96, *PTR_MPI2_SGE_TRANSACTION96, ++ Mpi2SGETransaction96_t, *pMpi2SGETransaction96_t; ++ ++typedef struct _MPI2_SGE_TRANSACTION128 { ++ U8 Reserved; ++ U8 ContextSize; ++ U8 DetailsLength; ++ U8 Flags; ++ U32 TransactionContext[4]; ++ U32 TransactionDetails[1]; ++} MPI2_SGE_TRANSACTION128, *PTR_MPI2_SGE_TRANSACTION128, ++ Mpi2SGETransaction_t128, *pMpi2SGETransaction_t128; ++ ++typedef struct _MPI2_SGE_TRANSACTION_UNION { ++ U8 Reserved; ++ U8 ContextSize; ++ U8 DetailsLength; ++ U8 Flags; ++ union { ++ U32 TransactionContext32[1]; ++ U32 TransactionContext64[2]; ++ U32 TransactionContext96[3]; ++ U32 TransactionContext128[4]; ++ } u; ++ U32 TransactionDetails[1]; ++} MPI2_SGE_TRANSACTION_UNION, ++ *PTR_MPI2_SGE_TRANSACTION_UNION, ++ Mpi2SGETransactionUnion_t, ++ *pMpi2SGETransactionUnion_t; ++ ++/**************************************************************************** ++* MPI SGE union for IO SGL's - for MPI v2.0 products only ++****************************************************************************/ ++ ++typedef struct _MPI2_MPI_SGE_IO_UNION { ++ union { ++ MPI2_SGE_SIMPLE_UNION Simple; ++ MPI2_SGE_CHAIN_UNION Chain; ++ } u; ++} MPI2_MPI_SGE_IO_UNION, *PTR_MPI2_MPI_SGE_IO_UNION, ++ Mpi2MpiSGEIOUnion_t, *pMpi2MpiSGEIOUnion_t; ++ ++/**************************************************************************** ++* MPI SGE union for SGL's with Simple and Transaction elements - for MPI v2.0 products only ++****************************************************************************/ ++ ++typedef struct _MPI2_SGE_TRANS_SIMPLE_UNION { ++ union { ++ MPI2_SGE_SIMPLE_UNION Simple; ++ MPI2_SGE_TRANSACTION_UNION Transaction; ++ } u; ++} MPI2_SGE_TRANS_SIMPLE_UNION, ++ *PTR_MPI2_SGE_TRANS_SIMPLE_UNION, ++ Mpi2SGETransSimpleUnion_t, ++ *pMpi2SGETransSimpleUnion_t; ++ ++/**************************************************************************** ++* All MPI SGE types union ++****************************************************************************/ ++ ++typedef struct _MPI2_MPI_SGE_UNION { ++ union { ++ MPI2_SGE_SIMPLE_UNION Simple; ++ MPI2_SGE_CHAIN_UNION Chain; ++ MPI2_SGE_TRANSACTION_UNION Transaction; ++ } u; ++} MPI2_MPI_SGE_UNION, *PTR_MPI2_MPI_SGE_UNION, ++ Mpi2MpiSgeUnion_t, *pMpi2MpiSgeUnion_t; ++ ++/**************************************************************************** ++* MPI SGE field definition and masks ++****************************************************************************/ ++ ++/*Flags field bit definitions */ ++ ++#define MPI2_SGE_FLAGS_LAST_ELEMENT (0x80) ++#define MPI2_SGE_FLAGS_END_OF_BUFFER (0x40) ++#define MPI2_SGE_FLAGS_ELEMENT_TYPE_MASK (0x30) ++#define MPI2_SGE_FLAGS_LOCAL_ADDRESS (0x08) ++#define MPI2_SGE_FLAGS_DIRECTION (0x04) ++#define MPI2_SGE_FLAGS_ADDRESS_SIZE (0x02) ++#define MPI2_SGE_FLAGS_END_OF_LIST (0x01) ++ ++#define MPI2_SGE_FLAGS_SHIFT (24) ++ ++#define MPI2_SGE_LENGTH_MASK (0x00FFFFFF) ++#define MPI2_SGE_CHAIN_LENGTH_MASK (0x0000FFFF) ++ ++/*Element Type */ ++ ++#define MPI2_SGE_FLAGS_TRANSACTION_ELEMENT (0x00) ++#define MPI2_SGE_FLAGS_SIMPLE_ELEMENT (0x10) ++#define MPI2_SGE_FLAGS_CHAIN_ELEMENT (0x30) ++#define MPI2_SGE_FLAGS_ELEMENT_MASK (0x30) ++ ++/*Address location */ ++ ++#define MPI2_SGE_FLAGS_SYSTEM_ADDRESS (0x00) ++ ++/*Direction */ ++ ++#define MPI2_SGE_FLAGS_IOC_TO_HOST (0x00) ++#define MPI2_SGE_FLAGS_HOST_TO_IOC (0x04) ++ ++#define MPI2_SGE_FLAGS_DEST (MPI2_SGE_FLAGS_IOC_TO_HOST) ++#define MPI2_SGE_FLAGS_SOURCE (MPI2_SGE_FLAGS_HOST_TO_IOC) ++ ++/*Address Size */ ++ ++#define MPI2_SGE_FLAGS_32_BIT_ADDRESSING (0x00) ++#define MPI2_SGE_FLAGS_64_BIT_ADDRESSING (0x02) ++ ++/*Context Size */ ++ ++#define MPI2_SGE_FLAGS_32_BIT_CONTEXT (0x00) ++#define MPI2_SGE_FLAGS_64_BIT_CONTEXT (0x02) ++#define MPI2_SGE_FLAGS_96_BIT_CONTEXT (0x04) ++#define MPI2_SGE_FLAGS_128_BIT_CONTEXT (0x06) ++ ++#define MPI2_SGE_CHAIN_OFFSET_MASK (0x00FF0000) ++#define MPI2_SGE_CHAIN_OFFSET_SHIFT (16) ++ ++/**************************************************************************** ++* MPI SGE operation Macros ++****************************************************************************/ ++ ++/*SIMPLE FlagsLength manipulations... */ ++#define MPI2_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_SGE_FLAGS_SHIFT) ++#define MPI2_SGE_GET_FLAGS(f) (((f) & ~MPI2_SGE_LENGTH_MASK) >> \ ++ MPI2_SGE_FLAGS_SHIFT) ++#define MPI2_SGE_LENGTH(f) ((f) & MPI2_SGE_LENGTH_MASK) ++#define MPI2_SGE_CHAIN_LENGTH(f) ((f) & MPI2_SGE_CHAIN_LENGTH_MASK) ++ ++#define MPI2_SGE_SET_FLAGS_LENGTH(f, l) (MPI2_SGE_SET_FLAGS(f) | \ ++ MPI2_SGE_LENGTH(l)) ++ ++#define MPI2_pSGE_GET_FLAGS(psg) MPI2_SGE_GET_FLAGS((psg)->FlagsLength) ++#define MPI2_pSGE_GET_LENGTH(psg) MPI2_SGE_LENGTH((psg)->FlagsLength) ++#define MPI2_pSGE_SET_FLAGS_LENGTH(psg, f, l) ((psg)->FlagsLength = \ ++ MPI2_SGE_SET_FLAGS_LENGTH(f, l)) ++ ++/*CAUTION - The following are READ-MODIFY-WRITE! */ ++#define MPI2_pSGE_SET_FLAGS(psg, f) ((psg)->FlagsLength |= \ ++ MPI2_SGE_SET_FLAGS(f)) ++#define MPI2_pSGE_SET_LENGTH(psg, l) ((psg)->FlagsLength |= \ ++ MPI2_SGE_LENGTH(l)) ++ ++#define MPI2_GET_CHAIN_OFFSET(x) ((x & MPI2_SGE_CHAIN_OFFSET_MASK) >> \ ++ MPI2_SGE_CHAIN_OFFSET_SHIFT) ++ ++/***************************************************************************** ++* ++* Fusion-MPT IEEE Scatter Gather Elements ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* IEEE Simple Element structures ++****************************************************************************/ ++ ++/*MPI2_IEEE_SGE_SIMPLE32 is for MPI v2.0 products only */ ++typedef struct _MPI2_IEEE_SGE_SIMPLE32 { ++ U32 Address; ++ U32 FlagsLength; ++} MPI2_IEEE_SGE_SIMPLE32, *PTR_MPI2_IEEE_SGE_SIMPLE32, ++ Mpi2IeeeSgeSimple32_t, *pMpi2IeeeSgeSimple32_t; ++ ++typedef struct _MPI2_IEEE_SGE_SIMPLE64 { ++ U64 Address; ++ U32 Length; ++ U16 Reserved1; ++ U8 Reserved2; ++ U8 Flags; ++} MPI2_IEEE_SGE_SIMPLE64, *PTR_MPI2_IEEE_SGE_SIMPLE64, ++ Mpi2IeeeSgeSimple64_t, *pMpi2IeeeSgeSimple64_t; ++ ++typedef union _MPI2_IEEE_SGE_SIMPLE_UNION { ++ MPI2_IEEE_SGE_SIMPLE32 Simple32; ++ MPI2_IEEE_SGE_SIMPLE64 Simple64; ++} MPI2_IEEE_SGE_SIMPLE_UNION, ++ *PTR_MPI2_IEEE_SGE_SIMPLE_UNION, ++ Mpi2IeeeSgeSimpleUnion_t, ++ *pMpi2IeeeSgeSimpleUnion_t; ++ ++/**************************************************************************** ++* IEEE Chain Element structures ++****************************************************************************/ ++ ++/*MPI2_IEEE_SGE_CHAIN32 is for MPI v2.0 products only */ ++typedef MPI2_IEEE_SGE_SIMPLE32 MPI2_IEEE_SGE_CHAIN32; ++ ++/*MPI2_IEEE_SGE_CHAIN64 is for MPI v2.0 products only */ ++typedef MPI2_IEEE_SGE_SIMPLE64 MPI2_IEEE_SGE_CHAIN64; ++ ++typedef union _MPI2_IEEE_SGE_CHAIN_UNION { ++ MPI2_IEEE_SGE_CHAIN32 Chain32; ++ MPI2_IEEE_SGE_CHAIN64 Chain64; ++} MPI2_IEEE_SGE_CHAIN_UNION, ++ *PTR_MPI2_IEEE_SGE_CHAIN_UNION, ++ Mpi2IeeeSgeChainUnion_t, ++ *pMpi2IeeeSgeChainUnion_t; ++ ++/*MPI25_IEEE_SGE_CHAIN64 is for MPI v2.5 and later */ ++typedef struct _MPI25_IEEE_SGE_CHAIN64 { ++ U64 Address; ++ U32 Length; ++ U16 Reserved1; ++ U8 NextChainOffset; ++ U8 Flags; ++} MPI25_IEEE_SGE_CHAIN64, ++ *PTR_MPI25_IEEE_SGE_CHAIN64, ++ Mpi25IeeeSgeChain64_t, ++ *pMpi25IeeeSgeChain64_t; ++ ++/**************************************************************************** ++* All IEEE SGE types union ++****************************************************************************/ ++ ++/*MPI2_IEEE_SGE_UNION is for MPI v2.0 products only */ ++typedef struct _MPI2_IEEE_SGE_UNION { ++ union { ++ MPI2_IEEE_SGE_SIMPLE_UNION Simple; ++ MPI2_IEEE_SGE_CHAIN_UNION Chain; ++ } u; ++} MPI2_IEEE_SGE_UNION, *PTR_MPI2_IEEE_SGE_UNION, ++ Mpi2IeeeSgeUnion_t, *pMpi2IeeeSgeUnion_t; ++ ++/**************************************************************************** ++* IEEE SGE union for IO SGL's ++****************************************************************************/ ++ ++typedef union _MPI25_SGE_IO_UNION { ++ MPI2_IEEE_SGE_SIMPLE64 IeeeSimple; ++ MPI25_IEEE_SGE_CHAIN64 IeeeChain; ++} MPI25_SGE_IO_UNION, *PTR_MPI25_SGE_IO_UNION, ++ Mpi25SGEIOUnion_t, *pMpi25SGEIOUnion_t; ++ ++/**************************************************************************** ++* IEEE SGE field definitions and masks ++****************************************************************************/ ++ ++/*Flags field bit definitions */ ++ ++#define MPI2_IEEE_SGE_FLAGS_ELEMENT_TYPE_MASK (0x80) ++#define MPI25_IEEE_SGE_FLAGS_END_OF_LIST (0x40) ++ ++#define MPI2_IEEE32_SGE_FLAGS_SHIFT (24) ++ ++#define MPI2_IEEE32_SGE_LENGTH_MASK (0x00FFFFFF) ++ ++/*Element Type */ ++ ++#define MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT (0x00) ++#define MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT (0x80) ++ ++/*Next Segment Format */ ++ ++#define MPI26_IEEE_SGE_FLAGS_NSF_MASK (0x1C) ++#define MPI26_IEEE_SGE_FLAGS_NSF_MPI_IEEE (0x00) ++ ++/*Data Location Address Space */ ++ ++#define MPI2_IEEE_SGE_FLAGS_ADDR_MASK (0x03) ++#define MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR (0x00) ++#define MPI2_IEEE_SGE_FLAGS_IOCDDR_ADDR (0x01) ++#define MPI2_IEEE_SGE_FLAGS_IOCPLB_ADDR (0x02) ++#define MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR (0x03) ++#define MPI2_IEEE_SGE_FLAGS_SYSTEMPLBPCI_ADDR (0x03) ++#define MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR \ ++ (MPI2_IEEE_SGE_FLAGS_SYSTEMPLBPCI_ADDR) ++#define MPI26_IEEE_SGE_FLAGS_IOCCTL_ADDR (0x02) ++ ++/**************************************************************************** ++* IEEE SGE operation Macros ++****************************************************************************/ ++ ++/*SIMPLE FlagsLength manipulations... */ ++#define MPI2_IEEE32_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_IEEE32_SGE_FLAGS_SHIFT) ++#define MPI2_IEEE32_SGE_GET_FLAGS(f) (((f) & ~MPI2_IEEE32_SGE_LENGTH_MASK) \ ++ >> MPI2_IEEE32_SGE_FLAGS_SHIFT) ++#define MPI2_IEEE32_SGE_LENGTH(f) ((f) & MPI2_IEEE32_SGE_LENGTH_MASK) ++ ++#define MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f, l) (MPI2_IEEE32_SGE_SET_FLAGS(f) |\ ++ MPI2_IEEE32_SGE_LENGTH(l)) ++ ++#define MPI2_IEEE32_pSGE_GET_FLAGS(psg) \ ++ MPI2_IEEE32_SGE_GET_FLAGS((psg)->FlagsLength) ++#define MPI2_IEEE32_pSGE_GET_LENGTH(psg) \ ++ MPI2_IEEE32_SGE_LENGTH((psg)->FlagsLength) ++#define MPI2_IEEE32_pSGE_SET_FLAGS_LENGTH(psg, f, l) ((psg)->FlagsLength = \ ++ MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f, l)) ++ ++/*CAUTION - The following are READ-MODIFY-WRITE! */ ++#define MPI2_IEEE32_pSGE_SET_FLAGS(psg, f) ((psg)->FlagsLength |= \ ++ MPI2_IEEE32_SGE_SET_FLAGS(f)) ++#define MPI2_IEEE32_pSGE_SET_LENGTH(psg, l) ((psg)->FlagsLength |= \ ++ MPI2_IEEE32_SGE_LENGTH(l)) ++ ++/***************************************************************************** ++* ++* Fusion-MPT MPI/IEEE Scatter Gather Unions ++* ++*****************************************************************************/ ++ ++typedef union _MPI2_SIMPLE_SGE_UNION { ++ MPI2_SGE_SIMPLE_UNION MpiSimple; ++ MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple; ++} MPI2_SIMPLE_SGE_UNION, *PTR_MPI2_SIMPLE_SGE_UNION, ++ Mpi2SimpleSgeUntion_t, *pMpi2SimpleSgeUntion_t; ++ ++typedef union _MPI2_SGE_IO_UNION { ++ MPI2_SGE_SIMPLE_UNION MpiSimple; ++ MPI2_SGE_CHAIN_UNION MpiChain; ++ MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple; ++ MPI2_IEEE_SGE_CHAIN_UNION IeeeChain; ++} MPI2_SGE_IO_UNION, *PTR_MPI2_SGE_IO_UNION, ++ Mpi2SGEIOUnion_t, *pMpi2SGEIOUnion_t; ++ ++/**************************************************************************** ++* ++* Values for SGLFlags field, used in many request messages with an SGL ++* ++****************************************************************************/ ++ ++/*values for MPI SGL Data Location Address Space subfield */ ++#define MPI2_SGLFLAGS_ADDRESS_SPACE_MASK (0x0C) ++#define MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE (0x00) ++#define MPI2_SGLFLAGS_IOCDDR_ADDRESS_SPACE (0x04) ++#define MPI2_SGLFLAGS_IOCPLB_ADDRESS_SPACE (0x08) ++#define MPI26_SGLFLAGS_IOCPLB_ADDRESS_SPACE (0x08) ++#define MPI2_SGLFLAGS_IOCPLBNTA_ADDRESS_SPACE (0x0C) ++/*values for SGL Type subfield */ ++#define MPI2_SGLFLAGS_SGL_TYPE_MASK (0x03) ++#define MPI2_SGLFLAGS_SGL_TYPE_MPI (0x00) ++#define MPI2_SGLFLAGS_SGL_TYPE_IEEE32 (0x01) ++#define MPI2_SGLFLAGS_SGL_TYPE_IEEE64 (0x02) ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h +new file mode 100644 +index 0000000..95356a8 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h +@@ -0,0 +1,3493 @@ ++/* ++ * Copyright 2000-2015 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2_cnfg.h ++ * Title: MPI Configuration messages and pages ++ * Creation Date: November 10, 2006 ++ * ++ * mpi2_cnfg.h Version: 02.00.35 ++ * ++ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 ++ * prefix are for use only on MPI v2.5 products, and must not be used ++ * with MPI v2.0 products. Unless otherwise noted, names beginning with ++ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products. ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 06-04-07 02.00.01 Added defines for SAS IO Unit Page 2 PhyFlags. ++ * Added Manufacturing Page 11. ++ * Added MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE ++ * define. ++ * 06-26-07 02.00.02 Adding generic structure for product-specific ++ * Manufacturing pages: MPI2_CONFIG_PAGE_MANUFACTURING_PS. ++ * Rework of BIOS Page 2 configuration page. ++ * Fixed MPI2_BIOSPAGE2_BOOT_DEVICE to be a union of the ++ * forms. ++ * Added configuration pages IOC Page 8 and Driver ++ * Persistent Mapping Page 0. ++ * 08-31-07 02.00.03 Modified configuration pages dealing with Integrated ++ * RAID (Manufacturing Page 4, RAID Volume Pages 0 and 1, ++ * RAID Physical Disk Pages 0 and 1, RAID Configuration ++ * Page 0). ++ * Added new value for AccessStatus field of SAS Device ++ * Page 0 (_SATA_NEEDS_INITIALIZATION). ++ * 10-31-07 02.00.04 Added missing SEPDevHandle field to ++ * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. ++ * 12-18-07 02.00.05 Modified IO Unit Page 0 to use 32-bit version fields for ++ * NVDATA. ++ * Modified IOC Page 7 to use masks and added field for ++ * SASBroadcastPrimitiveMasks. ++ * Added MPI2_CONFIG_PAGE_BIOS_4. ++ * Added MPI2_CONFIG_PAGE_LOG_0. ++ * 02-29-08 02.00.06 Modified various names to make them 32-character unique. ++ * Added SAS Device IDs. ++ * Updated Integrated RAID configuration pages including ++ * Manufacturing Page 4, IOC Page 6, and RAID Configuration ++ * Page 0. ++ * 05-21-08 02.00.07 Added define MPI2_MANPAGE4_MIX_SSD_SAS_SATA. ++ * Added define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION. ++ * Fixed define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING. ++ * Added missing MaxNumRoutedSasAddresses field to ++ * MPI2_CONFIG_PAGE_EXPANDER_0. ++ * Added SAS Port Page 0. ++ * Modified structure layout for ++ * MPI2_CONFIG_PAGE_DRIVER_MAPPING_0. ++ * 06-27-08 02.00.08 Changed MPI2_CONFIG_PAGE_RD_PDISK_1 to use ++ * MPI2_RAID_PHYS_DISK1_PATH_MAX to size the array. ++ * 10-02-08 02.00.09 Changed MPI2_RAID_PGAD_CONFIGNUM_MASK from 0x0000FFFF ++ * to 0x000000FF. ++ * Added two new values for the Physical Disk Coercion Size ++ * bits in the Flags field of Manufacturing Page 4. ++ * Added product-specific Manufacturing pages 16 to 31. ++ * Modified Flags bits for controlling write cache on SATA ++ * drives in IO Unit Page 1. ++ * Added new bit to AdditionalControlFlags of SAS IO Unit ++ * Page 1 to control Invalid Topology Correction. ++ * Added additional defines for RAID Volume Page 0 ++ * VolumeStatusFlags field. ++ * Modified meaning of RAID Volume Page 0 VolumeSettings ++ * define for auto-configure of hot-swap drives. ++ * Added SupportedPhysDisks field to RAID Volume Page 1 and ++ * added related defines. ++ * Added PhysDiskAttributes field (and related defines) to ++ * RAID Physical Disk Page 0. ++ * Added MPI2_SAS_PHYINFO_PHY_VACANT define. ++ * Added three new DiscoveryStatus bits for SAS IO Unit ++ * Page 0 and SAS Expander Page 0. ++ * Removed multiplexing information from SAS IO Unit pages. ++ * Added BootDeviceWaitTime field to SAS IO Unit Page 4. ++ * Removed Zone Address Resolved bit from PhyInfo and from ++ * Expander Page 0 Flags field. ++ * Added two new AccessStatus values to SAS Device Page 0 ++ * for indicating routing problems. Added 3 reserved words ++ * to this page. ++ * 01-19-09 02.00.10 Fixed defines for GPIOVal field of IO Unit Page 3. ++ * Inserted missing reserved field into structure for IOC ++ * Page 6. ++ * Added more pending task bits to RAID Volume Page 0 ++ * VolumeStatusFlags defines. ++ * Added MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED define. ++ * Added a new DiscoveryStatus bit for SAS IO Unit Page 0 ++ * and SAS Expander Page 0 to flag a downstream initiator ++ * when in simplified routing mode. ++ * Removed SATA Init Failure defines for DiscoveryStatus ++ * fields of SAS IO Unit Page 0 and SAS Expander Page 0. ++ * Added MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED define. ++ * Added PortGroups, DmaGroup, and ControlGroup fields to ++ * SAS Device Page 0. ++ * 05-06-09 02.00.11 Added structures and defines for IO Unit Page 5 and IO ++ * Unit Page 6. ++ * Added expander reduced functionality data to SAS ++ * Expander Page 0. ++ * Added SAS PHY Page 2 and SAS PHY Page 3. ++ * 07-30-09 02.00.12 Added IO Unit Page 7. ++ * Added new device ids. ++ * Added SAS IO Unit Page 5. ++ * Added partial and slumber power management capable flags ++ * to SAS Device Page 0 Flags field. ++ * Added PhyInfo defines for power condition. ++ * Added Ethernet configuration pages. ++ * 10-28-09 02.00.13 Added MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY. ++ * Added SAS PHY Page 4 structure and defines. ++ * 02-10-10 02.00.14 Modified the comments for the configuration page ++ * structures that contain an array of data. The host ++ * should use the "count" field in the page data (e.g. the ++ * NumPhys field) to determine the number of valid elements ++ * in the array. ++ * Added/modified some MPI2_MFGPAGE_DEVID_SAS defines. ++ * Added PowerManagementCapabilities to IO Unit Page 7. ++ * Added PortWidthModGroup field to ++ * MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS. ++ * Added MPI2_CONFIG_PAGE_SASIOUNIT_6 and related defines. ++ * Added MPI2_CONFIG_PAGE_SASIOUNIT_7 and related defines. ++ * Added MPI2_CONFIG_PAGE_SASIOUNIT_8 and related defines. ++ * 05-12-10 02.00.15 Added MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT ++ * define. ++ * Added MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE define. ++ * Added MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY define. ++ * 08-11-10 02.00.16 Removed IO Unit Page 1 device path (multi-pathing) ++ * defines. ++ * 11-10-10 02.00.17 Added ReceptacleID field (replacing Reserved1) to ++ * MPI2_MANPAGE7_CONNECTOR_INFO and reworked defines for ++ * the Pinout field. ++ * Added BoardTemperature and BoardTemperatureUnits fields ++ * to MPI2_CONFIG_PAGE_IO_UNIT_7. ++ * Added MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING define ++ * and MPI2_CONFIG_PAGE_EXT_MAN_PS structure. ++ * 02-23-11 02.00.18 Added ProxyVF_ID field to MPI2_CONFIG_REQUEST. ++ * Added IO Unit Page 8, IO Unit Page 9, ++ * and IO Unit Page 10. ++ * Added SASNotifyPrimitiveMasks field to ++ * MPI2_CONFIG_PAGE_IOC_7. ++ * 03-09-11 02.00.19 Fixed IO Unit Page 10 (to match the spec). ++ * 05-25-11 02.00.20 Cleaned up a few comments. ++ * 08-24-11 02.00.21 Marked the IO Unit Page 7 PowerManagementCapabilities ++ * for PCIe link as obsolete. ++ * Added SpinupFlags field containing a Disable Spin-up bit ++ * to the MPI2_SAS_IOUNIT4_SPINUP_GROUP fields of SAS IO ++ * Unit Page 4. ++ * 11-18-11 02.00.22 Added define MPI2_IOCPAGE6_CAP_FLAGS_4K_SECTORS_SUPPORT. ++ * Added UEFIVersion field to BIOS Page 1 and defined new ++ * BiosOptions bits. ++ * Incorporating additions for MPI v2.5. ++ * 11-27-12 02.00.23 Added MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER. ++ * Added MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID. ++ * 12-20-12 02.00.24 Marked MPI2_SASIOUNIT1_CONTROL_CLEAR_AFFILIATION as ++ * obsolete for MPI v2.5 and later. ++ * Added some defines for 12G SAS speeds. ++ * 04-09-13 02.00.25 Added MPI2_IOUNITPAGE1_ATA_SECURITY_FREEZE_LOCK. ++ * Fixed MPI2_IOUNITPAGE5_DMA_CAP_MASK_MAX_REQUESTS to ++ * match the specification. ++ * 08-19-13 02.00.26 Added reserved words to MPI2_CONFIG_PAGE_IO_UNIT_7 for ++ * future use. ++ * 12-05-13 02.00.27 Added MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL for ++ * MPI2_CONFIG_PAGE_MAN_7. ++ * Added EnclosureLevel and ConnectorName fields to ++ * MPI2_CONFIG_PAGE_SAS_DEV_0. ++ * Added MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID for ++ * MPI2_CONFIG_PAGE_SAS_DEV_0. ++ * Added EnclosureLevel field to ++ * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. ++ * Added MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID for ++ * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. ++ * 01-08-14 02.00.28 Added more defines for the BiosOptions field of ++ * MPI2_CONFIG_PAGE_BIOS_1. ++ * 06-13-14 02.00.29 Added SSUTimeout field to MPI2_CONFIG_PAGE_BIOS_1, and ++ * more defines for the BiosOptions field. ++ * 11-18-14 02.00.30 Updated copyright information. ++ * Added MPI2_BIOSPAGE1_OPTIONS_ADVANCED_CONFIG. ++ * Added AdapterOrderAux fields to BIOS Page 3. ++ * 03-16-15 02.00.31 Updated for MPI v2.6. ++ * Added Flags field to IO Unit Page 7. ++ * Added new SAS Phy Event codes ++ * 05-25-15 02.00.33 Added more defines for the BiosOptions field of ++ * MPI2_CONFIG_PAGE_BIOS_1. ++ * 08-25-15 02.00.34 Bumped Header Version. ++ * 12-18-15 02.00.35 Added SATADeviceWaitTime to SAS IO Unit Page 4. ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_CNFG_H ++#define MPI2_CNFG_H ++ ++/***************************************************************************** ++* Configuration Page Header and defines ++*****************************************************************************/ ++ ++/*Config Page Header */ ++typedef struct _MPI2_CONFIG_PAGE_HEADER { ++ U8 PageVersion; /*0x00 */ ++ U8 PageLength; /*0x01 */ ++ U8 PageNumber; /*0x02 */ ++ U8 PageType; /*0x03 */ ++} MPI2_CONFIG_PAGE_HEADER, *PTR_MPI2_CONFIG_PAGE_HEADER, ++ Mpi2ConfigPageHeader_t, *pMpi2ConfigPageHeader_t; ++ ++typedef union _MPI2_CONFIG_PAGE_HEADER_UNION { ++ MPI2_CONFIG_PAGE_HEADER Struct; ++ U8 Bytes[4]; ++ U16 Word16[2]; ++ U32 Word32; ++} MPI2_CONFIG_PAGE_HEADER_UNION, *PTR_MPI2_CONFIG_PAGE_HEADER_UNION, ++ Mpi2ConfigPageHeaderUnion, *pMpi2ConfigPageHeaderUnion; ++ ++/*Extended Config Page Header */ ++typedef struct _MPI2_CONFIG_EXTENDED_PAGE_HEADER { ++ U8 PageVersion; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 PageNumber; /*0x02 */ ++ U8 PageType; /*0x03 */ ++ U16 ExtPageLength; /*0x04 */ ++ U8 ExtPageType; /*0x06 */ ++ U8 Reserved2; /*0x07 */ ++} MPI2_CONFIG_EXTENDED_PAGE_HEADER, ++ *PTR_MPI2_CONFIG_EXTENDED_PAGE_HEADER, ++ Mpi2ConfigExtendedPageHeader_t, ++ *pMpi2ConfigExtendedPageHeader_t; ++ ++typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION { ++ MPI2_CONFIG_PAGE_HEADER Struct; ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Ext; ++ U8 Bytes[8]; ++ U16 Word16[4]; ++ U32 Word32[2]; ++} MPI2_CONFIG_EXT_PAGE_HEADER_UNION, ++ *PTR_MPI2_CONFIG_EXT_PAGE_HEADER_UNION, ++ Mpi2ConfigPageExtendedHeaderUnion, ++ *pMpi2ConfigPageExtendedHeaderUnion; ++ ++ ++/*PageType field values */ ++#define MPI2_CONFIG_PAGEATTR_READ_ONLY (0x00) ++#define MPI2_CONFIG_PAGEATTR_CHANGEABLE (0x10) ++#define MPI2_CONFIG_PAGEATTR_PERSISTENT (0x20) ++#define MPI2_CONFIG_PAGEATTR_MASK (0xF0) ++ ++#define MPI2_CONFIG_PAGETYPE_IO_UNIT (0x00) ++#define MPI2_CONFIG_PAGETYPE_IOC (0x01) ++#define MPI2_CONFIG_PAGETYPE_BIOS (0x02) ++#define MPI2_CONFIG_PAGETYPE_RAID_VOLUME (0x08) ++#define MPI2_CONFIG_PAGETYPE_MANUFACTURING (0x09) ++#define MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK (0x0A) ++#define MPI2_CONFIG_PAGETYPE_EXTENDED (0x0F) ++#define MPI2_CONFIG_PAGETYPE_MASK (0x0F) ++ ++#define MPI2_CONFIG_TYPENUM_MASK (0x0FFF) ++ ++ ++/*ExtPageType field values */ ++#define MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT (0x10) ++#define MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER (0x11) ++#define MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE (0x12) ++#define MPI2_CONFIG_EXTPAGETYPE_SAS_PHY (0x13) ++#define MPI2_CONFIG_EXTPAGETYPE_LOG (0x14) ++#define MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE (0x15) ++#define MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG (0x16) ++#define MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING (0x17) ++#define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT (0x18) ++#define MPI2_CONFIG_EXTPAGETYPE_ETHERNET (0x19) ++#define MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING (0x1A) ++ ++ ++/***************************************************************************** ++* PageAddress defines ++*****************************************************************************/ ++ ++/*RAID Volume PageAddress format */ ++#define MPI2_RAID_VOLUME_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) ++#define MPI2_RAID_VOLUME_PGAD_FORM_HANDLE (0x10000000) ++ ++#define MPI2_RAID_VOLUME_PGAD_HANDLE_MASK (0x0000FFFF) ++ ++ ++/*RAID Physical Disk PageAddress format */ ++#define MPI2_PHYSDISK_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM (0x00000000) ++#define MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM (0x10000000) ++#define MPI2_PHYSDISK_PGAD_FORM_DEVHANDLE (0x20000000) ++ ++#define MPI2_PHYSDISK_PGAD_PHYSDISKNUM_MASK (0x000000FF) ++#define MPI2_PHYSDISK_PGAD_DEVHANDLE_MASK (0x0000FFFF) ++ ++ ++/*SAS Expander PageAddress format */ ++#define MPI2_SAS_EXPAND_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL (0x00000000) ++#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM (0x10000000) ++#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL (0x20000000) ++ ++#define MPI2_SAS_EXPAND_PGAD_HANDLE_MASK (0x0000FFFF) ++#define MPI2_SAS_EXPAND_PGAD_PHYNUM_MASK (0x00FF0000) ++#define MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT (16) ++ ++ ++/*SAS Device PageAddress format */ ++#define MPI2_SAS_DEVICE_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) ++#define MPI2_SAS_DEVICE_PGAD_FORM_HANDLE (0x20000000) ++ ++#define MPI2_SAS_DEVICE_PGAD_HANDLE_MASK (0x0000FFFF) ++ ++ ++/*SAS PHY PageAddress format */ ++#define MPI2_SAS_PHY_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER (0x00000000) ++#define MPI2_SAS_PHY_PGAD_FORM_PHY_TBL_INDEX (0x10000000) ++ ++#define MPI2_SAS_PHY_PGAD_PHY_NUMBER_MASK (0x000000FF) ++#define MPI2_SAS_PHY_PGAD_PHY_TBL_INDEX_MASK (0x0000FFFF) ++ ++ ++/*SAS Port PageAddress format */ ++#define MPI2_SASPORT_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_SASPORT_PGAD_FORM_GET_NEXT_PORT (0x00000000) ++#define MPI2_SASPORT_PGAD_FORM_PORT_NUM (0x10000000) ++ ++#define MPI2_SASPORT_PGAD_PORTNUMBER_MASK (0x00000FFF) ++ ++ ++/*SAS Enclosure PageAddress format */ ++#define MPI2_SAS_ENCLOS_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) ++#define MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE (0x10000000) ++ ++#define MPI2_SAS_ENCLOS_PGAD_HANDLE_MASK (0x0000FFFF) ++ ++ ++/*RAID Configuration PageAddress format */ ++#define MPI2_RAID_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_RAID_PGAD_FORM_GET_NEXT_CONFIGNUM (0x00000000) ++#define MPI2_RAID_PGAD_FORM_CONFIGNUM (0x10000000) ++#define MPI2_RAID_PGAD_FORM_ACTIVE_CONFIG (0x20000000) ++ ++#define MPI2_RAID_PGAD_CONFIGNUM_MASK (0x000000FF) ++ ++ ++/*Driver Persistent Mapping PageAddress format */ ++#define MPI2_DPM_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_DPM_PGAD_FORM_ENTRY_RANGE (0x00000000) ++ ++#define MPI2_DPM_PGAD_ENTRY_COUNT_MASK (0x0FFF0000) ++#define MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT (16) ++#define MPI2_DPM_PGAD_START_ENTRY_MASK (0x0000FFFF) ++ ++ ++/*Ethernet PageAddress format */ ++#define MPI2_ETHERNET_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_ETHERNET_PGAD_FORM_IF_NUM (0x00000000) ++ ++#define MPI2_ETHERNET_PGAD_IF_NUMBER_MASK (0x000000FF) ++ ++ ++/**************************************************************************** ++* Configuration messages ++****************************************************************************/ ++ ++/*Configuration Request Message */ ++typedef struct _MPI2_CONFIG_REQUEST { ++ U8 Action; /*0x00 */ ++ U8 SGLFlags; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 ExtPageLength; /*0x04 */ ++ U8 ExtPageType; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U8 Reserved2; /*0x0C */ ++ U8 ProxyVF_ID; /*0x0D */ ++ U16 Reserved4; /*0x0E */ ++ U32 Reserved3; /*0x10 */ ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x14 */ ++ U32 PageAddress; /*0x18 */ ++ MPI2_SGE_IO_UNION PageBufferSGE; /*0x1C */ ++} MPI2_CONFIG_REQUEST, *PTR_MPI2_CONFIG_REQUEST, ++ Mpi2ConfigRequest_t, *pMpi2ConfigRequest_t; ++ ++/*values for the Action field */ ++#define MPI2_CONFIG_ACTION_PAGE_HEADER (0x00) ++#define MPI2_CONFIG_ACTION_PAGE_READ_CURRENT (0x01) ++#define MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT (0x02) ++#define MPI2_CONFIG_ACTION_PAGE_DEFAULT (0x03) ++#define MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM (0x04) ++#define MPI2_CONFIG_ACTION_PAGE_READ_DEFAULT (0x05) ++#define MPI2_CONFIG_ACTION_PAGE_READ_NVRAM (0x06) ++#define MPI2_CONFIG_ACTION_PAGE_GET_CHANGEABLE (0x07) ++ ++/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ ++ ++ ++/*Config Reply Message */ ++typedef struct _MPI2_CONFIG_REPLY { ++ U8 Action; /*0x00 */ ++ U8 SGLFlags; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 ExtPageLength; /*0x04 */ ++ U8 ExtPageType; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U16 Reserved2; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x14 */ ++} MPI2_CONFIG_REPLY, *PTR_MPI2_CONFIG_REPLY, ++ Mpi2ConfigReply_t, *pMpi2ConfigReply_t; ++ ++ ++ ++/***************************************************************************** ++* ++* C o n f i g u r a t i o n P a g e s ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* Manufacturing Config pages ++****************************************************************************/ ++ ++#define MPI2_MFGPAGE_VENDORID_LSI (0x1000) ++ ++/*MPI v2.0 SAS products */ ++#define MPI2_MFGPAGE_DEVID_SAS2004 (0x0070) ++#define MPI2_MFGPAGE_DEVID_SAS2008 (0x0072) ++#define MPI2_MFGPAGE_DEVID_SAS2108_1 (0x0074) ++#define MPI2_MFGPAGE_DEVID_SAS2108_2 (0x0076) ++#define MPI2_MFGPAGE_DEVID_SAS2108_3 (0x0077) ++#define MPI2_MFGPAGE_DEVID_SAS2116_1 (0x0064) ++#define MPI2_MFGPAGE_DEVID_SAS2116_2 (0x0065) ++ ++#define MPI2_MFGPAGE_DEVID_SSS6200 (0x007E) ++ ++#define MPI2_MFGPAGE_DEVID_SAS2208_1 (0x0080) ++#define MPI2_MFGPAGE_DEVID_SAS2208_2 (0x0081) ++#define MPI2_MFGPAGE_DEVID_SAS2208_3 (0x0082) ++#define MPI2_MFGPAGE_DEVID_SAS2208_4 (0x0083) ++#define MPI2_MFGPAGE_DEVID_SAS2208_5 (0x0084) ++#define MPI2_MFGPAGE_DEVID_SAS2208_6 (0x0085) ++#define MPI2_MFGPAGE_DEVID_SAS2308_1 (0x0086) ++#define MPI2_MFGPAGE_DEVID_SAS2308_2 (0x0087) ++#define MPI2_MFGPAGE_DEVID_SAS2308_3 (0x006E) ++ ++/*MPI v2.5 SAS products */ ++#define MPI25_MFGPAGE_DEVID_SAS3004 (0x0096) ++#define MPI25_MFGPAGE_DEVID_SAS3008 (0x0097) ++#define MPI25_MFGPAGE_DEVID_SAS3108_1 (0x0090) ++#define MPI25_MFGPAGE_DEVID_SAS3108_2 (0x0091) ++#define MPI25_MFGPAGE_DEVID_SAS3108_5 (0x0094) ++#define MPI25_MFGPAGE_DEVID_SAS3108_6 (0x0095) ++ ++/* MPI v2.6 SAS Products */ ++#define MPI26_MFGPAGE_DEVID_SAS3216 (0x00C9) ++#define MPI26_MFGPAGE_DEVID_SAS3224 (0x00C4) ++#define MPI26_MFGPAGE_DEVID_SAS3316_1 (0x00C5) ++#define MPI26_MFGPAGE_DEVID_SAS3316_2 (0x00C6) ++#define MPI26_MFGPAGE_DEVID_SAS3316_3 (0x00C7) ++#define MPI26_MFGPAGE_DEVID_SAS3316_4 (0x00C8) ++#define MPI26_MFGPAGE_DEVID_SAS3324_1 (0x00C0) ++#define MPI26_MFGPAGE_DEVID_SAS3324_2 (0x00C1) ++#define MPI26_MFGPAGE_DEVID_SAS3324_3 (0x00C2) ++#define MPI26_MFGPAGE_DEVID_SAS3324_4 (0x00C3) ++ ++/*Manufacturing Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_0 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 ChipName[16]; /*0x04 */ ++ U8 ChipRevision[8]; /*0x14 */ ++ U8 BoardName[16]; /*0x1C */ ++ U8 BoardAssembly[16]; /*0x2C */ ++ U8 BoardTracerNumber[16]; /*0x3C */ ++} MPI2_CONFIG_PAGE_MAN_0, ++ *PTR_MPI2_CONFIG_PAGE_MAN_0, ++ Mpi2ManufacturingPage0_t, ++ *pMpi2ManufacturingPage0_t; ++ ++#define MPI2_MANUFACTURING0_PAGEVERSION (0x00) ++ ++ ++/*Manufacturing Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_1 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 VPD[256]; /*0x04 */ ++} MPI2_CONFIG_PAGE_MAN_1, ++ *PTR_MPI2_CONFIG_PAGE_MAN_1, ++ Mpi2ManufacturingPage1_t, ++ *pMpi2ManufacturingPage1_t; ++ ++#define MPI2_MANUFACTURING1_PAGEVERSION (0x00) ++ ++ ++typedef struct _MPI2_CHIP_REVISION_ID { ++ U16 DeviceID; /*0x00 */ ++ U8 PCIRevisionID; /*0x02 */ ++ U8 Reserved; /*0x03 */ ++} MPI2_CHIP_REVISION_ID, *PTR_MPI2_CHIP_REVISION_ID, ++ Mpi2ChipRevisionId_t, *pMpi2ChipRevisionId_t; ++ ++ ++/*Manufacturing Page 2 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check Header.PageLength at runtime. ++ */ ++#ifndef MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS ++#define MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_2 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ MPI2_CHIP_REVISION_ID ChipId; /*0x04 */ ++ U32 ++ HwSettings[MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS];/*0x08 */ ++} MPI2_CONFIG_PAGE_MAN_2, ++ *PTR_MPI2_CONFIG_PAGE_MAN_2, ++ Mpi2ManufacturingPage2_t, ++ *pMpi2ManufacturingPage2_t; ++ ++#define MPI2_MANUFACTURING2_PAGEVERSION (0x00) ++ ++ ++/*Manufacturing Page 3 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check Header.PageLength at runtime. ++ */ ++#ifndef MPI2_MAN_PAGE_3_INFO_WORDS ++#define MPI2_MAN_PAGE_3_INFO_WORDS (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_3 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ MPI2_CHIP_REVISION_ID ChipId; /*0x04 */ ++ U32 ++ Info[MPI2_MAN_PAGE_3_INFO_WORDS];/*0x08 */ ++} MPI2_CONFIG_PAGE_MAN_3, ++ *PTR_MPI2_CONFIG_PAGE_MAN_3, ++ Mpi2ManufacturingPage3_t, ++ *pMpi2ManufacturingPage3_t; ++ ++#define MPI2_MANUFACTURING3_PAGEVERSION (0x00) ++ ++ ++/*Manufacturing Page 4 */ ++ ++typedef struct _MPI2_MANPAGE4_PWR_SAVE_SETTINGS { ++ U8 PowerSaveFlags; /*0x00 */ ++ U8 InternalOperationsSleepTime; /*0x01 */ ++ U8 InternalOperationsRunTime; /*0x02 */ ++ U8 HostIdleTime; /*0x03 */ ++} MPI2_MANPAGE4_PWR_SAVE_SETTINGS, ++ *PTR_MPI2_MANPAGE4_PWR_SAVE_SETTINGS, ++ Mpi2ManPage4PwrSaveSettings_t, ++ *pMpi2ManPage4PwrSaveSettings_t; ++ ++/*defines for the PowerSaveFlags field */ ++#define MPI2_MANPAGE4_MASK_POWERSAVE_MODE (0x03) ++#define MPI2_MANPAGE4_POWERSAVE_MODE_DISABLED (0x00) ++#define MPI2_MANPAGE4_CUSTOM_POWERSAVE_MODE (0x01) ++#define MPI2_MANPAGE4_FULL_POWERSAVE_MODE (0x02) ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_4 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 Flags; /*0x08 */ ++ U8 InquirySize; /*0x0C */ ++ U8 Reserved2; /*0x0D */ ++ U16 Reserved3; /*0x0E */ ++ U8 InquiryData[56]; /*0x10 */ ++ U32 RAID0VolumeSettings; /*0x48 */ ++ U32 RAID1EVolumeSettings; /*0x4C */ ++ U32 RAID1VolumeSettings; /*0x50 */ ++ U32 RAID10VolumeSettings; /*0x54 */ ++ U32 Reserved4; /*0x58 */ ++ U32 Reserved5; /*0x5C */ ++ MPI2_MANPAGE4_PWR_SAVE_SETTINGS PowerSaveSettings; /*0x60 */ ++ U8 MaxOCEDisks; /*0x64 */ ++ U8 ResyncRate; /*0x65 */ ++ U16 DataScrubDuration; /*0x66 */ ++ U8 MaxHotSpares; /*0x68 */ ++ U8 MaxPhysDisksPerVol; /*0x69 */ ++ U8 MaxPhysDisks; /*0x6A */ ++ U8 MaxVolumes; /*0x6B */ ++} MPI2_CONFIG_PAGE_MAN_4, ++ *PTR_MPI2_CONFIG_PAGE_MAN_4, ++ Mpi2ManufacturingPage4_t, ++ *pMpi2ManufacturingPage4_t; ++ ++#define MPI2_MANUFACTURING4_PAGEVERSION (0x0A) ++ ++/*Manufacturing Page 4 Flags field */ ++#define MPI2_MANPAGE4_METADATA_SIZE_MASK (0x00030000) ++#define MPI2_MANPAGE4_METADATA_512MB (0x00000000) ++ ++#define MPI2_MANPAGE4_MIX_SSD_SAS_SATA (0x00008000) ++#define MPI2_MANPAGE4_MIX_SSD_AND_NON_SSD (0x00004000) ++#define MPI2_MANPAGE4_HIDE_PHYSDISK_NON_IR (0x00002000) ++ ++#define MPI2_MANPAGE4_MASK_PHYSDISK_COERCION (0x00001C00) ++#define MPI2_MANPAGE4_PHYSDISK_COERCION_1GB (0x00000000) ++#define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION (0x00000400) ++#define MPI2_MANPAGE4_PHYSDISK_ADAPTIVE_COERCION (0x00000800) ++#define MPI2_MANPAGE4_PHYSDISK_ZERO_COERCION (0x00000C00) ++ ++#define MPI2_MANPAGE4_MASK_BAD_BLOCK_MARKING (0x00000300) ++#define MPI2_MANPAGE4_DEFAULT_BAD_BLOCK_MARKING (0x00000000) ++#define MPI2_MANPAGE4_TABLE_BAD_BLOCK_MARKING (0x00000100) ++#define MPI2_MANPAGE4_WRITE_LONG_BAD_BLOCK_MARKING (0x00000200) ++ ++#define MPI2_MANPAGE4_FORCE_OFFLINE_FAILOVER (0x00000080) ++#define MPI2_MANPAGE4_RAID10_DISABLE (0x00000040) ++#define MPI2_MANPAGE4_RAID1E_DISABLE (0x00000020) ++#define MPI2_MANPAGE4_RAID1_DISABLE (0x00000010) ++#define MPI2_MANPAGE4_RAID0_DISABLE (0x00000008) ++#define MPI2_MANPAGE4_IR_MODEPAGE8_DISABLE (0x00000004) ++#define MPI2_MANPAGE4_IM_RESYNC_CACHE_ENABLE (0x00000002) ++#define MPI2_MANPAGE4_IR_NO_MIX_SAS_SATA (0x00000001) ++ ++ ++/*Manufacturing Page 5 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI2_MAN_PAGE_5_PHY_ENTRIES ++#define MPI2_MAN_PAGE_5_PHY_ENTRIES (1) ++#endif ++ ++typedef struct _MPI2_MANUFACTURING5_ENTRY { ++ U64 WWID; /*0x00 */ ++ U64 DeviceName; /*0x08 */ ++} MPI2_MANUFACTURING5_ENTRY, ++ *PTR_MPI2_MANUFACTURING5_ENTRY, ++ Mpi2Manufacturing5Entry_t, ++ *pMpi2Manufacturing5Entry_t; ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_5 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 NumPhys; /*0x04 */ ++ U8 Reserved1; /*0x05 */ ++ U16 Reserved2; /*0x06 */ ++ U32 Reserved3; /*0x08 */ ++ U32 Reserved4; /*0x0C */ ++ MPI2_MANUFACTURING5_ENTRY ++ Phy[MPI2_MAN_PAGE_5_PHY_ENTRIES];/*0x08 */ ++} MPI2_CONFIG_PAGE_MAN_5, ++ *PTR_MPI2_CONFIG_PAGE_MAN_5, ++ Mpi2ManufacturingPage5_t, ++ *pMpi2ManufacturingPage5_t; ++ ++#define MPI2_MANUFACTURING5_PAGEVERSION (0x03) ++ ++ ++/*Manufacturing Page 6 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_6 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 ProductSpecificInfo;/*0x04 */ ++} MPI2_CONFIG_PAGE_MAN_6, ++ *PTR_MPI2_CONFIG_PAGE_MAN_6, ++ Mpi2ManufacturingPage6_t, ++ *pMpi2ManufacturingPage6_t; ++ ++#define MPI2_MANUFACTURING6_PAGEVERSION (0x00) ++ ++ ++/*Manufacturing Page 7 */ ++ ++typedef struct _MPI2_MANPAGE7_CONNECTOR_INFO { ++ U32 Pinout; /*0x00 */ ++ U8 Connector[16]; /*0x04 */ ++ U8 Location; /*0x14 */ ++ U8 ReceptacleID; /*0x15 */ ++ U16 Slot; /*0x16 */ ++ U32 Reserved2; /*0x18 */ ++} MPI2_MANPAGE7_CONNECTOR_INFO, ++ *PTR_MPI2_MANPAGE7_CONNECTOR_INFO, ++ Mpi2ManPage7ConnectorInfo_t, ++ *pMpi2ManPage7ConnectorInfo_t; ++ ++/*defines for the Pinout field */ ++#define MPI2_MANPAGE7_PINOUT_LANE_MASK (0x0000FF00) ++#define MPI2_MANPAGE7_PINOUT_LANE_SHIFT (8) ++ ++#define MPI2_MANPAGE7_PINOUT_TYPE_MASK (0x000000FF) ++#define MPI2_MANPAGE7_PINOUT_TYPE_UNKNOWN (0x00) ++#define MPI2_MANPAGE7_PINOUT_SATA_SINGLE (0x01) ++#define MPI2_MANPAGE7_PINOUT_SFF_8482 (0x02) ++#define MPI2_MANPAGE7_PINOUT_SFF_8486 (0x03) ++#define MPI2_MANPAGE7_PINOUT_SFF_8484 (0x04) ++#define MPI2_MANPAGE7_PINOUT_SFF_8087 (0x05) ++#define MPI2_MANPAGE7_PINOUT_SFF_8643_4I (0x06) ++#define MPI2_MANPAGE7_PINOUT_SFF_8643_8I (0x07) ++#define MPI2_MANPAGE7_PINOUT_SFF_8470 (0x08) ++#define MPI2_MANPAGE7_PINOUT_SFF_8088 (0x09) ++#define MPI2_MANPAGE7_PINOUT_SFF_8644_4X (0x0A) ++#define MPI2_MANPAGE7_PINOUT_SFF_8644_8X (0x0B) ++#define MPI2_MANPAGE7_PINOUT_SFF_8644_16X (0x0C) ++#define MPI2_MANPAGE7_PINOUT_SFF_8436 (0x0D) ++ ++/*defines for the Location field */ ++#define MPI2_MANPAGE7_LOCATION_UNKNOWN (0x01) ++#define MPI2_MANPAGE7_LOCATION_INTERNAL (0x02) ++#define MPI2_MANPAGE7_LOCATION_EXTERNAL (0x04) ++#define MPI2_MANPAGE7_LOCATION_SWITCHABLE (0x08) ++#define MPI2_MANPAGE7_LOCATION_AUTO (0x10) ++#define MPI2_MANPAGE7_LOCATION_NOT_PRESENT (0x20) ++#define MPI2_MANPAGE7_LOCATION_NOT_CONNECTED (0x80) ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI2_MANPAGE7_CONNECTOR_INFO_MAX ++#define MPI2_MANPAGE7_CONNECTOR_INFO_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_7 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 Reserved2; /*0x08 */ ++ U32 Flags; /*0x0C */ ++ U8 EnclosureName[16]; /*0x10 */ ++ U8 NumPhys; /*0x20 */ ++ U8 Reserved3; /*0x21 */ ++ U16 Reserved4; /*0x22 */ ++ MPI2_MANPAGE7_CONNECTOR_INFO ++ ConnectorInfo[MPI2_MANPAGE7_CONNECTOR_INFO_MAX]; /*0x24 */ ++} MPI2_CONFIG_PAGE_MAN_7, ++ *PTR_MPI2_CONFIG_PAGE_MAN_7, ++ Mpi2ManufacturingPage7_t, ++ *pMpi2ManufacturingPage7_t; ++ ++#define MPI2_MANUFACTURING7_PAGEVERSION (0x01) ++ ++/*defines for the Flags field */ ++#define MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL (0x00000008) ++#define MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER (0x00000002) ++#define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO (0x00000001) ++ ++ ++/* ++ *Generic structure to use for product-specific manufacturing pages ++ *(currently Manufacturing Page 8 through Manufacturing Page 31). ++ */ ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_PS { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 ProductSpecificInfo;/*0x04 */ ++} MPI2_CONFIG_PAGE_MAN_PS, ++ *PTR_MPI2_CONFIG_PAGE_MAN_PS, ++ Mpi2ManufacturingPagePS_t, ++ *pMpi2ManufacturingPagePS_t; ++ ++#define MPI2_MANUFACTURING8_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING9_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING10_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING11_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING12_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING13_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING14_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING15_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING16_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING17_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING18_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING19_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING20_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING21_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING22_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING23_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING24_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING25_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING26_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING27_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING28_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING29_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING30_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING31_PAGEVERSION (0x00) ++ ++ ++/**************************************************************************** ++* IO Unit Config Pages ++****************************************************************************/ ++ ++/*IO Unit Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_0 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U64 UniqueValue; /*0x04 */ ++ MPI2_VERSION_UNION NvdataVersionDefault; /*0x08 */ ++ MPI2_VERSION_UNION NvdataVersionPersistent; /*0x0A */ ++} MPI2_CONFIG_PAGE_IO_UNIT_0, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_0, ++ Mpi2IOUnitPage0_t, *pMpi2IOUnitPage0_t; ++ ++#define MPI2_IOUNITPAGE0_PAGEVERSION (0x02) ++ ++ ++/*IO Unit Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_1 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Flags; /*0x04 */ ++} MPI2_CONFIG_PAGE_IO_UNIT_1, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_1, ++ Mpi2IOUnitPage1_t, *pMpi2IOUnitPage1_t; ++ ++#define MPI2_IOUNITPAGE1_PAGEVERSION (0x04) ++ ++/*IO Unit Page 1 Flags defines */ ++#define MPI2_IOUNITPAGE1_ATA_SECURITY_FREEZE_LOCK (0x00004000) ++#define MPI25_IOUNITPAGE1_NEW_DEVICE_FAST_PATH_DISABLE (0x00002000) ++#define MPI25_IOUNITPAGE1_DISABLE_FAST_PATH (0x00001000) ++#define MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY (0x00000800) ++#define MPI2_IOUNITPAGE1_MASK_SATA_WRITE_CACHE (0x00000600) ++#define MPI2_IOUNITPAGE1_SATA_WRITE_CACHE_SHIFT (9) ++#define MPI2_IOUNITPAGE1_ENABLE_SATA_WRITE_CACHE (0x00000000) ++#define MPI2_IOUNITPAGE1_DISABLE_SATA_WRITE_CACHE (0x00000200) ++#define MPI2_IOUNITPAGE1_UNCHANGED_SATA_WRITE_CACHE (0x00000400) ++#define MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE (0x00000100) ++#define MPI2_IOUNITPAGE1_DISABLE_IR (0x00000040) ++#define MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING (0x00000020) ++#define MPI2_IOUNITPAGE1_IR_USE_STATIC_VOLUME_ID (0x00000004) ++ ++ ++/*IO Unit Page 3 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for GPIOCount at runtime. ++ */ ++#ifndef MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX ++#define MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_3 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 GPIOCount; /*0x04 */ ++ U8 Reserved1; /*0x05 */ ++ U16 Reserved2; /*0x06 */ ++ U16 ++ GPIOVal[MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX];/*0x08 */ ++} MPI2_CONFIG_PAGE_IO_UNIT_3, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_3, ++ Mpi2IOUnitPage3_t, *pMpi2IOUnitPage3_t; ++ ++#define MPI2_IOUNITPAGE3_PAGEVERSION (0x01) ++ ++/*defines for IO Unit Page 3 GPIOVal field */ ++#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_MASK (0xFFFC) ++#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_SHIFT (2) ++#define MPI2_IOUNITPAGE3_GPIO_SETTING_OFF (0x0000) ++#define MPI2_IOUNITPAGE3_GPIO_SETTING_ON (0x0001) ++ ++ ++/*IO Unit Page 5 */ ++ ++/* ++ *Upper layer code (drivers, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumDmaEngines at runtime. ++ */ ++#ifndef MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES ++#define MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_5 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U64 ++ RaidAcceleratorBufferBaseAddress; /*0x04 */ ++ U64 ++ RaidAcceleratorBufferSize; /*0x0C */ ++ U64 ++ RaidAcceleratorControlBaseAddress; /*0x14 */ ++ U8 RAControlSize; /*0x1C */ ++ U8 NumDmaEngines; /*0x1D */ ++ U8 RAMinControlSize; /*0x1E */ ++ U8 RAMaxControlSize; /*0x1F */ ++ U32 Reserved1; /*0x20 */ ++ U32 Reserved2; /*0x24 */ ++ U32 Reserved3; /*0x28 */ ++ U32 ++ DmaEngineCapabilities[MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES]; /*0x2C */ ++} MPI2_CONFIG_PAGE_IO_UNIT_5, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_5, ++ Mpi2IOUnitPage5_t, *pMpi2IOUnitPage5_t; ++ ++#define MPI2_IOUNITPAGE5_PAGEVERSION (0x00) ++ ++/*defines for IO Unit Page 5 DmaEngineCapabilities field */ ++#define MPI2_IOUNITPAGE5_DMA_CAP_MASK_MAX_REQUESTS (0xFFFF0000) ++#define MPI2_IOUNITPAGE5_DMA_CAP_SHIFT_MAX_REQUESTS (16) ++ ++#define MPI2_IOUNITPAGE5_DMA_CAP_EEDP (0x0008) ++#define MPI2_IOUNITPAGE5_DMA_CAP_PARITY_GENERATION (0x0004) ++#define MPI2_IOUNITPAGE5_DMA_CAP_HASHING (0x0002) ++#define MPI2_IOUNITPAGE5_DMA_CAP_ENCRYPTION (0x0001) ++ ++ ++/*IO Unit Page 6 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_6 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U16 Flags; /*0x04 */ ++ U8 RAHostControlSize; /*0x06 */ ++ U8 Reserved0; /*0x07 */ ++ U64 ++ RaidAcceleratorHostControlBaseAddress; /*0x08 */ ++ U32 Reserved1; /*0x10 */ ++ U32 Reserved2; /*0x14 */ ++ U32 Reserved3; /*0x18 */ ++} MPI2_CONFIG_PAGE_IO_UNIT_6, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_6, ++ Mpi2IOUnitPage6_t, *pMpi2IOUnitPage6_t; ++ ++#define MPI2_IOUNITPAGE6_PAGEVERSION (0x00) ++ ++/*defines for IO Unit Page 6 Flags field */ ++#define MPI2_IOUNITPAGE6_FLAGS_ENABLE_RAID_ACCELERATOR (0x0001) ++ ++ ++/*IO Unit Page 7 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 CurrentPowerMode; /*0x04 */ ++ U8 PreviousPowerMode; /*0x05 */ ++ U8 PCIeWidth; /*0x06 */ ++ U8 PCIeSpeed; /*0x07 */ ++ U32 ProcessorState; /*0x08 */ ++ U32 ++ PowerManagementCapabilities; /*0x0C */ ++ U16 IOCTemperature; /*0x10 */ ++ U8 ++ IOCTemperatureUnits; /*0x12 */ ++ U8 IOCSpeed; /*0x13 */ ++ U16 BoardTemperature; /*0x14 */ ++ U8 ++ BoardTemperatureUnits; /*0x16 */ ++ U8 Reserved3; /*0x17 */ ++ U32 BoardPowerRequirement; /*0x18 */ ++ U32 PCISlotPowerAllocation; /*0x1C */ ++/* reserved prior to MPI v2.6 */ ++ U8 Flags; /* 0x20 */ ++ U8 Reserved6; /* 0x21 */ ++ U16 Reserved7; /* 0x22 */ ++ U32 Reserved8; /* 0x24 */ ++} MPI2_CONFIG_PAGE_IO_UNIT_7, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_7, ++ Mpi2IOUnitPage7_t, *pMpi2IOUnitPage7_t; ++ ++#define MPI2_IOUNITPAGE7_PAGEVERSION (0x05) ++ ++/*defines for IO Unit Page 7 CurrentPowerMode and PreviousPowerMode fields */ ++#define MPI25_IOUNITPAGE7_PM_INIT_MASK (0xC0) ++#define MPI25_IOUNITPAGE7_PM_INIT_UNAVAILABLE (0x00) ++#define MPI25_IOUNITPAGE7_PM_INIT_HOST (0x40) ++#define MPI25_IOUNITPAGE7_PM_INIT_IO_UNIT (0x80) ++#define MPI25_IOUNITPAGE7_PM_INIT_PCIE_DPA (0xC0) ++ ++#define MPI25_IOUNITPAGE7_PM_MODE_MASK (0x07) ++#define MPI25_IOUNITPAGE7_PM_MODE_UNAVAILABLE (0x00) ++#define MPI25_IOUNITPAGE7_PM_MODE_UNKNOWN (0x01) ++#define MPI25_IOUNITPAGE7_PM_MODE_FULL_POWER (0x04) ++#define MPI25_IOUNITPAGE7_PM_MODE_REDUCED_POWER (0x05) ++#define MPI25_IOUNITPAGE7_PM_MODE_STANDBY (0x06) ++ ++ ++/*defines for IO Unit Page 7 PCIeWidth field */ ++#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X1 (0x01) ++#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X2 (0x02) ++#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X4 (0x04) ++#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X8 (0x08) ++ ++/*defines for IO Unit Page 7 PCIeSpeed field */ ++#define MPI2_IOUNITPAGE7_PCIE_SPEED_2_5_GBPS (0x00) ++#define MPI2_IOUNITPAGE7_PCIE_SPEED_5_0_GBPS (0x01) ++#define MPI2_IOUNITPAGE7_PCIE_SPEED_8_0_GBPS (0x02) ++ ++/*defines for IO Unit Page 7 ProcessorState field */ ++#define MPI2_IOUNITPAGE7_PSTATE_MASK_SECOND (0x0000000F) ++#define MPI2_IOUNITPAGE7_PSTATE_SHIFT_SECOND (0) ++ ++#define MPI2_IOUNITPAGE7_PSTATE_NOT_PRESENT (0x00) ++#define MPI2_IOUNITPAGE7_PSTATE_DISABLED (0x01) ++#define MPI2_IOUNITPAGE7_PSTATE_ENABLED (0x02) ++ ++/*defines for IO Unit Page 7 PowerManagementCapabilities field */ ++#define MPI25_IOUNITPAGE7_PMCAP_DPA_FULL_PWR_MODE (0x00400000) ++#define MPI25_IOUNITPAGE7_PMCAP_DPA_REDUCED_PWR_MODE (0x00200000) ++#define MPI25_IOUNITPAGE7_PMCAP_DPA_STANDBY_MODE (0x00100000) ++#define MPI25_IOUNITPAGE7_PMCAP_HOST_FULL_PWR_MODE (0x00040000) ++#define MPI25_IOUNITPAGE7_PMCAP_HOST_REDUCED_PWR_MODE (0x00020000) ++#define MPI25_IOUNITPAGE7_PMCAP_HOST_STANDBY_MODE (0x00010000) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_FULL_PWR_MODE (0x00004000) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_REDUCED_PWR_MODE (0x00002000) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_STANDBY_MODE (0x00001000) ++#define MPI2_IOUNITPAGE7_PMCAP_HOST_12_5_PCT_IOCSPEED (0x00000400) ++#define MPI2_IOUNITPAGE7_PMCAP_HOST_25_0_PCT_IOCSPEED (0x00000200) ++#define MPI2_IOUNITPAGE7_PMCAP_HOST_50_0_PCT_IOCSPEED (0x00000100) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_12_5_PCT_IOCSPEED (0x00000040) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_25_0_PCT_IOCSPEED (0x00000020) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_50_0_PCT_IOCSPEED (0x00000010) ++#define MPI2_IOUNITPAGE7_PMCAP_HOST_WIDTH_CHANGE_PCIE (0x00000008) ++#define MPI2_IOUNITPAGE7_PMCAP_HOST_SPEED_CHANGE_PCIE (0x00000004) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_WIDTH_CHANGE_PCIE (0x00000002) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_SPEED_CHANGE_PCIE (0x00000001) ++ ++/*obsolete names for the PowerManagementCapabilities bits (above) */ ++#define MPI2_IOUNITPAGE7_PMCAP_12_5_PCT_IOCSPEED (0x00000400) ++#define MPI2_IOUNITPAGE7_PMCAP_25_0_PCT_IOCSPEED (0x00000200) ++#define MPI2_IOUNITPAGE7_PMCAP_50_0_PCT_IOCSPEED (0x00000100) ++#define MPI2_IOUNITPAGE7_PMCAP_PCIE_WIDTH_CHANGE (0x00000008) /*obsolete */ ++#define MPI2_IOUNITPAGE7_PMCAP_PCIE_SPEED_CHANGE (0x00000004) /*obsolete */ ++ ++ ++/*defines for IO Unit Page 7 IOCTemperatureUnits field */ ++#define MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT (0x00) ++#define MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT (0x01) ++#define MPI2_IOUNITPAGE7_IOC_TEMP_CELSIUS (0x02) ++ ++/*defines for IO Unit Page 7 IOCSpeed field */ ++#define MPI2_IOUNITPAGE7_IOC_SPEED_FULL (0x01) ++#define MPI2_IOUNITPAGE7_IOC_SPEED_HALF (0x02) ++#define MPI2_IOUNITPAGE7_IOC_SPEED_QUARTER (0x04) ++#define MPI2_IOUNITPAGE7_IOC_SPEED_EIGHTH (0x08) ++ ++/*defines for IO Unit Page 7 BoardTemperatureUnits field */ ++#define MPI2_IOUNITPAGE7_BOARD_TEMP_NOT_PRESENT (0x00) ++#define MPI2_IOUNITPAGE7_BOARD_TEMP_FAHRENHEIT (0x01) ++#define MPI2_IOUNITPAGE7_BOARD_TEMP_CELSIUS (0x02) ++ ++/* defines for IO Unit Page 7 Flags field */ ++#define MPI2_IOUNITPAGE7_FLAG_CABLE_POWER_EXC (0x01) ++ ++/*IO Unit Page 8 */ ++ ++#define MPI2_IOUNIT8_NUM_THRESHOLDS (4) ++ ++typedef struct _MPI2_IOUNIT8_SENSOR { ++ U16 Flags; /*0x00 */ ++ U16 Reserved1; /*0x02 */ ++ U16 ++ Threshold[MPI2_IOUNIT8_NUM_THRESHOLDS]; /*0x04 */ ++ U32 Reserved2; /*0x0C */ ++ U32 Reserved3; /*0x10 */ ++ U32 Reserved4; /*0x14 */ ++} MPI2_IOUNIT8_SENSOR, *PTR_MPI2_IOUNIT8_SENSOR, ++ Mpi2IOUnit8Sensor_t, *pMpi2IOUnit8Sensor_t; ++ ++/*defines for IO Unit Page 8 Sensor Flags field */ ++#define MPI2_IOUNIT8_SENSOR_FLAGS_T3_ENABLE (0x0008) ++#define MPI2_IOUNIT8_SENSOR_FLAGS_T2_ENABLE (0x0004) ++#define MPI2_IOUNIT8_SENSOR_FLAGS_T1_ENABLE (0x0002) ++#define MPI2_IOUNIT8_SENSOR_FLAGS_T0_ENABLE (0x0001) ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumSensors at runtime. ++ */ ++#ifndef MPI2_IOUNITPAGE8_SENSOR_ENTRIES ++#define MPI2_IOUNITPAGE8_SENSOR_ENTRIES (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_8 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 Reserved2; /*0x08 */ ++ U8 NumSensors; /*0x0C */ ++ U8 PollingInterval; /*0x0D */ ++ U16 Reserved3; /*0x0E */ ++ MPI2_IOUNIT8_SENSOR ++ Sensor[MPI2_IOUNITPAGE8_SENSOR_ENTRIES];/*0x10 */ ++} MPI2_CONFIG_PAGE_IO_UNIT_8, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_8, ++ Mpi2IOUnitPage8_t, *pMpi2IOUnitPage8_t; ++ ++#define MPI2_IOUNITPAGE8_PAGEVERSION (0x00) ++ ++ ++/*IO Unit Page 9 */ ++ ++typedef struct _MPI2_IOUNIT9_SENSOR { ++ U16 CurrentTemperature; /*0x00 */ ++ U16 Reserved1; /*0x02 */ ++ U8 Flags; /*0x04 */ ++ U8 Reserved2; /*0x05 */ ++ U16 Reserved3; /*0x06 */ ++ U32 Reserved4; /*0x08 */ ++ U32 Reserved5; /*0x0C */ ++} MPI2_IOUNIT9_SENSOR, *PTR_MPI2_IOUNIT9_SENSOR, ++ Mpi2IOUnit9Sensor_t, *pMpi2IOUnit9Sensor_t; ++ ++/*defines for IO Unit Page 9 Sensor Flags field */ ++#define MPI2_IOUNIT9_SENSOR_FLAGS_TEMP_VALID (0x01) ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumSensors at runtime. ++ */ ++#ifndef MPI2_IOUNITPAGE9_SENSOR_ENTRIES ++#define MPI2_IOUNITPAGE9_SENSOR_ENTRIES (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_9 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 Reserved2; /*0x08 */ ++ U8 NumSensors; /*0x0C */ ++ U8 Reserved4; /*0x0D */ ++ U16 Reserved3; /*0x0E */ ++ MPI2_IOUNIT9_SENSOR ++ Sensor[MPI2_IOUNITPAGE9_SENSOR_ENTRIES];/*0x10 */ ++} MPI2_CONFIG_PAGE_IO_UNIT_9, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_9, ++ Mpi2IOUnitPage9_t, *pMpi2IOUnitPage9_t; ++ ++#define MPI2_IOUNITPAGE9_PAGEVERSION (0x00) ++ ++ ++/*IO Unit Page 10 */ ++ ++typedef struct _MPI2_IOUNIT10_FUNCTION { ++ U8 CreditPercent; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++} MPI2_IOUNIT10_FUNCTION, ++ *PTR_MPI2_IOUNIT10_FUNCTION, ++ Mpi2IOUnit10Function_t, ++ *pMpi2IOUnit10Function_t; ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumFunctions at runtime. ++ */ ++#ifndef MPI2_IOUNITPAGE10_FUNCTION_ENTRIES ++#define MPI2_IOUNITPAGE10_FUNCTION_ENTRIES (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_10 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 NumFunctions; /*0x04 */ ++ U8 Reserved1; /*0x05 */ ++ U16 Reserved2; /*0x06 */ ++ U32 Reserved3; /*0x08 */ ++ U32 Reserved4; /*0x0C */ ++ MPI2_IOUNIT10_FUNCTION ++ Function[MPI2_IOUNITPAGE10_FUNCTION_ENTRIES];/*0x10 */ ++} MPI2_CONFIG_PAGE_IO_UNIT_10, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_10, ++ Mpi2IOUnitPage10_t, *pMpi2IOUnitPage10_t; ++ ++#define MPI2_IOUNITPAGE10_PAGEVERSION (0x01) ++ ++ ++/* IO Unit Page 11 (for MPI v2.6 and later) */ ++ ++typedef struct _MPI26_IOUNIT11_SPINUP_GROUP { ++ U8 MaxTargetSpinup; /* 0x00 */ ++ U8 SpinupDelay; /* 0x01 */ ++ U8 SpinupFlags; /* 0x02 */ ++ U8 Reserved1; /* 0x03 */ ++} MPI26_IOUNIT11_SPINUP_GROUP, ++ *PTR_MPI26_IOUNIT11_SPINUP_GROUP, ++ Mpi26IOUnit11SpinupGroup_t, ++ *pMpi26IOUnit11SpinupGroup_t; ++ ++/* defines for IO Unit Page 11 SpinupFlags */ ++#define MPI26_IOUNITPAGE11_SPINUP_DISABLE_FLAG (0x01) ++ ++ ++/* ++ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ * four and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI26_IOUNITPAGE11_PHY_MAX ++#define MPI26_IOUNITPAGE11_PHY_MAX (4) ++#endif ++ ++typedef struct _MPI26_CONFIG_PAGE_IO_UNIT_11 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ MPI26_IOUNIT11_SPINUP_GROUP SpinupGroupParameters[4]; /*0x08 */ ++ U32 Reserved2; /*0x18 */ ++ U32 Reserved3; /*0x1C */ ++ U32 Reserved4; /*0x20 */ ++ U8 BootDeviceWaitTime; /*0x24 */ ++ U8 Reserved5; /*0x25 */ ++ U16 Reserved6; /*0x26 */ ++ U8 NumPhys; /*0x28 */ ++ U8 PEInitialSpinupDelay; /*0x29 */ ++ U8 PEReplyDelay; /*0x2A */ ++ U8 Flags; /*0x2B */ ++ U8 PHY[MPI26_IOUNITPAGE11_PHY_MAX];/*0x2C */ ++} MPI26_CONFIG_PAGE_IO_UNIT_11, ++ *PTR_MPI26_CONFIG_PAGE_IO_UNIT_11, ++ Mpi26IOUnitPage11_t, ++ *pMpi26IOUnitPage11_t; ++ ++#define MPI26_IOUNITPAGE11_PAGEVERSION (0x00) ++ ++/* defines for Flags field */ ++#define MPI26_IOUNITPAGE11_FLAGS_AUTO_PORTENABLE (0x01) ++ ++/* defines for PHY field */ ++#define MPI26_IOUNITPAGE11_PHY_SPINUP_GROUP_MASK (0x03) ++ ++ ++ ++ ++ ++ ++/**************************************************************************** ++* IOC Config Pages ++****************************************************************************/ ++ ++/*IOC Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IOC_0 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 Reserved2; /*0x08 */ ++ U16 VendorID; /*0x0C */ ++ U16 DeviceID; /*0x0E */ ++ U8 RevisionID; /*0x10 */ ++ U8 Reserved3; /*0x11 */ ++ U16 Reserved4; /*0x12 */ ++ U32 ClassCode; /*0x14 */ ++ U16 SubsystemVendorID; /*0x18 */ ++ U16 SubsystemID; /*0x1A */ ++} MPI2_CONFIG_PAGE_IOC_0, ++ *PTR_MPI2_CONFIG_PAGE_IOC_0, ++ Mpi2IOCPage0_t, *pMpi2IOCPage0_t; ++ ++#define MPI2_IOCPAGE0_PAGEVERSION (0x02) ++ ++ ++/*IOC Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IOC_1 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Flags; /*0x04 */ ++ U32 CoalescingTimeout; /*0x08 */ ++ U8 CoalescingDepth; /*0x0C */ ++ U8 PCISlotNum; /*0x0D */ ++ U8 PCIBusNum; /*0x0E */ ++ U8 PCIDomainSegment; /*0x0F */ ++ U32 Reserved1; /*0x10 */ ++ U32 Reserved2; /*0x14 */ ++} MPI2_CONFIG_PAGE_IOC_1, ++ *PTR_MPI2_CONFIG_PAGE_IOC_1, ++ Mpi2IOCPage1_t, *pMpi2IOCPage1_t; ++ ++#define MPI2_IOCPAGE1_PAGEVERSION (0x05) ++ ++/*defines for IOC Page 1 Flags field */ ++#define MPI2_IOCPAGE1_REPLY_COALESCING (0x00000001) ++ ++#define MPI2_IOCPAGE1_PCISLOTNUM_UNKNOWN (0xFF) ++#define MPI2_IOCPAGE1_PCIBUSNUM_UNKNOWN (0xFF) ++#define MPI2_IOCPAGE1_PCIDOMAIN_UNKNOWN (0xFF) ++ ++/*IOC Page 6 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IOC_6 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 ++ CapabilitiesFlags; /*0x04 */ ++ U8 MaxDrivesRAID0; /*0x08 */ ++ U8 MaxDrivesRAID1; /*0x09 */ ++ U8 ++ MaxDrivesRAID1E; /*0x0A */ ++ U8 ++ MaxDrivesRAID10; /*0x0B */ ++ U8 MinDrivesRAID0; /*0x0C */ ++ U8 MinDrivesRAID1; /*0x0D */ ++ U8 ++ MinDrivesRAID1E; /*0x0E */ ++ U8 ++ MinDrivesRAID10; /*0x0F */ ++ U32 Reserved1; /*0x10 */ ++ U8 ++ MaxGlobalHotSpares; /*0x14 */ ++ U8 MaxPhysDisks; /*0x15 */ ++ U8 MaxVolumes; /*0x16 */ ++ U8 MaxConfigs; /*0x17 */ ++ U8 MaxOCEDisks; /*0x18 */ ++ U8 Reserved2; /*0x19 */ ++ U16 Reserved3; /*0x1A */ ++ U32 ++ SupportedStripeSizeMapRAID0; /*0x1C */ ++ U32 ++ SupportedStripeSizeMapRAID1E; /*0x20 */ ++ U32 ++ SupportedStripeSizeMapRAID10; /*0x24 */ ++ U32 Reserved4; /*0x28 */ ++ U32 Reserved5; /*0x2C */ ++ U16 ++ DefaultMetadataSize; /*0x30 */ ++ U16 Reserved6; /*0x32 */ ++ U16 ++ MaxBadBlockTableEntries; /*0x34 */ ++ U16 Reserved7; /*0x36 */ ++ U32 ++ IRNvsramVersion; /*0x38 */ ++} MPI2_CONFIG_PAGE_IOC_6, ++ *PTR_MPI2_CONFIG_PAGE_IOC_6, ++ Mpi2IOCPage6_t, *pMpi2IOCPage6_t; ++ ++#define MPI2_IOCPAGE6_PAGEVERSION (0x05) ++ ++/*defines for IOC Page 6 CapabilitiesFlags */ ++#define MPI2_IOCPAGE6_CAP_FLAGS_4K_SECTORS_SUPPORT (0x00000020) ++#define MPI2_IOCPAGE6_CAP_FLAGS_RAID10_SUPPORT (0x00000010) ++#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1_SUPPORT (0x00000008) ++#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1E_SUPPORT (0x00000004) ++#define MPI2_IOCPAGE6_CAP_FLAGS_RAID0_SUPPORT (0x00000002) ++#define MPI2_IOCPAGE6_CAP_FLAGS_GLOBAL_HOT_SPARE (0x00000001) ++ ++ ++/*IOC Page 7 */ ++ ++#define MPI2_IOCPAGE7_EVENTMASK_WORDS (4) ++ ++typedef struct _MPI2_CONFIG_PAGE_IOC_7 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 ++ EventMasks[MPI2_IOCPAGE7_EVENTMASK_WORDS];/*0x08 */ ++ U16 SASBroadcastPrimitiveMasks; /*0x18 */ ++ U16 SASNotifyPrimitiveMasks; /*0x1A */ ++ U32 Reserved3; /*0x1C */ ++} MPI2_CONFIG_PAGE_IOC_7, ++ *PTR_MPI2_CONFIG_PAGE_IOC_7, ++ Mpi2IOCPage7_t, *pMpi2IOCPage7_t; ++ ++#define MPI2_IOCPAGE7_PAGEVERSION (0x02) ++ ++ ++/*IOC Page 8 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IOC_8 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 NumDevsPerEnclosure; /*0x04 */ ++ U8 Reserved1; /*0x05 */ ++ U16 Reserved2; /*0x06 */ ++ U16 MaxPersistentEntries; /*0x08 */ ++ U16 MaxNumPhysicalMappedIDs; /*0x0A */ ++ U16 Flags; /*0x0C */ ++ U16 Reserved3; /*0x0E */ ++ U16 IRVolumeMappingFlags; /*0x10 */ ++ U16 Reserved4; /*0x12 */ ++ U32 Reserved5; /*0x14 */ ++} MPI2_CONFIG_PAGE_IOC_8, ++ *PTR_MPI2_CONFIG_PAGE_IOC_8, ++ Mpi2IOCPage8_t, *pMpi2IOCPage8_t; ++ ++#define MPI2_IOCPAGE8_PAGEVERSION (0x00) ++ ++/*defines for IOC Page 8 Flags field */ ++#define MPI2_IOCPAGE8_FLAGS_DA_START_SLOT_1 (0x00000020) ++#define MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0 (0x00000010) ++ ++#define MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE (0x0000000E) ++#define MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING (0x00000000) ++#define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING (0x00000002) ++ ++#define MPI2_IOCPAGE8_FLAGS_DISABLE_PERSISTENT_MAPPING (0x00000001) ++#define MPI2_IOCPAGE8_FLAGS_ENABLE_PERSISTENT_MAPPING (0x00000000) ++ ++/*defines for IOC Page 8 IRVolumeMappingFlags */ ++#define MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE (0x00000003) ++#define MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING (0x00000000) ++#define MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING (0x00000001) ++ ++ ++/**************************************************************************** ++* BIOS Config Pages ++****************************************************************************/ ++ ++/*BIOS Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_BIOS_1 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 BiosOptions; /*0x04 */ ++ U32 IOCSettings; /*0x08 */ ++ U8 SSUTimeout; /*0x0C */ ++ U8 Reserved1; /*0x0D */ ++ U16 Reserved2; /*0x0E */ ++ U32 DeviceSettings; /*0x10 */ ++ U16 NumberOfDevices; /*0x14 */ ++ U16 UEFIVersion; /*0x16 */ ++ U16 IOTimeoutBlockDevicesNonRM; /*0x18 */ ++ U16 IOTimeoutSequential; /*0x1A */ ++ U16 IOTimeoutOther; /*0x1C */ ++ U16 IOTimeoutBlockDevicesRM; /*0x1E */ ++} MPI2_CONFIG_PAGE_BIOS_1, ++ *PTR_MPI2_CONFIG_PAGE_BIOS_1, ++ Mpi2BiosPage1_t, *pMpi2BiosPage1_t; ++ ++#define MPI2_BIOSPAGE1_PAGEVERSION (0x07) ++ ++/*values for BIOS Page 1 BiosOptions field */ ++#define MPI2_BIOSPAGE1_OPTIONS_BOOT_LIST_ADD_ALT_BOOT_DEVICE (0x00008000) ++#define MPI2_BIOSPAGE1_OPTIONS_ADVANCED_CONFIG (0x00004000) ++ ++#define MPI2_BIOSPAGE1_OPTIONS_PNS_MASK (0x00003800) ++#define MPI2_BIOSPAGE1_OPTIONS_PNS_MASK (0x00003800) ++#define MPI2_BIOSPAGE1_OPTIONS_PNS_PBDHL (0x00000000) ++#define MPI2_BIOSPAGE1_OPTIONS_PNS_ENCSLOSURE (0x00000800) ++#define MPI2_BIOSPAGE1_OPTIONS_PNS_LWWID (0x00001000) ++#define MPI2_BIOSPAGE1_OPTIONS_PNS_PSENS (0x00001800) ++#define MPI2_BIOSPAGE1_OPTIONS_PNS_ESPHY (0x00002000) ++ ++#define MPI2_BIOSPAGE1_OPTIONS_X86_DISABLE_BIOS (0x00000400) ++ ++#define MPI2_BIOSPAGE1_OPTIONS_MASK_REGISTRATION_UEFI_BSD (0x00000300) ++#define MPI2_BIOSPAGE1_OPTIONS_USE_BIT0_REGISTRATION_UEFI_BSD (0x00000000) ++#define MPI2_BIOSPAGE1_OPTIONS_FULL_REGISTRATION_UEFI_BSD (0x00000100) ++#define MPI2_BIOSPAGE1_OPTIONS_ADAPTER_REGISTRATION_UEFI_BSD (0x00000200) ++#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_REGISTRATION_UEFI_BSD (0x00000300) ++ ++#define MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID (0x000000F0) ++#define MPI2_BIOSPAGE1_OPTIONS_LSI_OEM_ID (0x00000000) ++ ++#define MPI2_BIOSPAGE1_OPTIONS_MASK_UEFI_HII_REGISTRATION (0x00000006) ++#define MPI2_BIOSPAGE1_OPTIONS_ENABLE_UEFI_HII (0x00000000) ++#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_UEFI_HII (0x00000002) ++#define MPI2_BIOSPAGE1_OPTIONS_VERSION_CHECK_UEFI_HII (0x00000004) ++ ++#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_BIOS (0x00000001) ++ ++/*values for BIOS Page 1 IOCSettings field */ ++#define MPI2_BIOSPAGE1_IOCSET_MASK_BOOT_PREFERENCE (0x00030000) ++#define MPI2_BIOSPAGE1_IOCSET_ENCLOSURE_SLOT_BOOT (0x00000000) ++#define MPI2_BIOSPAGE1_IOCSET_SAS_ADDRESS_BOOT (0x00010000) ++ ++#define MPI2_BIOSPAGE1_IOCSET_MASK_RM_SETTING (0x000000C0) ++#define MPI2_BIOSPAGE1_IOCSET_NONE_RM_SETTING (0x00000000) ++#define MPI2_BIOSPAGE1_IOCSET_BOOT_RM_SETTING (0x00000040) ++#define MPI2_BIOSPAGE1_IOCSET_MEDIA_RM_SETTING (0x00000080) ++ ++#define MPI2_BIOSPAGE1_IOCSET_MASK_ADAPTER_SUPPORT (0x00000030) ++#define MPI2_BIOSPAGE1_IOCSET_NO_SUPPORT (0x00000000) ++#define MPI2_BIOSPAGE1_IOCSET_BIOS_SUPPORT (0x00000010) ++#define MPI2_BIOSPAGE1_IOCSET_OS_SUPPORT (0x00000020) ++#define MPI2_BIOSPAGE1_IOCSET_ALL_SUPPORT (0x00000030) ++ ++#define MPI2_BIOSPAGE1_IOCSET_ALTERNATE_CHS (0x00000008) ++ ++/*values for BIOS Page 1 DeviceSettings field */ ++#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SMART_POLLING (0x00000010) ++#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SEQ_LUN (0x00000008) ++#define MPI2_BIOSPAGE1_DEVSET_DISABLE_RM_LUN (0x00000004) ++#define MPI2_BIOSPAGE1_DEVSET_DISABLE_NON_RM_LUN (0x00000002) ++#define MPI2_BIOSPAGE1_DEVSET_DISABLE_OTHER_LUN (0x00000001) ++ ++/*defines for BIOS Page 1 UEFIVersion field */ ++#define MPI2_BIOSPAGE1_UEFI_VER_MAJOR_MASK (0xFF00) ++#define MPI2_BIOSPAGE1_UEFI_VER_MAJOR_SHIFT (8) ++#define MPI2_BIOSPAGE1_UEFI_VER_MINOR_MASK (0x00FF) ++#define MPI2_BIOSPAGE1_UEFI_VER_MINOR_SHIFT (0) ++ ++ ++ ++/*BIOS Page 2 */ ++ ++typedef struct _MPI2_BOOT_DEVICE_ADAPTER_ORDER { ++ U32 Reserved1; /*0x00 */ ++ U32 Reserved2; /*0x04 */ ++ U32 Reserved3; /*0x08 */ ++ U32 Reserved4; /*0x0C */ ++ U32 Reserved5; /*0x10 */ ++ U32 Reserved6; /*0x14 */ ++} MPI2_BOOT_DEVICE_ADAPTER_ORDER, ++ *PTR_MPI2_BOOT_DEVICE_ADAPTER_ORDER, ++ Mpi2BootDeviceAdapterOrder_t, ++ *pMpi2BootDeviceAdapterOrder_t; ++ ++typedef struct _MPI2_BOOT_DEVICE_SAS_WWID { ++ U64 SASAddress; /*0x00 */ ++ U8 LUN[8]; /*0x08 */ ++ U32 Reserved1; /*0x10 */ ++ U32 Reserved2; /*0x14 */ ++} MPI2_BOOT_DEVICE_SAS_WWID, ++ *PTR_MPI2_BOOT_DEVICE_SAS_WWID, ++ Mpi2BootDeviceSasWwid_t, ++ *pMpi2BootDeviceSasWwid_t; ++ ++typedef struct _MPI2_BOOT_DEVICE_ENCLOSURE_SLOT { ++ U64 EnclosureLogicalID; /*0x00 */ ++ U32 Reserved1; /*0x08 */ ++ U32 Reserved2; /*0x0C */ ++ U16 SlotNumber; /*0x10 */ ++ U16 Reserved3; /*0x12 */ ++ U32 Reserved4; /*0x14 */ ++} MPI2_BOOT_DEVICE_ENCLOSURE_SLOT, ++ *PTR_MPI2_BOOT_DEVICE_ENCLOSURE_SLOT, ++ Mpi2BootDeviceEnclosureSlot_t, ++ *pMpi2BootDeviceEnclosureSlot_t; ++ ++typedef struct _MPI2_BOOT_DEVICE_DEVICE_NAME { ++ U64 DeviceName; /*0x00 */ ++ U8 LUN[8]; /*0x08 */ ++ U32 Reserved1; /*0x10 */ ++ U32 Reserved2; /*0x14 */ ++} MPI2_BOOT_DEVICE_DEVICE_NAME, ++ *PTR_MPI2_BOOT_DEVICE_DEVICE_NAME, ++ Mpi2BootDeviceDeviceName_t, ++ *pMpi2BootDeviceDeviceName_t; ++ ++typedef union _MPI2_MPI2_BIOSPAGE2_BOOT_DEVICE { ++ MPI2_BOOT_DEVICE_ADAPTER_ORDER AdapterOrder; ++ MPI2_BOOT_DEVICE_SAS_WWID SasWwid; ++ MPI2_BOOT_DEVICE_ENCLOSURE_SLOT EnclosureSlot; ++ MPI2_BOOT_DEVICE_DEVICE_NAME DeviceName; ++} MPI2_BIOSPAGE2_BOOT_DEVICE, ++ *PTR_MPI2_BIOSPAGE2_BOOT_DEVICE, ++ Mpi2BiosPage2BootDevice_t, ++ *pMpi2BiosPage2BootDevice_t; ++ ++typedef struct _MPI2_CONFIG_PAGE_BIOS_2 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 Reserved2; /*0x08 */ ++ U32 Reserved3; /*0x0C */ ++ U32 Reserved4; /*0x10 */ ++ U32 Reserved5; /*0x14 */ ++ U32 Reserved6; /*0x18 */ ++ U8 ReqBootDeviceForm; /*0x1C */ ++ U8 Reserved7; /*0x1D */ ++ U16 Reserved8; /*0x1E */ ++ MPI2_BIOSPAGE2_BOOT_DEVICE RequestedBootDevice; /*0x20 */ ++ U8 ReqAltBootDeviceForm; /*0x38 */ ++ U8 Reserved9; /*0x39 */ ++ U16 Reserved10; /*0x3A */ ++ MPI2_BIOSPAGE2_BOOT_DEVICE RequestedAltBootDevice; /*0x3C */ ++ U8 CurrentBootDeviceForm; /*0x58 */ ++ U8 Reserved11; /*0x59 */ ++ U16 Reserved12; /*0x5A */ ++ MPI2_BIOSPAGE2_BOOT_DEVICE CurrentBootDevice; /*0x58 */ ++} MPI2_CONFIG_PAGE_BIOS_2, *PTR_MPI2_CONFIG_PAGE_BIOS_2, ++ Mpi2BiosPage2_t, *pMpi2BiosPage2_t; ++ ++#define MPI2_BIOSPAGE2_PAGEVERSION (0x04) ++ ++/*values for BIOS Page 2 BootDeviceForm fields */ ++#define MPI2_BIOSPAGE2_FORM_MASK (0x0F) ++#define MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED (0x00) ++#define MPI2_BIOSPAGE2_FORM_SAS_WWID (0x05) ++#define MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT (0x06) ++#define MPI2_BIOSPAGE2_FORM_DEVICE_NAME (0x07) ++ ++ ++/*BIOS Page 3 */ ++ ++#define MPI2_BIOSPAGE3_NUM_ADAPTER (4) ++ ++typedef struct _MPI2_ADAPTER_INFO { ++ U8 PciBusNumber; /*0x00 */ ++ U8 PciDeviceAndFunctionNumber; /*0x01 */ ++ U16 AdapterFlags; /*0x02 */ ++} MPI2_ADAPTER_INFO, *PTR_MPI2_ADAPTER_INFO, ++ Mpi2AdapterInfo_t, *pMpi2AdapterInfo_t; ++ ++#define MPI2_ADAPTER_INFO_FLAGS_EMBEDDED (0x0001) ++#define MPI2_ADAPTER_INFO_FLAGS_INIT_STATUS (0x0002) ++ ++typedef struct _MPI2_ADAPTER_ORDER_AUX { ++ U64 WWID; /* 0x00 */ ++ U32 Reserved1; /* 0x08 */ ++ U32 Reserved2; /* 0x0C */ ++} MPI2_ADAPTER_ORDER_AUX, *PTR_MPI2_ADAPTER_ORDER_AUX, ++ Mpi2AdapterOrderAux_t, *pMpi2AdapterOrderAux_t; ++ ++ ++typedef struct _MPI2_CONFIG_PAGE_BIOS_3 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 GlobalFlags; /*0x04 */ ++ U32 BiosVersion; /*0x08 */ ++ MPI2_ADAPTER_INFO AdapterOrder[MPI2_BIOSPAGE3_NUM_ADAPTER]; ++ U32 Reserved1; /*0x1C */ ++ MPI2_ADAPTER_ORDER_AUX AdapterOrderAux[MPI2_BIOSPAGE3_NUM_ADAPTER]; ++} MPI2_CONFIG_PAGE_BIOS_3, ++ *PTR_MPI2_CONFIG_PAGE_BIOS_3, ++ Mpi2BiosPage3_t, *pMpi2BiosPage3_t; ++ ++#define MPI2_BIOSPAGE3_PAGEVERSION (0x01) ++ ++/*values for BIOS Page 3 GlobalFlags */ ++#define MPI2_BIOSPAGE3_FLAGS_PAUSE_ON_ERROR (0x00000002) ++#define MPI2_BIOSPAGE3_FLAGS_VERBOSE_ENABLE (0x00000004) ++#define MPI2_BIOSPAGE3_FLAGS_HOOK_INT_40_DISABLE (0x00000010) ++ ++#define MPI2_BIOSPAGE3_FLAGS_DEV_LIST_DISPLAY_MASK (0x000000E0) ++#define MPI2_BIOSPAGE3_FLAGS_INSTALLED_DEV_DISPLAY (0x00000000) ++#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DISPLAY (0x00000020) ++#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DEV_DISPLAY (0x00000040) ++ ++ ++/*BIOS Page 4 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI2_BIOS_PAGE_4_PHY_ENTRIES ++#define MPI2_BIOS_PAGE_4_PHY_ENTRIES (1) ++#endif ++ ++typedef struct _MPI2_BIOS4_ENTRY { ++ U64 ReassignmentWWID; /*0x00 */ ++ U64 ReassignmentDeviceName; /*0x08 */ ++} MPI2_BIOS4_ENTRY, *PTR_MPI2_BIOS4_ENTRY, ++ Mpi2MBios4Entry_t, *pMpi2Bios4Entry_t; ++ ++typedef struct _MPI2_CONFIG_PAGE_BIOS_4 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 NumPhys; /*0x04 */ ++ U8 Reserved1; /*0x05 */ ++ U16 Reserved2; /*0x06 */ ++ MPI2_BIOS4_ENTRY ++ Phy[MPI2_BIOS_PAGE_4_PHY_ENTRIES]; /*0x08 */ ++} MPI2_CONFIG_PAGE_BIOS_4, *PTR_MPI2_CONFIG_PAGE_BIOS_4, ++ Mpi2BiosPage4_t, *pMpi2BiosPage4_t; ++ ++#define MPI2_BIOSPAGE4_PAGEVERSION (0x01) ++ ++ ++/**************************************************************************** ++* RAID Volume Config Pages ++****************************************************************************/ ++ ++/*RAID Volume Page 0 */ ++ ++typedef struct _MPI2_RAIDVOL0_PHYS_DISK { ++ U8 RAIDSetNum; /*0x00 */ ++ U8 PhysDiskMap; /*0x01 */ ++ U8 PhysDiskNum; /*0x02 */ ++ U8 Reserved; /*0x03 */ ++} MPI2_RAIDVOL0_PHYS_DISK, *PTR_MPI2_RAIDVOL0_PHYS_DISK, ++ Mpi2RaidVol0PhysDisk_t, *pMpi2RaidVol0PhysDisk_t; ++ ++/*defines for the PhysDiskMap field */ ++#define MPI2_RAIDVOL0_PHYSDISK_PRIMARY (0x01) ++#define MPI2_RAIDVOL0_PHYSDISK_SECONDARY (0x02) ++ ++typedef struct _MPI2_RAIDVOL0_SETTINGS { ++ U16 Settings; /*0x00 */ ++ U8 HotSparePool; /*0x01 */ ++ U8 Reserved; /*0x02 */ ++} MPI2_RAIDVOL0_SETTINGS, *PTR_MPI2_RAIDVOL0_SETTINGS, ++ Mpi2RaidVol0Settings_t, ++ *pMpi2RaidVol0Settings_t; ++ ++/*RAID Volume Page 0 HotSparePool defines, also used in RAID Physical Disk */ ++#define MPI2_RAID_HOT_SPARE_POOL_0 (0x01) ++#define MPI2_RAID_HOT_SPARE_POOL_1 (0x02) ++#define MPI2_RAID_HOT_SPARE_POOL_2 (0x04) ++#define MPI2_RAID_HOT_SPARE_POOL_3 (0x08) ++#define MPI2_RAID_HOT_SPARE_POOL_4 (0x10) ++#define MPI2_RAID_HOT_SPARE_POOL_5 (0x20) ++#define MPI2_RAID_HOT_SPARE_POOL_6 (0x40) ++#define MPI2_RAID_HOT_SPARE_POOL_7 (0x80) ++ ++/*RAID Volume Page 0 VolumeSettings defines */ ++#define MPI2_RAIDVOL0_SETTING_USE_PRODUCT_ID_SUFFIX (0x0008) ++#define MPI2_RAIDVOL0_SETTING_AUTO_CONFIG_HSWAP_DISABLE (0x0004) ++ ++#define MPI2_RAIDVOL0_SETTING_MASK_WRITE_CACHING (0x0003) ++#define MPI2_RAIDVOL0_SETTING_UNCHANGED (0x0000) ++#define MPI2_RAIDVOL0_SETTING_DISABLE_WRITE_CACHING (0x0001) ++#define MPI2_RAIDVOL0_SETTING_ENABLE_WRITE_CACHING (0x0002) ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhysDisks at runtime. ++ */ ++#ifndef MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX ++#define MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_0 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U16 DevHandle; /*0x04 */ ++ U8 VolumeState; /*0x06 */ ++ U8 VolumeType; /*0x07 */ ++ U32 VolumeStatusFlags; /*0x08 */ ++ MPI2_RAIDVOL0_SETTINGS VolumeSettings; /*0x0C */ ++ U64 MaxLBA; /*0x10 */ ++ U32 StripeSize; /*0x18 */ ++ U16 BlockSize; /*0x1C */ ++ U16 Reserved1; /*0x1E */ ++ U8 SupportedPhysDisks;/*0x20 */ ++ U8 ResyncRate; /*0x21 */ ++ U16 DataScrubDuration; /*0x22 */ ++ U8 NumPhysDisks; /*0x24 */ ++ U8 Reserved2; /*0x25 */ ++ U8 Reserved3; /*0x26 */ ++ U8 InactiveStatus; /*0x27 */ ++ MPI2_RAIDVOL0_PHYS_DISK ++ PhysDisk[MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX]; /*0x28 */ ++} MPI2_CONFIG_PAGE_RAID_VOL_0, ++ *PTR_MPI2_CONFIG_PAGE_RAID_VOL_0, ++ Mpi2RaidVolPage0_t, *pMpi2RaidVolPage0_t; ++ ++#define MPI2_RAIDVOLPAGE0_PAGEVERSION (0x0A) ++ ++/*values for RAID VolumeState */ ++#define MPI2_RAID_VOL_STATE_MISSING (0x00) ++#define MPI2_RAID_VOL_STATE_FAILED (0x01) ++#define MPI2_RAID_VOL_STATE_INITIALIZING (0x02) ++#define MPI2_RAID_VOL_STATE_ONLINE (0x03) ++#define MPI2_RAID_VOL_STATE_DEGRADED (0x04) ++#define MPI2_RAID_VOL_STATE_OPTIMAL (0x05) ++ ++/*values for RAID VolumeType */ ++#define MPI2_RAID_VOL_TYPE_RAID0 (0x00) ++#define MPI2_RAID_VOL_TYPE_RAID1E (0x01) ++#define MPI2_RAID_VOL_TYPE_RAID1 (0x02) ++#define MPI2_RAID_VOL_TYPE_RAID10 (0x05) ++#define MPI2_RAID_VOL_TYPE_UNKNOWN (0xFF) ++ ++/*values for RAID Volume Page 0 VolumeStatusFlags field */ ++#define MPI2_RAIDVOL0_STATUS_FLAG_PENDING_RESYNC (0x02000000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_BACKG_INIT_PENDING (0x01000000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_MDC_PENDING (0x00800000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_USER_CONSIST_PENDING (0x00400000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_MAKE_DATA_CONSISTENT (0x00200000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_DATA_SCRUB (0x00100000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_CONSISTENCY_CHECK (0x00080000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_CAPACITY_EXPANSION (0x00040000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_BACKGROUND_INIT (0x00020000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS (0x00010000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT (0x00000080) ++#define MPI2_RAIDVOL0_STATUS_FLAG_OCE_ALLOWED (0x00000040) ++#define MPI2_RAIDVOL0_STATUS_FLAG_BGI_COMPLETE (0x00000020) ++#define MPI2_RAIDVOL0_STATUS_FLAG_1E_OFFSET_MIRROR (0x00000000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_1E_ADJACENT_MIRROR (0x00000010) ++#define MPI2_RAIDVOL0_STATUS_FLAG_BAD_BLOCK_TABLE_FULL (0x00000008) ++#define MPI2_RAIDVOL0_STATUS_FLAG_VOLUME_INACTIVE (0x00000004) ++#define MPI2_RAIDVOL0_STATUS_FLAG_QUIESCED (0x00000002) ++#define MPI2_RAIDVOL0_STATUS_FLAG_ENABLED (0x00000001) ++ ++/*values for RAID Volume Page 0 SupportedPhysDisks field */ ++#define MPI2_RAIDVOL0_SUPPORT_SOLID_STATE_DISKS (0x08) ++#define MPI2_RAIDVOL0_SUPPORT_HARD_DISKS (0x04) ++#define MPI2_RAIDVOL0_SUPPORT_SAS_PROTOCOL (0x02) ++#define MPI2_RAIDVOL0_SUPPORT_SATA_PROTOCOL (0x01) ++ ++/*values for RAID Volume Page 0 InactiveStatus field */ ++#define MPI2_RAIDVOLPAGE0_UNKNOWN_INACTIVE (0x00) ++#define MPI2_RAIDVOLPAGE0_STALE_METADATA_INACTIVE (0x01) ++#define MPI2_RAIDVOLPAGE0_FOREIGN_VOLUME_INACTIVE (0x02) ++#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_RESOURCE_INACTIVE (0x03) ++#define MPI2_RAIDVOLPAGE0_CLONE_VOLUME_INACTIVE (0x04) ++#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_METADATA_INACTIVE (0x05) ++#define MPI2_RAIDVOLPAGE0_PREVIOUSLY_DELETED (0x06) ++ ++ ++/*RAID Volume Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_1 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U16 DevHandle; /*0x04 */ ++ U16 Reserved0; /*0x06 */ ++ U8 GUID[24]; /*0x08 */ ++ U8 Name[16]; /*0x20 */ ++ U64 WWID; /*0x30 */ ++ U32 Reserved1; /*0x38 */ ++ U32 Reserved2; /*0x3C */ ++} MPI2_CONFIG_PAGE_RAID_VOL_1, ++ *PTR_MPI2_CONFIG_PAGE_RAID_VOL_1, ++ Mpi2RaidVolPage1_t, *pMpi2RaidVolPage1_t; ++ ++#define MPI2_RAIDVOLPAGE1_PAGEVERSION (0x03) ++ ++ ++/**************************************************************************** ++* RAID Physical Disk Config Pages ++****************************************************************************/ ++ ++/*RAID Physical Disk Page 0 */ ++ ++typedef struct _MPI2_RAIDPHYSDISK0_SETTINGS { ++ U16 Reserved1; /*0x00 */ ++ U8 HotSparePool; /*0x02 */ ++ U8 Reserved2; /*0x03 */ ++} MPI2_RAIDPHYSDISK0_SETTINGS, ++ *PTR_MPI2_RAIDPHYSDISK0_SETTINGS, ++ Mpi2RaidPhysDisk0Settings_t, ++ *pMpi2RaidPhysDisk0Settings_t; ++ ++/*use MPI2_RAID_HOT_SPARE_POOL_ defines for the HotSparePool field */ ++ ++typedef struct _MPI2_RAIDPHYSDISK0_INQUIRY_DATA { ++ U8 VendorID[8]; /*0x00 */ ++ U8 ProductID[16]; /*0x08 */ ++ U8 ProductRevLevel[4]; /*0x18 */ ++ U8 SerialNum[32]; /*0x1C */ ++} MPI2_RAIDPHYSDISK0_INQUIRY_DATA, ++ *PTR_MPI2_RAIDPHYSDISK0_INQUIRY_DATA, ++ Mpi2RaidPhysDisk0InquiryData_t, ++ *pMpi2RaidPhysDisk0InquiryData_t; ++ ++typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_0 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U16 DevHandle; /*0x04 */ ++ U8 Reserved1; /*0x06 */ ++ U8 PhysDiskNum; /*0x07 */ ++ MPI2_RAIDPHYSDISK0_SETTINGS PhysDiskSettings; /*0x08 */ ++ U32 Reserved2; /*0x0C */ ++ MPI2_RAIDPHYSDISK0_INQUIRY_DATA InquiryData; /*0x10 */ ++ U32 Reserved3; /*0x4C */ ++ U8 PhysDiskState; /*0x50 */ ++ U8 OfflineReason; /*0x51 */ ++ U8 IncompatibleReason; /*0x52 */ ++ U8 PhysDiskAttributes; /*0x53 */ ++ U32 PhysDiskStatusFlags;/*0x54 */ ++ U64 DeviceMaxLBA; /*0x58 */ ++ U64 HostMaxLBA; /*0x60 */ ++ U64 CoercedMaxLBA; /*0x68 */ ++ U16 BlockSize; /*0x70 */ ++ U16 Reserved5; /*0x72 */ ++ U32 Reserved6; /*0x74 */ ++} MPI2_CONFIG_PAGE_RD_PDISK_0, ++ *PTR_MPI2_CONFIG_PAGE_RD_PDISK_0, ++ Mpi2RaidPhysDiskPage0_t, ++ *pMpi2RaidPhysDiskPage0_t; ++ ++#define MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION (0x05) ++ ++/*PhysDiskState defines */ ++#define MPI2_RAID_PD_STATE_NOT_CONFIGURED (0x00) ++#define MPI2_RAID_PD_STATE_NOT_COMPATIBLE (0x01) ++#define MPI2_RAID_PD_STATE_OFFLINE (0x02) ++#define MPI2_RAID_PD_STATE_ONLINE (0x03) ++#define MPI2_RAID_PD_STATE_HOT_SPARE (0x04) ++#define MPI2_RAID_PD_STATE_DEGRADED (0x05) ++#define MPI2_RAID_PD_STATE_REBUILDING (0x06) ++#define MPI2_RAID_PD_STATE_OPTIMAL (0x07) ++ ++/*OfflineReason defines */ ++#define MPI2_PHYSDISK0_ONLINE (0x00) ++#define MPI2_PHYSDISK0_OFFLINE_MISSING (0x01) ++#define MPI2_PHYSDISK0_OFFLINE_FAILED (0x03) ++#define MPI2_PHYSDISK0_OFFLINE_INITIALIZING (0x04) ++#define MPI2_PHYSDISK0_OFFLINE_REQUESTED (0x05) ++#define MPI2_PHYSDISK0_OFFLINE_FAILED_REQUESTED (0x06) ++#define MPI2_PHYSDISK0_OFFLINE_OTHER (0xFF) ++ ++/*IncompatibleReason defines */ ++#define MPI2_PHYSDISK0_COMPATIBLE (0x00) ++#define MPI2_PHYSDISK0_INCOMPATIBLE_PROTOCOL (0x01) ++#define MPI2_PHYSDISK0_INCOMPATIBLE_BLOCKSIZE (0x02) ++#define MPI2_PHYSDISK0_INCOMPATIBLE_MAX_LBA (0x03) ++#define MPI2_PHYSDISK0_INCOMPATIBLE_SATA_EXTENDED_CMD (0x04) ++#define MPI2_PHYSDISK0_INCOMPATIBLE_REMOVEABLE_MEDIA (0x05) ++#define MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE (0x06) ++#define MPI2_PHYSDISK0_INCOMPATIBLE_UNKNOWN (0xFF) ++ ++/*PhysDiskAttributes defines */ ++#define MPI2_PHYSDISK0_ATTRIB_MEDIA_MASK (0x0C) ++#define MPI2_PHYSDISK0_ATTRIB_SOLID_STATE_DRIVE (0x08) ++#define MPI2_PHYSDISK0_ATTRIB_HARD_DISK_DRIVE (0x04) ++ ++#define MPI2_PHYSDISK0_ATTRIB_PROTOCOL_MASK (0x03) ++#define MPI2_PHYSDISK0_ATTRIB_SAS_PROTOCOL (0x02) ++#define MPI2_PHYSDISK0_ATTRIB_SATA_PROTOCOL (0x01) ++ ++/*PhysDiskStatusFlags defines */ ++#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED (0x00000040) ++#define MPI2_PHYSDISK0_STATUS_FLAG_OCE_TARGET (0x00000020) ++#define MPI2_PHYSDISK0_STATUS_FLAG_WRITE_CACHE_ENABLED (0x00000010) ++#define MPI2_PHYSDISK0_STATUS_FLAG_OPTIMAL_PREVIOUS (0x00000000) ++#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_OPTIMAL_PREVIOUS (0x00000008) ++#define MPI2_PHYSDISK0_STATUS_FLAG_INACTIVE_VOLUME (0x00000004) ++#define MPI2_PHYSDISK0_STATUS_FLAG_QUIESCED (0x00000002) ++#define MPI2_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC (0x00000001) ++ ++ ++/*RAID Physical Disk Page 1 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhysDiskPaths at runtime. ++ */ ++#ifndef MPI2_RAID_PHYS_DISK1_PATH_MAX ++#define MPI2_RAID_PHYS_DISK1_PATH_MAX (1) ++#endif ++ ++typedef struct _MPI2_RAIDPHYSDISK1_PATH { ++ U16 DevHandle; /*0x00 */ ++ U16 Reserved1; /*0x02 */ ++ U64 WWID; /*0x04 */ ++ U64 OwnerWWID; /*0x0C */ ++ U8 OwnerIdentifier; /*0x14 */ ++ U8 Reserved2; /*0x15 */ ++ U16 Flags; /*0x16 */ ++} MPI2_RAIDPHYSDISK1_PATH, *PTR_MPI2_RAIDPHYSDISK1_PATH, ++ Mpi2RaidPhysDisk1Path_t, ++ *pMpi2RaidPhysDisk1Path_t; ++ ++/*RAID Physical Disk Page 1 Physical Disk Path Flags field defines */ ++#define MPI2_RAID_PHYSDISK1_FLAG_PRIMARY (0x0004) ++#define MPI2_RAID_PHYSDISK1_FLAG_BROKEN (0x0002) ++#define MPI2_RAID_PHYSDISK1_FLAG_INVALID (0x0001) ++ ++typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 NumPhysDiskPaths; /*0x04 */ ++ U8 PhysDiskNum; /*0x05 */ ++ U16 Reserved1; /*0x06 */ ++ U32 Reserved2; /*0x08 */ ++ MPI2_RAIDPHYSDISK1_PATH ++ PhysicalDiskPath[MPI2_RAID_PHYS_DISK1_PATH_MAX];/*0x0C */ ++} MPI2_CONFIG_PAGE_RD_PDISK_1, ++ *PTR_MPI2_CONFIG_PAGE_RD_PDISK_1, ++ Mpi2RaidPhysDiskPage1_t, ++ *pMpi2RaidPhysDiskPage1_t; ++ ++#define MPI2_RAIDPHYSDISKPAGE1_PAGEVERSION (0x02) ++ ++ ++/**************************************************************************** ++* values for fields used by several types of SAS Config Pages ++****************************************************************************/ ++ ++/*values for NegotiatedLinkRates fields */ ++#define MPI2_SAS_NEG_LINK_RATE_MASK_LOGICAL (0xF0) ++#define MPI2_SAS_NEG_LINK_RATE_SHIFT_LOGICAL (4) ++#define MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL (0x0F) ++/*link rates used for Negotiated Physical and Logical Link Rate */ ++#define MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE (0x00) ++#define MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED (0x01) ++#define MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED (0x02) ++#define MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE (0x03) ++#define MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR (0x04) ++#define MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS (0x05) ++#define MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY (0x06) ++#define MPI2_SAS_NEG_LINK_RATE_1_5 (0x08) ++#define MPI2_SAS_NEG_LINK_RATE_3_0 (0x09) ++#define MPI2_SAS_NEG_LINK_RATE_6_0 (0x0A) ++#define MPI25_SAS_NEG_LINK_RATE_12_0 (0x0B) ++ ++ ++/*values for AttachedPhyInfo fields */ ++#define MPI2_SAS_APHYINFO_INSIDE_ZPSDS_PERSISTENT (0x00000040) ++#define MPI2_SAS_APHYINFO_REQUESTED_INSIDE_ZPSDS (0x00000020) ++#define MPI2_SAS_APHYINFO_BREAK_REPLY_CAPABLE (0x00000010) ++ ++#define MPI2_SAS_APHYINFO_REASON_MASK (0x0000000F) ++#define MPI2_SAS_APHYINFO_REASON_UNKNOWN (0x00000000) ++#define MPI2_SAS_APHYINFO_REASON_POWER_ON (0x00000001) ++#define MPI2_SAS_APHYINFO_REASON_HARD_RESET (0x00000002) ++#define MPI2_SAS_APHYINFO_REASON_SMP_PHY_CONTROL (0x00000003) ++#define MPI2_SAS_APHYINFO_REASON_LOSS_OF_SYNC (0x00000004) ++#define MPI2_SAS_APHYINFO_REASON_MULTIPLEXING_SEQ (0x00000005) ++#define MPI2_SAS_APHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00000006) ++#define MPI2_SAS_APHYINFO_REASON_BREAK_TIMEOUT (0x00000007) ++#define MPI2_SAS_APHYINFO_REASON_PHY_TEST_STOPPED (0x00000008) ++ ++ ++/*values for PhyInfo fields */ ++#define MPI2_SAS_PHYINFO_PHY_VACANT (0x80000000) ++ ++#define MPI2_SAS_PHYINFO_PHY_POWER_CONDITION_MASK (0x18000000) ++#define MPI2_SAS_PHYINFO_SHIFT_PHY_POWER_CONDITION (27) ++#define MPI2_SAS_PHYINFO_PHY_POWER_ACTIVE (0x00000000) ++#define MPI2_SAS_PHYINFO_PHY_POWER_PARTIAL (0x08000000) ++#define MPI2_SAS_PHYINFO_PHY_POWER_SLUMBER (0x10000000) ++ ++#define MPI2_SAS_PHYINFO_CHANGED_REQ_INSIDE_ZPSDS (0x04000000) ++#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS_PERSISTENT (0x02000000) ++#define MPI2_SAS_PHYINFO_REQ_INSIDE_ZPSDS (0x01000000) ++#define MPI2_SAS_PHYINFO_ZONE_GROUP_PERSISTENT (0x00400000) ++#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS (0x00200000) ++#define MPI2_SAS_PHYINFO_ZONING_ENABLED (0x00100000) ++ ++#define MPI2_SAS_PHYINFO_REASON_MASK (0x000F0000) ++#define MPI2_SAS_PHYINFO_REASON_UNKNOWN (0x00000000) ++#define MPI2_SAS_PHYINFO_REASON_POWER_ON (0x00010000) ++#define MPI2_SAS_PHYINFO_REASON_HARD_RESET (0x00020000) ++#define MPI2_SAS_PHYINFO_REASON_SMP_PHY_CONTROL (0x00030000) ++#define MPI2_SAS_PHYINFO_REASON_LOSS_OF_SYNC (0x00040000) ++#define MPI2_SAS_PHYINFO_REASON_MULTIPLEXING_SEQ (0x00050000) ++#define MPI2_SAS_PHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00060000) ++#define MPI2_SAS_PHYINFO_REASON_BREAK_TIMEOUT (0x00070000) ++#define MPI2_SAS_PHYINFO_REASON_PHY_TEST_STOPPED (0x00080000) ++ ++#define MPI2_SAS_PHYINFO_MULTIPLEXING_SUPPORTED (0x00008000) ++#define MPI2_SAS_PHYINFO_SATA_PORT_ACTIVE (0x00004000) ++#define MPI2_SAS_PHYINFO_SATA_PORT_SELECTOR_PRESENT (0x00002000) ++#define MPI2_SAS_PHYINFO_VIRTUAL_PHY (0x00001000) ++ ++#define MPI2_SAS_PHYINFO_MASK_PARTIAL_PATHWAY_TIME (0x00000F00) ++#define MPI2_SAS_PHYINFO_SHIFT_PARTIAL_PATHWAY_TIME (8) ++ ++#define MPI2_SAS_PHYINFO_MASK_ROUTING_ATTRIBUTE (0x000000F0) ++#define MPI2_SAS_PHYINFO_DIRECT_ROUTING (0x00000000) ++#define MPI2_SAS_PHYINFO_SUBTRACTIVE_ROUTING (0x00000010) ++#define MPI2_SAS_PHYINFO_TABLE_ROUTING (0x00000020) ++ ++ ++/*values for SAS ProgrammedLinkRate fields */ ++#define MPI2_SAS_PRATE_MAX_RATE_MASK (0xF0) ++#define MPI2_SAS_PRATE_MAX_RATE_NOT_PROGRAMMABLE (0x00) ++#define MPI2_SAS_PRATE_MAX_RATE_1_5 (0x80) ++#define MPI2_SAS_PRATE_MAX_RATE_3_0 (0x90) ++#define MPI2_SAS_PRATE_MAX_RATE_6_0 (0xA0) ++#define MPI25_SAS_PRATE_MAX_RATE_12_0 (0xB0) ++#define MPI2_SAS_PRATE_MIN_RATE_MASK (0x0F) ++#define MPI2_SAS_PRATE_MIN_RATE_NOT_PROGRAMMABLE (0x00) ++#define MPI2_SAS_PRATE_MIN_RATE_1_5 (0x08) ++#define MPI2_SAS_PRATE_MIN_RATE_3_0 (0x09) ++#define MPI2_SAS_PRATE_MIN_RATE_6_0 (0x0A) ++#define MPI25_SAS_PRATE_MIN_RATE_12_0 (0x0B) ++ ++ ++/*values for SAS HwLinkRate fields */ ++#define MPI2_SAS_HWRATE_MAX_RATE_MASK (0xF0) ++#define MPI2_SAS_HWRATE_MAX_RATE_1_5 (0x80) ++#define MPI2_SAS_HWRATE_MAX_RATE_3_0 (0x90) ++#define MPI2_SAS_HWRATE_MAX_RATE_6_0 (0xA0) ++#define MPI25_SAS_HWRATE_MAX_RATE_12_0 (0xB0) ++#define MPI2_SAS_HWRATE_MIN_RATE_MASK (0x0F) ++#define MPI2_SAS_HWRATE_MIN_RATE_1_5 (0x08) ++#define MPI2_SAS_HWRATE_MIN_RATE_3_0 (0x09) ++#define MPI2_SAS_HWRATE_MIN_RATE_6_0 (0x0A) ++#define MPI25_SAS_HWRATE_MIN_RATE_12_0 (0x0B) ++ ++ ++ ++/**************************************************************************** ++* SAS IO Unit Config Pages ++****************************************************************************/ ++ ++/*SAS IO Unit Page 0 */ ++ ++typedef struct _MPI2_SAS_IO_UNIT0_PHY_DATA { ++ U8 Port; /*0x00 */ ++ U8 PortFlags; /*0x01 */ ++ U8 PhyFlags; /*0x02 */ ++ U8 NegotiatedLinkRate; /*0x03 */ ++ U32 ControllerPhyDeviceInfo;/*0x04 */ ++ U16 AttachedDevHandle; /*0x08 */ ++ U16 ControllerDevHandle; /*0x0A */ ++ U32 DiscoveryStatus; /*0x0C */ ++ U32 Reserved; /*0x10 */ ++} MPI2_SAS_IO_UNIT0_PHY_DATA, ++ *PTR_MPI2_SAS_IO_UNIT0_PHY_DATA, ++ Mpi2SasIOUnit0PhyData_t, ++ *pMpi2SasIOUnit0PhyData_t; ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI2_SAS_IOUNIT0_PHY_MAX ++#define MPI2_SAS_IOUNIT0_PHY_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1;/*0x08 */ ++ U8 NumPhys; /*0x0C */ ++ U8 Reserved2;/*0x0D */ ++ U16 Reserved3;/*0x0E */ ++ MPI2_SAS_IO_UNIT0_PHY_DATA ++ PhyData[MPI2_SAS_IOUNIT0_PHY_MAX]; /*0x10 */ ++} MPI2_CONFIG_PAGE_SASIOUNIT_0, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_0, ++ Mpi2SasIOUnitPage0_t, *pMpi2SasIOUnitPage0_t; ++ ++#define MPI2_SASIOUNITPAGE0_PAGEVERSION (0x05) ++ ++/*values for SAS IO Unit Page 0 PortFlags */ ++#define MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS (0x08) ++#define MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG (0x01) ++ ++/*values for SAS IO Unit Page 0 PhyFlags */ ++#define MPI2_SASIOUNIT0_PHYFLAGS_INIT_PERSIST_CONNECT (0x40) ++#define MPI2_SASIOUNIT0_PHYFLAGS_TARG_PERSIST_CONNECT (0x20) ++#define MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED (0x10) ++#define MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08) ++ ++/*use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ ++ ++/*see mpi2_sas.h for values for ++ *SAS IO Unit Page 0 ControllerPhyDeviceInfo values */ ++ ++/*values for SAS IO Unit Page 0 DiscoveryStatus */ ++#define MPI2_SASIOUNIT0_DS_MAX_ENCLOSURES_EXCEED (0x80000000) ++#define MPI2_SASIOUNIT0_DS_MAX_EXPANDERS_EXCEED (0x40000000) ++#define MPI2_SASIOUNIT0_DS_MAX_DEVICES_EXCEED (0x20000000) ++#define MPI2_SASIOUNIT0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000) ++#define MPI2_SASIOUNIT0_DS_DOWNSTREAM_INITIATOR (0x08000000) ++#define MPI2_SASIOUNIT0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000) ++#define MPI2_SASIOUNIT0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000) ++#define MPI2_SASIOUNIT0_DS_MULTI_PORT_DOMAIN (0x00002000) ++#define MPI2_SASIOUNIT0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000) ++#define MPI2_SASIOUNIT0_DS_UNSUPPORTED_DEVICE (0x00000800) ++#define MPI2_SASIOUNIT0_DS_TABLE_LINK (0x00000400) ++#define MPI2_SASIOUNIT0_DS_SUBTRACTIVE_LINK (0x00000200) ++#define MPI2_SASIOUNIT0_DS_SMP_CRC_ERROR (0x00000100) ++#define MPI2_SASIOUNIT0_DS_SMP_FUNCTION_FAILED (0x00000080) ++#define MPI2_SASIOUNIT0_DS_INDEX_NOT_EXIST (0x00000040) ++#define MPI2_SASIOUNIT0_DS_OUT_ROUTE_ENTRIES (0x00000020) ++#define MPI2_SASIOUNIT0_DS_SMP_TIMEOUT (0x00000010) ++#define MPI2_SASIOUNIT0_DS_MULTIPLE_PORTS (0x00000004) ++#define MPI2_SASIOUNIT0_DS_UNADDRESSABLE_DEVICE (0x00000002) ++#define MPI2_SASIOUNIT0_DS_LOOP_DETECTED (0x00000001) ++ ++ ++/*SAS IO Unit Page 1 */ ++ ++typedef struct _MPI2_SAS_IO_UNIT1_PHY_DATA { ++ U8 Port; /*0x00 */ ++ U8 PortFlags; /*0x01 */ ++ U8 PhyFlags; /*0x02 */ ++ U8 MaxMinLinkRate; /*0x03 */ ++ U32 ControllerPhyDeviceInfo; /*0x04 */ ++ U16 MaxTargetPortConnectTime; /*0x08 */ ++ U16 Reserved1; /*0x0A */ ++} MPI2_SAS_IO_UNIT1_PHY_DATA, ++ *PTR_MPI2_SAS_IO_UNIT1_PHY_DATA, ++ Mpi2SasIOUnit1PhyData_t, ++ *pMpi2SasIOUnit1PhyData_t; ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI2_SAS_IOUNIT1_PHY_MAX ++#define MPI2_SAS_IOUNIT1_PHY_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_1 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U16 ++ ControlFlags; /*0x08 */ ++ U16 ++ SASNarrowMaxQueueDepth; /*0x0A */ ++ U16 ++ AdditionalControlFlags; /*0x0C */ ++ U16 ++ SASWideMaxQueueDepth; /*0x0E */ ++ U8 ++ NumPhys; /*0x10 */ ++ U8 ++ SATAMaxQDepth; /*0x11 */ ++ U8 ++ ReportDeviceMissingDelay; /*0x12 */ ++ U8 ++ IODeviceMissingDelay; /*0x13 */ ++ MPI2_SAS_IO_UNIT1_PHY_DATA ++ PhyData[MPI2_SAS_IOUNIT1_PHY_MAX]; /*0x14 */ ++} MPI2_CONFIG_PAGE_SASIOUNIT_1, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_1, ++ Mpi2SasIOUnitPage1_t, *pMpi2SasIOUnitPage1_t; ++ ++#define MPI2_SASIOUNITPAGE1_PAGEVERSION (0x09) ++ ++/*values for SAS IO Unit Page 1 ControlFlags */ ++#define MPI2_SASIOUNIT1_CONTROL_DEVICE_SELF_TEST (0x8000) ++#define MPI2_SASIOUNIT1_CONTROL_SATA_3_0_MAX (0x4000) ++#define MPI2_SASIOUNIT1_CONTROL_SATA_1_5_MAX (0x2000) ++#define MPI2_SASIOUNIT1_CONTROL_SATA_SW_PRESERVE (0x1000) ++ ++#define MPI2_SASIOUNIT1_CONTROL_MASK_DEV_SUPPORT (0x0600) ++#define MPI2_SASIOUNIT1_CONTROL_SHIFT_DEV_SUPPORT (9) ++#define MPI2_SASIOUNIT1_CONTROL_DEV_SUPPORT_BOTH (0x0) ++#define MPI2_SASIOUNIT1_CONTROL_DEV_SAS_SUPPORT (0x1) ++#define MPI2_SASIOUNIT1_CONTROL_DEV_SATA_SUPPORT (0x2) ++ ++#define MPI2_SASIOUNIT1_CONTROL_SATA_48BIT_LBA_REQUIRED (0x0080) ++#define MPI2_SASIOUNIT1_CONTROL_SATA_SMART_REQUIRED (0x0040) ++#define MPI2_SASIOUNIT1_CONTROL_SATA_NCQ_REQUIRED (0x0020) ++#define MPI2_SASIOUNIT1_CONTROL_SATA_FUA_REQUIRED (0x0010) ++#define MPI2_SASIOUNIT1_CONTROL_TABLE_SUBTRACTIVE_ILLEGAL (0x0008) ++#define MPI2_SASIOUNIT1_CONTROL_SUBTRACTIVE_ILLEGAL (0x0004) ++#define MPI2_SASIOUNIT1_CONTROL_FIRST_LVL_DISC_ONLY (0x0002) ++#define MPI2_SASIOUNIT1_CONTROL_CLEAR_AFFILIATION (0x0001) ++ ++/*values for SAS IO Unit Page 1 AdditionalControlFlags */ ++#define MPI2_SASIOUNIT1_ACONTROL_DA_PERSIST_CONNECT (0x0100) ++#define MPI2_SASIOUNIT1_ACONTROL_MULTI_PORT_DOMAIN_ILLEGAL (0x0080) ++#define MPI2_SASIOUNIT1_ACONTROL_SATA_ASYNCHROUNOUS_NOTIFICATION (0x0040) ++#define MPI2_SASIOUNIT1_ACONTROL_INVALID_TOPOLOGY_CORRECTION (0x0020) ++#define MPI2_SASIOUNIT1_ACONTROL_PORT_ENABLE_ONLY_SATA_LINK_RESET (0x0010) ++#define MPI2_SASIOUNIT1_ACONTROL_OTHER_AFFILIATION_SATA_LINK_RESET (0x0008) ++#define MPI2_SASIOUNIT1_ACONTROL_SELF_AFFILIATION_SATA_LINK_RESET (0x0004) ++#define MPI2_SASIOUNIT1_ACONTROL_NO_AFFILIATION_SATA_LINK_RESET (0x0002) ++#define MPI2_SASIOUNIT1_ACONTROL_ALLOW_TABLE_TO_TABLE (0x0001) ++ ++/*defines for SAS IO Unit Page 1 ReportDeviceMissingDelay */ ++#define MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK (0x7F) ++#define MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16 (0x80) ++ ++/*values for SAS IO Unit Page 1 PortFlags */ ++#define MPI2_SASIOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG (0x01) ++ ++/*values for SAS IO Unit Page 1 PhyFlags */ ++#define MPI2_SASIOUNIT1_PHYFLAGS_INIT_PERSIST_CONNECT (0x40) ++#define MPI2_SASIOUNIT1_PHYFLAGS_TARG_PERSIST_CONNECT (0x20) ++#define MPI2_SASIOUNIT1_PHYFLAGS_ZONING_ENABLE (0x10) ++#define MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08) ++ ++/*values for SAS IO Unit Page 1 MaxMinLinkRate */ ++#define MPI2_SASIOUNIT1_MAX_RATE_MASK (0xF0) ++#define MPI2_SASIOUNIT1_MAX_RATE_1_5 (0x80) ++#define MPI2_SASIOUNIT1_MAX_RATE_3_0 (0x90) ++#define MPI2_SASIOUNIT1_MAX_RATE_6_0 (0xA0) ++#define MPI25_SASIOUNIT1_MAX_RATE_12_0 (0xB0) ++#define MPI2_SASIOUNIT1_MIN_RATE_MASK (0x0F) ++#define MPI2_SASIOUNIT1_MIN_RATE_1_5 (0x08) ++#define MPI2_SASIOUNIT1_MIN_RATE_3_0 (0x09) ++#define MPI2_SASIOUNIT1_MIN_RATE_6_0 (0x0A) ++#define MPI25_SASIOUNIT1_MIN_RATE_12_0 (0x0B) ++ ++/*see mpi2_sas.h for values for ++ *SAS IO Unit Page 1 ControllerPhyDeviceInfo values */ ++ ++ ++/*SAS IO Unit Page 4 (for MPI v2.5 and earlier) */ ++ ++typedef struct _MPI2_SAS_IOUNIT4_SPINUP_GROUP { ++ U8 MaxTargetSpinup; /*0x00 */ ++ U8 SpinupDelay; /*0x01 */ ++ U8 SpinupFlags; /*0x02 */ ++ U8 Reserved1; /*0x03 */ ++} MPI2_SAS_IOUNIT4_SPINUP_GROUP, ++ *PTR_MPI2_SAS_IOUNIT4_SPINUP_GROUP, ++ Mpi2SasIOUnit4SpinupGroup_t, ++ *pMpi2SasIOUnit4SpinupGroup_t; ++/*defines for SAS IO Unit Page 4 SpinupFlags */ ++#define MPI2_SASIOUNIT4_SPINUP_DISABLE_FLAG (0x01) ++ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI2_SAS_IOUNIT4_PHY_MAX ++#define MPI2_SAS_IOUNIT4_PHY_MAX (4) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_4 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header;/*0x00 */ ++ MPI2_SAS_IOUNIT4_SPINUP_GROUP ++ SpinupGroupParameters[4]; /*0x08 */ ++ U32 ++ Reserved1; /*0x18 */ ++ U32 ++ Reserved2; /*0x1C */ ++ U32 ++ Reserved3; /*0x20 */ ++ U8 ++ BootDeviceWaitTime; /*0x24 */ ++ U8 ++ SATADeviceWaitTime; /*0x25 */ ++ U16 ++ Reserved5; /*0x26 */ ++ U8 ++ NumPhys; /*0x28 */ ++ U8 ++ PEInitialSpinupDelay; /*0x29 */ ++ U8 ++ PEReplyDelay; /*0x2A */ ++ U8 ++ Flags; /*0x2B */ ++ U8 ++ PHY[MPI2_SAS_IOUNIT4_PHY_MAX]; /*0x2C */ ++} MPI2_CONFIG_PAGE_SASIOUNIT_4, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_4, ++ Mpi2SasIOUnitPage4_t, *pMpi2SasIOUnitPage4_t; ++ ++#define MPI2_SASIOUNITPAGE4_PAGEVERSION (0x02) ++ ++/*defines for Flags field */ ++#define MPI2_SASIOUNIT4_FLAGS_AUTO_PORTENABLE (0x01) ++ ++/*defines for PHY field */ ++#define MPI2_SASIOUNIT4_PHY_SPINUP_GROUP_MASK (0x03) ++ ++ ++/*SAS IO Unit Page 5 */ ++ ++typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS { ++ U8 ControlFlags; /*0x00 */ ++ U8 PortWidthModGroup; /*0x01 */ ++ U16 InactivityTimerExponent; /*0x02 */ ++ U8 SATAPartialTimeout; /*0x04 */ ++ U8 Reserved2; /*0x05 */ ++ U8 SATASlumberTimeout; /*0x06 */ ++ U8 Reserved3; /*0x07 */ ++ U8 SASPartialTimeout; /*0x08 */ ++ U8 Reserved4; /*0x09 */ ++ U8 SASSlumberTimeout; /*0x0A */ ++ U8 Reserved5; /*0x0B */ ++} MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS, ++ *PTR_MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS, ++ Mpi2SasIOUnit5PhyPmSettings_t, ++ *pMpi2SasIOUnit5PhyPmSettings_t; ++ ++/*defines for ControlFlags field */ ++#define MPI2_SASIOUNIT5_CONTROL_SAS_SLUMBER_ENABLE (0x08) ++#define MPI2_SASIOUNIT5_CONTROL_SAS_PARTIAL_ENABLE (0x04) ++#define MPI2_SASIOUNIT5_CONTROL_SATA_SLUMBER_ENABLE (0x02) ++#define MPI2_SASIOUNIT5_CONTROL_SATA_PARTIAL_ENABLE (0x01) ++ ++/*defines for PortWidthModeGroup field */ ++#define MPI2_SASIOUNIT5_PWMG_DISABLE (0xFF) ++ ++/*defines for InactivityTimerExponent field */ ++#define MPI2_SASIOUNIT5_ITE_MASK_SAS_SLUMBER (0x7000) ++#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_SLUMBER (12) ++#define MPI2_SASIOUNIT5_ITE_MASK_SAS_PARTIAL (0x0700) ++#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_PARTIAL (8) ++#define MPI2_SASIOUNIT5_ITE_MASK_SATA_SLUMBER (0x0070) ++#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_SLUMBER (4) ++#define MPI2_SASIOUNIT5_ITE_MASK_SATA_PARTIAL (0x0007) ++#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_PARTIAL (0) ++ ++#define MPI2_SASIOUNIT5_ITE_TEN_SECONDS (7) ++#define MPI2_SASIOUNIT5_ITE_ONE_SECOND (6) ++#define MPI2_SASIOUNIT5_ITE_HUNDRED_MILLISECONDS (5) ++#define MPI2_SASIOUNIT5_ITE_TEN_MILLISECONDS (4) ++#define MPI2_SASIOUNIT5_ITE_ONE_MILLISECOND (3) ++#define MPI2_SASIOUNIT5_ITE_HUNDRED_MICROSECONDS (2) ++#define MPI2_SASIOUNIT5_ITE_TEN_MICROSECONDS (1) ++#define MPI2_SASIOUNIT5_ITE_ONE_MICROSECOND (0) ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI2_SAS_IOUNIT5_PHY_MAX ++#define MPI2_SAS_IOUNIT5_PHY_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_5 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U8 NumPhys; /*0x08 */ ++ U8 Reserved1;/*0x09 */ ++ U16 Reserved2;/*0x0A */ ++ U32 Reserved3;/*0x0C */ ++ MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS ++ SASPhyPowerManagementSettings[MPI2_SAS_IOUNIT5_PHY_MAX];/*0x10 */ ++} MPI2_CONFIG_PAGE_SASIOUNIT_5, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_5, ++ Mpi2SasIOUnitPage5_t, *pMpi2SasIOUnitPage5_t; ++ ++#define MPI2_SASIOUNITPAGE5_PAGEVERSION (0x01) ++ ++ ++/*SAS IO Unit Page 6 */ ++ ++typedef struct _MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS { ++ U8 CurrentStatus; /*0x00 */ ++ U8 CurrentModulation; /*0x01 */ ++ U8 CurrentUtilization; /*0x02 */ ++ U8 Reserved1; /*0x03 */ ++ U32 Reserved2; /*0x04 */ ++} MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS, ++ *PTR_MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS, ++ Mpi2SasIOUnit6PortWidthModGroupStatus_t, ++ *pMpi2SasIOUnit6PortWidthModGroupStatus_t; ++ ++/*defines for CurrentStatus field */ ++#define MPI2_SASIOUNIT6_STATUS_UNAVAILABLE (0x00) ++#define MPI2_SASIOUNIT6_STATUS_UNCONFIGURED (0x01) ++#define MPI2_SASIOUNIT6_STATUS_INVALID_CONFIG (0x02) ++#define MPI2_SASIOUNIT6_STATUS_LINK_DOWN (0x03) ++#define MPI2_SASIOUNIT6_STATUS_OBSERVATION_ONLY (0x04) ++#define MPI2_SASIOUNIT6_STATUS_INACTIVE (0x05) ++#define MPI2_SASIOUNIT6_STATUS_ACTIVE_IOUNIT (0x06) ++#define MPI2_SASIOUNIT6_STATUS_ACTIVE_HOST (0x07) ++ ++/*defines for CurrentModulation field */ ++#define MPI2_SASIOUNIT6_MODULATION_25_PERCENT (0x00) ++#define MPI2_SASIOUNIT6_MODULATION_50_PERCENT (0x01) ++#define MPI2_SASIOUNIT6_MODULATION_75_PERCENT (0x02) ++#define MPI2_SASIOUNIT6_MODULATION_100_PERCENT (0x03) ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumGroups at runtime. ++ */ ++#ifndef MPI2_SAS_IOUNIT6_GROUP_MAX ++#define MPI2_SAS_IOUNIT6_GROUP_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_6 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x08 */ ++ U32 Reserved2; /*0x0C */ ++ U8 NumGroups; /*0x10 */ ++ U8 Reserved3; /*0x11 */ ++ U16 Reserved4; /*0x12 */ ++ MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS ++ PortWidthModulationGroupStatus[MPI2_SAS_IOUNIT6_GROUP_MAX]; /*0x14 */ ++} MPI2_CONFIG_PAGE_SASIOUNIT_6, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_6, ++ Mpi2SasIOUnitPage6_t, *pMpi2SasIOUnitPage6_t; ++ ++#define MPI2_SASIOUNITPAGE6_PAGEVERSION (0x00) ++ ++ ++/*SAS IO Unit Page 7 */ ++ ++typedef struct _MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS { ++ U8 Flags; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U8 Threshold75Pct; /*0x04 */ ++ U8 Threshold50Pct; /*0x05 */ ++ U8 Threshold25Pct; /*0x06 */ ++ U8 Reserved3; /*0x07 */ ++} MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS, ++ *PTR_MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS, ++ Mpi2SasIOUnit7PortWidthModGroupSettings_t, ++ *pMpi2SasIOUnit7PortWidthModGroupSettings_t; ++ ++/*defines for Flags field */ ++#define MPI2_SASIOUNIT7_FLAGS_ENABLE_PORT_WIDTH_MODULATION (0x01) ++ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumGroups at runtime. ++ */ ++#ifndef MPI2_SAS_IOUNIT7_GROUP_MAX ++#define MPI2_SAS_IOUNIT7_GROUP_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_7 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U8 SamplingInterval; /*0x08 */ ++ U8 WindowLength; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U32 Reserved2; /*0x0C */ ++ U32 Reserved3; /*0x10 */ ++ U8 NumGroups; /*0x14 */ ++ U8 Reserved4; /*0x15 */ ++ U16 Reserved5; /*0x16 */ ++ MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS ++ PortWidthModulationGroupSettings[MPI2_SAS_IOUNIT7_GROUP_MAX];/*0x18 */ ++} MPI2_CONFIG_PAGE_SASIOUNIT_7, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_7, ++ Mpi2SasIOUnitPage7_t, *pMpi2SasIOUnitPage7_t; ++ ++#define MPI2_SASIOUNITPAGE7_PAGEVERSION (0x00) ++ ++ ++/*SAS IO Unit Page 8 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_8 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ Reserved1; /*0x08 */ ++ U32 ++ PowerManagementCapabilities; /*0x0C */ ++ U8 ++ TxRxSleepStatus; /*0x10 */ ++ U8 ++ Reserved2; /*0x11 */ ++ U16 ++ Reserved3; /*0x12 */ ++} MPI2_CONFIG_PAGE_SASIOUNIT_8, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_8, ++ Mpi2SasIOUnitPage8_t, *pMpi2SasIOUnitPage8_t; ++ ++#define MPI2_SASIOUNITPAGE8_PAGEVERSION (0x00) ++ ++/*defines for PowerManagementCapabilities field */ ++#define MPI2_SASIOUNIT8_PM_HOST_PORT_WIDTH_MOD (0x00001000) ++#define MPI2_SASIOUNIT8_PM_HOST_SAS_SLUMBER_MODE (0x00000800) ++#define MPI2_SASIOUNIT8_PM_HOST_SAS_PARTIAL_MODE (0x00000400) ++#define MPI2_SASIOUNIT8_PM_HOST_SATA_SLUMBER_MODE (0x00000200) ++#define MPI2_SASIOUNIT8_PM_HOST_SATA_PARTIAL_MODE (0x00000100) ++#define MPI2_SASIOUNIT8_PM_IOUNIT_PORT_WIDTH_MOD (0x00000010) ++#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_SLUMBER_MODE (0x00000008) ++#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_PARTIAL_MODE (0x00000004) ++#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_SLUMBER_MODE (0x00000002) ++#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_PARTIAL_MODE (0x00000001) ++ ++/*defines for TxRxSleepStatus field */ ++#define MPI25_SASIOUNIT8_TXRXSLEEP_UNSUPPORTED (0x00) ++#define MPI25_SASIOUNIT8_TXRXSLEEP_DISENGAGED (0x01) ++#define MPI25_SASIOUNIT8_TXRXSLEEP_ACTIVE (0x02) ++#define MPI25_SASIOUNIT8_TXRXSLEEP_SHUTDOWN (0x03) ++ ++ ++ ++/*SAS IO Unit Page 16 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT16 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U64 ++ TimeStamp; /*0x08 */ ++ U32 ++ Reserved1; /*0x10 */ ++ U32 ++ Reserved2; /*0x14 */ ++ U32 ++ FastPathPendedRequests; /*0x18 */ ++ U32 ++ FastPathUnPendedRequests; /*0x1C */ ++ U32 ++ FastPathHostRequestStarts; /*0x20 */ ++ U32 ++ FastPathFirmwareRequestStarts; /*0x24 */ ++ U32 ++ FastPathHostCompletions; /*0x28 */ ++ U32 ++ FastPathFirmwareCompletions; /*0x2C */ ++ U32 ++ NonFastPathRequestStarts; /*0x30 */ ++ U32 ++ NonFastPathHostCompletions; /*0x30 */ ++} MPI2_CONFIG_PAGE_SASIOUNIT16, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT16, ++ Mpi2SasIOUnitPage16_t, *pMpi2SasIOUnitPage16_t; ++ ++#define MPI2_SASIOUNITPAGE16_PAGEVERSION (0x00) ++ ++ ++/**************************************************************************** ++* SAS Expander Config Pages ++****************************************************************************/ ++ ++/*SAS Expander Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_EXPANDER_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U8 ++ PhysicalPort; /*0x08 */ ++ U8 ++ ReportGenLength; /*0x09 */ ++ U16 ++ EnclosureHandle; /*0x0A */ ++ U64 ++ SASAddress; /*0x0C */ ++ U32 ++ DiscoveryStatus; /*0x14 */ ++ U16 ++ DevHandle; /*0x18 */ ++ U16 ++ ParentDevHandle; /*0x1A */ ++ U16 ++ ExpanderChangeCount; /*0x1C */ ++ U16 ++ ExpanderRouteIndexes; /*0x1E */ ++ U8 ++ NumPhys; /*0x20 */ ++ U8 ++ SASLevel; /*0x21 */ ++ U16 ++ Flags; /*0x22 */ ++ U16 ++ STPBusInactivityTimeLimit; /*0x24 */ ++ U16 ++ STPMaxConnectTimeLimit; /*0x26 */ ++ U16 ++ STP_SMP_NexusLossTime; /*0x28 */ ++ U16 ++ MaxNumRoutedSasAddresses; /*0x2A */ ++ U64 ++ ActiveZoneManagerSASAddress;/*0x2C */ ++ U16 ++ ZoneLockInactivityLimit; /*0x34 */ ++ U16 ++ Reserved1; /*0x36 */ ++ U8 ++ TimeToReducedFunc; /*0x38 */ ++ U8 ++ InitialTimeToReducedFunc; /*0x39 */ ++ U8 ++ MaxReducedFuncTime; /*0x3A */ ++ U8 ++ Reserved2; /*0x3B */ ++} MPI2_CONFIG_PAGE_EXPANDER_0, ++ *PTR_MPI2_CONFIG_PAGE_EXPANDER_0, ++ Mpi2ExpanderPage0_t, *pMpi2ExpanderPage0_t; ++ ++#define MPI2_SASEXPANDER0_PAGEVERSION (0x06) ++ ++/*values for SAS Expander Page 0 DiscoveryStatus field */ ++#define MPI2_SAS_EXPANDER0_DS_MAX_ENCLOSURES_EXCEED (0x80000000) ++#define MPI2_SAS_EXPANDER0_DS_MAX_EXPANDERS_EXCEED (0x40000000) ++#define MPI2_SAS_EXPANDER0_DS_MAX_DEVICES_EXCEED (0x20000000) ++#define MPI2_SAS_EXPANDER0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000) ++#define MPI2_SAS_EXPANDER0_DS_DOWNSTREAM_INITIATOR (0x08000000) ++#define MPI2_SAS_EXPANDER0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000) ++#define MPI2_SAS_EXPANDER0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000) ++#define MPI2_SAS_EXPANDER0_DS_MULTI_PORT_DOMAIN (0x00002000) ++#define MPI2_SAS_EXPANDER0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000) ++#define MPI2_SAS_EXPANDER0_DS_UNSUPPORTED_DEVICE (0x00000800) ++#define MPI2_SAS_EXPANDER0_DS_TABLE_LINK (0x00000400) ++#define MPI2_SAS_EXPANDER0_DS_SUBTRACTIVE_LINK (0x00000200) ++#define MPI2_SAS_EXPANDER0_DS_SMP_CRC_ERROR (0x00000100) ++#define MPI2_SAS_EXPANDER0_DS_SMP_FUNCTION_FAILED (0x00000080) ++#define MPI2_SAS_EXPANDER0_DS_INDEX_NOT_EXIST (0x00000040) ++#define MPI2_SAS_EXPANDER0_DS_OUT_ROUTE_ENTRIES (0x00000020) ++#define MPI2_SAS_EXPANDER0_DS_SMP_TIMEOUT (0x00000010) ++#define MPI2_SAS_EXPANDER0_DS_MULTIPLE_PORTS (0x00000004) ++#define MPI2_SAS_EXPANDER0_DS_UNADDRESSABLE_DEVICE (0x00000002) ++#define MPI2_SAS_EXPANDER0_DS_LOOP_DETECTED (0x00000001) ++ ++/*values for SAS Expander Page 0 Flags field */ ++#define MPI2_SAS_EXPANDER0_FLAGS_REDUCED_FUNCTIONALITY (0x2000) ++#define MPI2_SAS_EXPANDER0_FLAGS_ZONE_LOCKED (0x1000) ++#define MPI2_SAS_EXPANDER0_FLAGS_SUPPORTED_PHYSICAL_PRES (0x0800) ++#define MPI2_SAS_EXPANDER0_FLAGS_ASSERTED_PHYSICAL_PRES (0x0400) ++#define MPI2_SAS_EXPANDER0_FLAGS_ZONING_SUPPORT (0x0200) ++#define MPI2_SAS_EXPANDER0_FLAGS_ENABLED_ZONING (0x0100) ++#define MPI2_SAS_EXPANDER0_FLAGS_TABLE_TO_TABLE_SUPPORT (0x0080) ++#define MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE (0x0010) ++#define MPI2_SAS_EXPANDER0_FLAGS_OTHERS_CONFIG (0x0004) ++#define MPI2_SAS_EXPANDER0_FLAGS_CONFIG_IN_PROGRESS (0x0002) ++#define MPI2_SAS_EXPANDER0_FLAGS_ROUTE_TABLE_CONFIG (0x0001) ++ ++ ++/*SAS Expander Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_EXPANDER_1 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U8 ++ PhysicalPort; /*0x08 */ ++ U8 ++ Reserved1; /*0x09 */ ++ U16 ++ Reserved2; /*0x0A */ ++ U8 ++ NumPhys; /*0x0C */ ++ U8 ++ Phy; /*0x0D */ ++ U16 ++ NumTableEntriesProgrammed; /*0x0E */ ++ U8 ++ ProgrammedLinkRate; /*0x10 */ ++ U8 ++ HwLinkRate; /*0x11 */ ++ U16 ++ AttachedDevHandle; /*0x12 */ ++ U32 ++ PhyInfo; /*0x14 */ ++ U32 ++ AttachedDeviceInfo; /*0x18 */ ++ U16 ++ ExpanderDevHandle; /*0x1C */ ++ U8 ++ ChangeCount; /*0x1E */ ++ U8 ++ NegotiatedLinkRate; /*0x1F */ ++ U8 ++ PhyIdentifier; /*0x20 */ ++ U8 ++ AttachedPhyIdentifier; /*0x21 */ ++ U8 ++ Reserved3; /*0x22 */ ++ U8 ++ DiscoveryInfo; /*0x23 */ ++ U32 ++ AttachedPhyInfo; /*0x24 */ ++ U8 ++ ZoneGroup; /*0x28 */ ++ U8 ++ SelfConfigStatus; /*0x29 */ ++ U16 ++ Reserved4; /*0x2A */ ++} MPI2_CONFIG_PAGE_EXPANDER_1, ++ *PTR_MPI2_CONFIG_PAGE_EXPANDER_1, ++ Mpi2ExpanderPage1_t, *pMpi2ExpanderPage1_t; ++ ++#define MPI2_SASEXPANDER1_PAGEVERSION (0x02) ++ ++/*use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */ ++ ++/*use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */ ++ ++/*use MPI2_SAS_PHYINFO_ for the PhyInfo field */ ++ ++/*see mpi2_sas.h for the MPI2_SAS_DEVICE_INFO_ defines ++ *used for the AttachedDeviceInfo field */ ++ ++/*use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ ++ ++/*values for SAS Expander Page 1 DiscoveryInfo field */ ++#define MPI2_SAS_EXPANDER1_DISCINFO_BAD_PHY_DISABLED (0x04) ++#define MPI2_SAS_EXPANDER1_DISCINFO_LINK_STATUS_CHANGE (0x02) ++#define MPI2_SAS_EXPANDER1_DISCINFO_NO_ROUTING_ENTRIES (0x01) ++ ++/*use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */ ++ ++ ++/**************************************************************************** ++* SAS Device Config Pages ++****************************************************************************/ ++ ++/*SAS Device Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U16 ++ Slot; /*0x08 */ ++ U16 ++ EnclosureHandle; /*0x0A */ ++ U64 ++ SASAddress; /*0x0C */ ++ U16 ++ ParentDevHandle; /*0x14 */ ++ U8 ++ PhyNum; /*0x16 */ ++ U8 ++ AccessStatus; /*0x17 */ ++ U16 ++ DevHandle; /*0x18 */ ++ U8 ++ AttachedPhyIdentifier; /*0x1A */ ++ U8 ++ ZoneGroup; /*0x1B */ ++ U32 ++ DeviceInfo; /*0x1C */ ++ U16 ++ Flags; /*0x20 */ ++ U8 ++ PhysicalPort; /*0x22 */ ++ U8 ++ MaxPortConnections; /*0x23 */ ++ U64 ++ DeviceName; /*0x24 */ ++ U8 ++ PortGroups; /*0x2C */ ++ U8 ++ DmaGroup; /*0x2D */ ++ U8 ++ ControlGroup; /*0x2E */ ++ U8 ++ EnclosureLevel; /*0x2F */ ++ U32 ++ ConnectorName[4]; /*0x30 */ ++ U32 ++ Reserved3; /*0x34 */ ++} MPI2_CONFIG_PAGE_SAS_DEV_0, ++ *PTR_MPI2_CONFIG_PAGE_SAS_DEV_0, ++ Mpi2SasDevicePage0_t, ++ *pMpi2SasDevicePage0_t; ++ ++#define MPI2_SASDEVICE0_PAGEVERSION (0x09) ++ ++/*values for SAS Device Page 0 AccessStatus field */ ++#define MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS (0x00) ++#define MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED (0x01) ++#define MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED (0x02) ++#define MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT (0x03) ++#define MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION (0x04) ++#define MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE (0x05) ++#define MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE (0x06) ++#define MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED (0x07) ++/*specific values for SATA Init failures */ ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN (0x10) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT (0x11) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG (0x12) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION (0x13) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER (0x14) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN (0x15) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN (0x16) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN (0x17) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION (0x18) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE (0x19) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX (0x1F) ++ ++/*see mpi2_sas.h for values for SAS Device Page 0 DeviceInfo values */ ++ ++/*values for SAS Device Page 0 Flags field */ ++#define MPI2_SAS_DEVICE0_FLAGS_UNAUTHORIZED_DEVICE (0x8000) ++#define MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH (0x4000) ++#define MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE (0x2000) ++#define MPI2_SAS_DEVICE0_FLAGS_SLUMBER_PM_CAPABLE (0x1000) ++#define MPI2_SAS_DEVICE0_FLAGS_PARTIAL_PM_CAPABLE (0x0800) ++#define MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY (0x0400) ++#define MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE (0x0200) ++#define MPI2_SAS_DEVICE0_FLAGS_UNSUPPORTED_DEVICE (0x0100) ++#define MPI2_SAS_DEVICE0_FLAGS_SATA_48BIT_LBA_SUPPORTED (0x0080) ++#define MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED (0x0040) ++#define MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED (0x0020) ++#define MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED (0x0010) ++#define MPI2_SAS_DEVICE0_FLAGS_PORT_SELECTOR_ATTACH (0x0008) ++#define MPI2_SAS_DEVICE0_FLAGS_PERSIST_CAPABLE (0x0004) ++#define MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID (0x0002) ++#define MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT (0x0001) ++ ++ ++/*SAS Device Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_1 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ Reserved1; /*0x08 */ ++ U64 ++ SASAddress; /*0x0C */ ++ U32 ++ Reserved2; /*0x14 */ ++ U16 ++ DevHandle; /*0x18 */ ++ U16 ++ Reserved3; /*0x1A */ ++ U8 ++ InitialRegDeviceFIS[20];/*0x1C */ ++} MPI2_CONFIG_PAGE_SAS_DEV_1, ++ *PTR_MPI2_CONFIG_PAGE_SAS_DEV_1, ++ Mpi2SasDevicePage1_t, ++ *pMpi2SasDevicePage1_t; ++ ++#define MPI2_SASDEVICE1_PAGEVERSION (0x01) ++ ++ ++/**************************************************************************** ++* SAS PHY Config Pages ++****************************************************************************/ ++ ++/*SAS PHY Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U16 ++ OwnerDevHandle; /*0x08 */ ++ U16 ++ Reserved1; /*0x0A */ ++ U16 ++ AttachedDevHandle; /*0x0C */ ++ U8 ++ AttachedPhyIdentifier; /*0x0E */ ++ U8 ++ Reserved2; /*0x0F */ ++ U32 ++ AttachedPhyInfo; /*0x10 */ ++ U8 ++ ProgrammedLinkRate; /*0x14 */ ++ U8 ++ HwLinkRate; /*0x15 */ ++ U8 ++ ChangeCount; /*0x16 */ ++ U8 ++ Flags; /*0x17 */ ++ U32 ++ PhyInfo; /*0x18 */ ++ U8 ++ NegotiatedLinkRate; /*0x1C */ ++ U8 ++ Reserved3; /*0x1D */ ++ U16 ++ Reserved4; /*0x1E */ ++} MPI2_CONFIG_PAGE_SAS_PHY_0, ++ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_0, ++ Mpi2SasPhyPage0_t, *pMpi2SasPhyPage0_t; ++ ++#define MPI2_SASPHY0_PAGEVERSION (0x03) ++ ++/*use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */ ++ ++/*use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */ ++ ++/*use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */ ++ ++/*values for SAS PHY Page 0 Flags field */ ++#define MPI2_SAS_PHY0_FLAGS_SGPIO_DIRECT_ATTACH_ENC (0x01) ++ ++/*use MPI2_SAS_PHYINFO_ for the PhyInfo field */ ++ ++/*use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ ++ ++ ++/*SAS PHY Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_1 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ Reserved1; /*0x08 */ ++ U32 ++ InvalidDwordCount; /*0x0C */ ++ U32 ++ RunningDisparityErrorCount; /*0x10 */ ++ U32 ++ LossDwordSynchCount; /*0x14 */ ++ U32 ++ PhyResetProblemCount; /*0x18 */ ++} MPI2_CONFIG_PAGE_SAS_PHY_1, ++ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_1, ++ Mpi2SasPhyPage1_t, *pMpi2SasPhyPage1_t; ++ ++#define MPI2_SASPHY1_PAGEVERSION (0x01) ++ ++ ++/*SAS PHY Page 2 */ ++ ++typedef struct _MPI2_SASPHY2_PHY_EVENT { ++ U8 PhyEventCode; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U32 PhyEventInfo; /*0x04 */ ++} MPI2_SASPHY2_PHY_EVENT, *PTR_MPI2_SASPHY2_PHY_EVENT, ++ Mpi2SasPhy2PhyEvent_t, *pMpi2SasPhy2PhyEvent_t; ++ ++/*use MPI2_SASPHY3_EVENT_CODE_ for the PhyEventCode field */ ++ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhyEvents at runtime. ++ */ ++#ifndef MPI2_SASPHY2_PHY_EVENT_MAX ++#define MPI2_SASPHY2_PHY_EVENT_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_2 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ Reserved1; /*0x08 */ ++ U8 ++ NumPhyEvents; /*0x0C */ ++ U8 ++ Reserved2; /*0x0D */ ++ U16 ++ Reserved3; /*0x0E */ ++ MPI2_SASPHY2_PHY_EVENT ++ PhyEvent[MPI2_SASPHY2_PHY_EVENT_MAX]; /*0x10 */ ++} MPI2_CONFIG_PAGE_SAS_PHY_2, ++ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_2, ++ Mpi2SasPhyPage2_t, ++ *pMpi2SasPhyPage2_t; ++ ++#define MPI2_SASPHY2_PAGEVERSION (0x00) ++ ++ ++/*SAS PHY Page 3 */ ++ ++typedef struct _MPI2_SASPHY3_PHY_EVENT_CONFIG { ++ U8 PhyEventCode; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U8 CounterType; /*0x04 */ ++ U8 ThresholdWindow; /*0x05 */ ++ U8 TimeUnits; /*0x06 */ ++ U8 Reserved3; /*0x07 */ ++ U32 EventThreshold; /*0x08 */ ++ U16 ThresholdFlags; /*0x0C */ ++ U16 Reserved4; /*0x0E */ ++} MPI2_SASPHY3_PHY_EVENT_CONFIG, ++ *PTR_MPI2_SASPHY3_PHY_EVENT_CONFIG, ++ Mpi2SasPhy3PhyEventConfig_t, ++ *pMpi2SasPhy3PhyEventConfig_t; ++ ++/*values for PhyEventCode field */ ++#define MPI2_SASPHY3_EVENT_CODE_NO_EVENT (0x00) ++#define MPI2_SASPHY3_EVENT_CODE_INVALID_DWORD (0x01) ++#define MPI2_SASPHY3_EVENT_CODE_RUNNING_DISPARITY_ERROR (0x02) ++#define MPI2_SASPHY3_EVENT_CODE_LOSS_DWORD_SYNC (0x03) ++#define MPI2_SASPHY3_EVENT_CODE_PHY_RESET_PROBLEM (0x04) ++#define MPI2_SASPHY3_EVENT_CODE_ELASTICITY_BUF_OVERFLOW (0x05) ++#define MPI2_SASPHY3_EVENT_CODE_RX_ERROR (0x06) ++#define MPI2_SASPHY3_EVENT_CODE_RX_ADDR_FRAME_ERROR (0x20) ++#define MPI2_SASPHY3_EVENT_CODE_TX_AC_OPEN_REJECT (0x21) ++#define MPI2_SASPHY3_EVENT_CODE_RX_AC_OPEN_REJECT (0x22) ++#define MPI2_SASPHY3_EVENT_CODE_TX_RC_OPEN_REJECT (0x23) ++#define MPI2_SASPHY3_EVENT_CODE_RX_RC_OPEN_REJECT (0x24) ++#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_PARTIAL_WAITING_ON (0x25) ++#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_CONNECT_WAITING_ON (0x26) ++#define MPI2_SASPHY3_EVENT_CODE_TX_BREAK (0x27) ++#define MPI2_SASPHY3_EVENT_CODE_RX_BREAK (0x28) ++#define MPI2_SASPHY3_EVENT_CODE_BREAK_TIMEOUT (0x29) ++#define MPI2_SASPHY3_EVENT_CODE_CONNECTION (0x2A) ++#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_PATHWAY_BLOCKED (0x2B) ++#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_ARB_WAIT_TIME (0x2C) ++#define MPI2_SASPHY3_EVENT_CODE_PEAK_ARB_WAIT_TIME (0x2D) ++#define MPI2_SASPHY3_EVENT_CODE_PEAK_CONNECT_TIME (0x2E) ++#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_FRAMES (0x40) ++#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_FRAMES (0x41) ++#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_ERROR_FRAMES (0x42) ++#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_ERROR_FRAMES (0x43) ++#define MPI2_SASPHY3_EVENT_CODE_TX_CREDIT_BLOCKED (0x44) ++#define MPI2_SASPHY3_EVENT_CODE_RX_CREDIT_BLOCKED (0x45) ++#define MPI2_SASPHY3_EVENT_CODE_TX_SATA_FRAMES (0x50) ++#define MPI2_SASPHY3_EVENT_CODE_RX_SATA_FRAMES (0x51) ++#define MPI2_SASPHY3_EVENT_CODE_SATA_OVERFLOW (0x52) ++#define MPI2_SASPHY3_EVENT_CODE_TX_SMP_FRAMES (0x60) ++#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_FRAMES (0x61) ++#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_ERROR_FRAMES (0x63) ++#define MPI2_SASPHY3_EVENT_CODE_HOTPLUG_TIMEOUT (0xD0) ++#define MPI2_SASPHY3_EVENT_CODE_MISALIGNED_MUX_PRIMITIVE (0xD1) ++#define MPI2_SASPHY3_EVENT_CODE_RX_AIP (0xD2) ++ ++/*Following codes are product specific and in MPI v2.6 and later */ ++#define MPI2_SASPHY3_EVENT_CODE_LCARB_WAIT_TIME (0xD3) ++#define MPI2_SASPHY3_EVENT_CODE_RCVD_CONN_RESP_WAIT_TIME (0xD4) ++#define MPI2_SASPHY3_EVENT_CODE_LCCONN_TIME (0xD5) ++#define MPI2_SASPHY3_EVENT_CODE_SSP_TX_START_TRANSMIT (0xD6) ++#define MPI2_SASPHY3_EVENT_CODE_SATA_TX_START (0xD7) ++#define MPI2_SASPHY3_EVENT_CODE_SMP_TX_START_TRANSMT (0xD8) ++#define MPI2_SASPHY3_EVENT_CODE_TX_SMP_BREAK_CONN (0xD9) ++#define MPI2_SASPHY3_EVENT_CODE_SSP_RX_START_RECEIVE (0xDA) ++#define MPI2_SASPHY3_EVENT_CODE_SATA_RX_START_RECEIVE (0xDB) ++#define MPI2_SASPHY3_EVENT_CODE_SMP_RX_START_RECEIVE (0xDC) ++ ++ ++/*values for the CounterType field */ ++#define MPI2_SASPHY3_COUNTER_TYPE_WRAPPING (0x00) ++#define MPI2_SASPHY3_COUNTER_TYPE_SATURATING (0x01) ++#define MPI2_SASPHY3_COUNTER_TYPE_PEAK_VALUE (0x02) ++ ++/*values for the TimeUnits field */ ++#define MPI2_SASPHY3_TIME_UNITS_10_MICROSECONDS (0x00) ++#define MPI2_SASPHY3_TIME_UNITS_100_MICROSECONDS (0x01) ++#define MPI2_SASPHY3_TIME_UNITS_1_MILLISECOND (0x02) ++#define MPI2_SASPHY3_TIME_UNITS_10_MILLISECONDS (0x03) ++ ++/*values for the ThresholdFlags field */ ++#define MPI2_SASPHY3_TFLAGS_PHY_RESET (0x0002) ++#define MPI2_SASPHY3_TFLAGS_EVENT_NOTIFY (0x0001) ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhyEvents at runtime. ++ */ ++#ifndef MPI2_SASPHY3_PHY_EVENT_MAX ++#define MPI2_SASPHY3_PHY_EVENT_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_3 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ Reserved1; /*0x08 */ ++ U8 ++ NumPhyEvents; /*0x0C */ ++ U8 ++ Reserved2; /*0x0D */ ++ U16 ++ Reserved3; /*0x0E */ ++ MPI2_SASPHY3_PHY_EVENT_CONFIG ++ PhyEventConfig[MPI2_SASPHY3_PHY_EVENT_MAX]; /*0x10 */ ++} MPI2_CONFIG_PAGE_SAS_PHY_3, ++ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_3, ++ Mpi2SasPhyPage3_t, *pMpi2SasPhyPage3_t; ++ ++#define MPI2_SASPHY3_PAGEVERSION (0x00) ++ ++ ++/*SAS PHY Page 4 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_4 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U16 ++ Reserved1; /*0x08 */ ++ U8 ++ Reserved2; /*0x0A */ ++ U8 ++ Flags; /*0x0B */ ++ U8 ++ InitialFrame[28]; /*0x0C */ ++} MPI2_CONFIG_PAGE_SAS_PHY_4, ++ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_4, ++ Mpi2SasPhyPage4_t, *pMpi2SasPhyPage4_t; ++ ++#define MPI2_SASPHY4_PAGEVERSION (0x00) ++ ++/*values for the Flags field */ ++#define MPI2_SASPHY4_FLAGS_FRAME_VALID (0x02) ++#define MPI2_SASPHY4_FLAGS_SATA_FRAME (0x01) ++ ++ ++ ++ ++/**************************************************************************** ++* SAS Port Config Pages ++****************************************************************************/ ++ ++/*SAS Port Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_PORT_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U8 ++ PortNumber; /*0x08 */ ++ U8 ++ PhysicalPort; /*0x09 */ ++ U8 ++ PortWidth; /*0x0A */ ++ U8 ++ PhysicalPortWidth; /*0x0B */ ++ U8 ++ ZoneGroup; /*0x0C */ ++ U8 ++ Reserved1; /*0x0D */ ++ U16 ++ Reserved2; /*0x0E */ ++ U64 ++ SASAddress; /*0x10 */ ++ U32 ++ DeviceInfo; /*0x18 */ ++ U32 ++ Reserved3; /*0x1C */ ++ U32 ++ Reserved4; /*0x20 */ ++} MPI2_CONFIG_PAGE_SAS_PORT_0, ++ *PTR_MPI2_CONFIG_PAGE_SAS_PORT_0, ++ Mpi2SasPortPage0_t, *pMpi2SasPortPage0_t; ++ ++#define MPI2_SASPORT0_PAGEVERSION (0x00) ++ ++/*see mpi2_sas.h for values for SAS Port Page 0 DeviceInfo values */ ++ ++ ++/**************************************************************************** ++* SAS Enclosure Config Pages ++****************************************************************************/ ++ ++/*SAS Enclosure Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ Reserved1; /*0x08 */ ++ U64 ++ EnclosureLogicalID; /*0x0C */ ++ U16 ++ Flags; /*0x14 */ ++ U16 ++ EnclosureHandle; /*0x16 */ ++ U16 ++ NumSlots; /*0x18 */ ++ U16 ++ StartSlot; /*0x1A */ ++ U8 ++ Reserved2; /*0x1C */ ++ U8 ++ EnclosureLevel; /*0x1D */ ++ U16 ++ SEPDevHandle; /*0x1E */ ++ U32 ++ Reserved3; /*0x20 */ ++ U32 ++ Reserved4; /*0x24 */ ++} MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0, ++ *PTR_MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0, ++ Mpi2SasEnclosurePage0_t, *pMpi2SasEnclosurePage0_t; ++ ++#define MPI2_SASENCLOSURE0_PAGEVERSION (0x04) ++ ++/*values for SAS Enclosure Page 0 Flags field */ ++#define MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID (0x0010) ++#define MPI2_SAS_ENCLS0_FLAGS_MNG_MASK (0x000F) ++#define MPI2_SAS_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000) ++#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SES (0x0001) ++#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SGPIO (0x0002) ++#define MPI2_SAS_ENCLS0_FLAGS_MNG_EXP_SGPIO (0x0003) ++#define MPI2_SAS_ENCLS0_FLAGS_MNG_SES_ENCLOSURE (0x0004) ++#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_GPIO (0x0005) ++ ++ ++/**************************************************************************** ++* Log Config Page ++****************************************************************************/ ++ ++/*Log Page 0 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumLogEntries at runtime. ++ */ ++#ifndef MPI2_LOG_0_NUM_LOG_ENTRIES ++#define MPI2_LOG_0_NUM_LOG_ENTRIES (1) ++#endif ++ ++#define MPI2_LOG_0_LOG_DATA_LENGTH (0x1C) ++ ++typedef struct _MPI2_LOG_0_ENTRY { ++ U64 TimeStamp; /*0x00 */ ++ U32 Reserved1; /*0x08 */ ++ U16 LogSequence; /*0x0C */ ++ U16 LogEntryQualifier; /*0x0E */ ++ U8 VP_ID; /*0x10 */ ++ U8 VF_ID; /*0x11 */ ++ U16 Reserved2; /*0x12 */ ++ U8 ++ LogData[MPI2_LOG_0_LOG_DATA_LENGTH];/*0x14 */ ++} MPI2_LOG_0_ENTRY, *PTR_MPI2_LOG_0_ENTRY, ++ Mpi2Log0Entry_t, *pMpi2Log0Entry_t; ++ ++/*values for Log Page 0 LogEntry LogEntryQualifier field */ ++#define MPI2_LOG_0_ENTRY_QUAL_ENTRY_UNUSED (0x0000) ++#define MPI2_LOG_0_ENTRY_QUAL_POWER_ON_RESET (0x0001) ++#define MPI2_LOG_0_ENTRY_QUAL_TIMESTAMP_UPDATE (0x0002) ++#define MPI2_LOG_0_ENTRY_QUAL_MIN_IMPLEMENT_SPEC (0x8000) ++#define MPI2_LOG_0_ENTRY_QUAL_MAX_IMPLEMENT_SPEC (0xFFFF) ++ ++typedef struct _MPI2_CONFIG_PAGE_LOG_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x08 */ ++ U32 Reserved2; /*0x0C */ ++ U16 NumLogEntries;/*0x10 */ ++ U16 Reserved3; /*0x12 */ ++ MPI2_LOG_0_ENTRY ++ LogEntry[MPI2_LOG_0_NUM_LOG_ENTRIES]; /*0x14 */ ++} MPI2_CONFIG_PAGE_LOG_0, *PTR_MPI2_CONFIG_PAGE_LOG_0, ++ Mpi2LogPage0_t, *pMpi2LogPage0_t; ++ ++#define MPI2_LOG_0_PAGEVERSION (0x02) ++ ++ ++/**************************************************************************** ++* RAID Config Page ++****************************************************************************/ ++ ++/*RAID Page 0 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumElements at runtime. ++ */ ++#ifndef MPI2_RAIDCONFIG0_MAX_ELEMENTS ++#define MPI2_RAIDCONFIG0_MAX_ELEMENTS (1) ++#endif ++ ++typedef struct _MPI2_RAIDCONFIG0_CONFIG_ELEMENT { ++ U16 ElementFlags; /*0x00 */ ++ U16 VolDevHandle; /*0x02 */ ++ U8 HotSparePool; /*0x04 */ ++ U8 PhysDiskNum; /*0x05 */ ++ U16 PhysDiskDevHandle; /*0x06 */ ++} MPI2_RAIDCONFIG0_CONFIG_ELEMENT, ++ *PTR_MPI2_RAIDCONFIG0_CONFIG_ELEMENT, ++ Mpi2RaidConfig0ConfigElement_t, ++ *pMpi2RaidConfig0ConfigElement_t; ++ ++/*values for the ElementFlags field */ ++#define MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE (0x000F) ++#define MPI2_RAIDCONFIG0_EFLAGS_VOLUME_ELEMENT (0x0000) ++#define MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT (0x0001) ++#define MPI2_RAIDCONFIG0_EFLAGS_HOT_SPARE_ELEMENT (0x0002) ++#define MPI2_RAIDCONFIG0_EFLAGS_OCE_ELEMENT (0x0003) ++ ++ ++typedef struct _MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U8 NumHotSpares; /*0x08 */ ++ U8 NumPhysDisks; /*0x09 */ ++ U8 NumVolumes; /*0x0A */ ++ U8 ConfigNum; /*0x0B */ ++ U32 Flags; /*0x0C */ ++ U8 ConfigGUID[24]; /*0x10 */ ++ U32 Reserved1; /*0x28 */ ++ U8 NumElements; /*0x2C */ ++ U8 Reserved2; /*0x2D */ ++ U16 Reserved3; /*0x2E */ ++ MPI2_RAIDCONFIG0_CONFIG_ELEMENT ++ ConfigElement[MPI2_RAIDCONFIG0_MAX_ELEMENTS]; /*0x30 */ ++} MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0, ++ *PTR_MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0, ++ Mpi2RaidConfigurationPage0_t, ++ *pMpi2RaidConfigurationPage0_t; ++ ++#define MPI2_RAIDCONFIG0_PAGEVERSION (0x00) ++ ++/*values for RAID Configuration Page 0 Flags field */ ++#define MPI2_RAIDCONFIG0_FLAG_FOREIGN_CONFIG (0x00000001) ++ ++ ++/**************************************************************************** ++* Driver Persistent Mapping Config Pages ++****************************************************************************/ ++ ++/*Driver Persistent Mapping Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY { ++ U64 PhysicalIdentifier; /*0x00 */ ++ U16 MappingInformation; /*0x08 */ ++ U16 DeviceIndex; /*0x0A */ ++ U32 PhysicalBitsMapping; /*0x0C */ ++ U32 Reserved1; /*0x10 */ ++} MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY, ++ *PTR_MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY, ++ Mpi2DriverMap0Entry_t, *pMpi2DriverMap0Entry_t; ++ ++typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAPPING_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY Entry; /*0x08 */ ++} MPI2_CONFIG_PAGE_DRIVER_MAPPING_0, ++ *PTR_MPI2_CONFIG_PAGE_DRIVER_MAPPING_0, ++ Mpi2DriverMappingPage0_t, *pMpi2DriverMappingPage0_t; ++ ++#define MPI2_DRIVERMAPPING0_PAGEVERSION (0x00) ++ ++/*values for Driver Persistent Mapping Page 0 MappingInformation field */ ++#define MPI2_DRVMAP0_MAPINFO_SLOT_MASK (0x07F0) ++#define MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT (4) ++#define MPI2_DRVMAP0_MAPINFO_MISSING_MASK (0x000F) ++ ++ ++/**************************************************************************** ++* Ethernet Config Pages ++****************************************************************************/ ++ ++/*Ethernet Page 0 */ ++ ++/*IP address (union of IPv4 and IPv6) */ ++typedef union _MPI2_ETHERNET_IP_ADDR { ++ U32 IPv4Addr; ++ U32 IPv6Addr[4]; ++} MPI2_ETHERNET_IP_ADDR, *PTR_MPI2_ETHERNET_IP_ADDR, ++ Mpi2EthernetIpAddr_t, *pMpi2EthernetIpAddr_t; ++ ++#define MPI2_ETHERNET_HOST_NAME_LENGTH (32) ++ ++typedef struct _MPI2_CONFIG_PAGE_ETHERNET_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U8 NumInterfaces; /*0x08 */ ++ U8 Reserved0; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U32 Status; /*0x0C */ ++ U8 MediaState; /*0x10 */ ++ U8 Reserved2; /*0x11 */ ++ U16 Reserved3; /*0x12 */ ++ U8 MacAddress[6]; /*0x14 */ ++ U8 Reserved4; /*0x1A */ ++ U8 Reserved5; /*0x1B */ ++ MPI2_ETHERNET_IP_ADDR IpAddress; /*0x1C */ ++ MPI2_ETHERNET_IP_ADDR SubnetMask; /*0x2C */ ++ MPI2_ETHERNET_IP_ADDR GatewayIpAddress;/*0x3C */ ++ MPI2_ETHERNET_IP_ADDR DNS1IpAddress; /*0x4C */ ++ MPI2_ETHERNET_IP_ADDR DNS2IpAddress; /*0x5C */ ++ MPI2_ETHERNET_IP_ADDR DhcpIpAddress; /*0x6C */ ++ U8 ++ HostName[MPI2_ETHERNET_HOST_NAME_LENGTH];/*0x7C */ ++} MPI2_CONFIG_PAGE_ETHERNET_0, ++ *PTR_MPI2_CONFIG_PAGE_ETHERNET_0, ++ Mpi2EthernetPage0_t, *pMpi2EthernetPage0_t; ++ ++#define MPI2_ETHERNETPAGE0_PAGEVERSION (0x00) ++ ++/*values for Ethernet Page 0 Status field */ ++#define MPI2_ETHPG0_STATUS_IPV6_CAPABLE (0x80000000) ++#define MPI2_ETHPG0_STATUS_IPV4_CAPABLE (0x40000000) ++#define MPI2_ETHPG0_STATUS_CONSOLE_CONNECTED (0x20000000) ++#define MPI2_ETHPG0_STATUS_DEFAULT_IF (0x00000100) ++#define MPI2_ETHPG0_STATUS_FW_DWNLD_ENABLED (0x00000080) ++#define MPI2_ETHPG0_STATUS_TELNET_ENABLED (0x00000040) ++#define MPI2_ETHPG0_STATUS_SSH2_ENABLED (0x00000020) ++#define MPI2_ETHPG0_STATUS_DHCP_CLIENT_ENABLED (0x00000010) ++#define MPI2_ETHPG0_STATUS_IPV6_ENABLED (0x00000008) ++#define MPI2_ETHPG0_STATUS_IPV4_ENABLED (0x00000004) ++#define MPI2_ETHPG0_STATUS_IPV6_ADDRESSES (0x00000002) ++#define MPI2_ETHPG0_STATUS_ETH_IF_ENABLED (0x00000001) ++ ++/*values for Ethernet Page 0 MediaState field */ ++#define MPI2_ETHPG0_MS_DUPLEX_MASK (0x80) ++#define MPI2_ETHPG0_MS_HALF_DUPLEX (0x00) ++#define MPI2_ETHPG0_MS_FULL_DUPLEX (0x80) ++ ++#define MPI2_ETHPG0_MS_CONNECT_SPEED_MASK (0x07) ++#define MPI2_ETHPG0_MS_NOT_CONNECTED (0x00) ++#define MPI2_ETHPG0_MS_10MBIT (0x01) ++#define MPI2_ETHPG0_MS_100MBIT (0x02) ++#define MPI2_ETHPG0_MS_1GBIT (0x03) ++ ++ ++/*Ethernet Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_ETHERNET_1 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ Reserved0; /*0x08 */ ++ U32 ++ Flags; /*0x0C */ ++ U8 ++ MediaState; /*0x10 */ ++ U8 ++ Reserved1; /*0x11 */ ++ U16 ++ Reserved2; /*0x12 */ ++ U8 ++ MacAddress[6]; /*0x14 */ ++ U8 ++ Reserved3; /*0x1A */ ++ U8 ++ Reserved4; /*0x1B */ ++ MPI2_ETHERNET_IP_ADDR ++ StaticIpAddress; /*0x1C */ ++ MPI2_ETHERNET_IP_ADDR ++ StaticSubnetMask; /*0x2C */ ++ MPI2_ETHERNET_IP_ADDR ++ StaticGatewayIpAddress; /*0x3C */ ++ MPI2_ETHERNET_IP_ADDR ++ StaticDNS1IpAddress; /*0x4C */ ++ MPI2_ETHERNET_IP_ADDR ++ StaticDNS2IpAddress; /*0x5C */ ++ U32 ++ Reserved5; /*0x6C */ ++ U32 ++ Reserved6; /*0x70 */ ++ U32 ++ Reserved7; /*0x74 */ ++ U32 ++ Reserved8; /*0x78 */ ++ U8 ++ HostName[MPI2_ETHERNET_HOST_NAME_LENGTH];/*0x7C */ ++} MPI2_CONFIG_PAGE_ETHERNET_1, ++ *PTR_MPI2_CONFIG_PAGE_ETHERNET_1, ++ Mpi2EthernetPage1_t, *pMpi2EthernetPage1_t; ++ ++#define MPI2_ETHERNETPAGE1_PAGEVERSION (0x00) ++ ++/*values for Ethernet Page 1 Flags field */ ++#define MPI2_ETHPG1_FLAG_SET_DEFAULT_IF (0x00000100) ++#define MPI2_ETHPG1_FLAG_ENABLE_FW_DOWNLOAD (0x00000080) ++#define MPI2_ETHPG1_FLAG_ENABLE_TELNET (0x00000040) ++#define MPI2_ETHPG1_FLAG_ENABLE_SSH2 (0x00000020) ++#define MPI2_ETHPG1_FLAG_ENABLE_DHCP_CLIENT (0x00000010) ++#define MPI2_ETHPG1_FLAG_ENABLE_IPV6 (0x00000008) ++#define MPI2_ETHPG1_FLAG_ENABLE_IPV4 (0x00000004) ++#define MPI2_ETHPG1_FLAG_USE_IPV6_ADDRESSES (0x00000002) ++#define MPI2_ETHPG1_FLAG_ENABLE_ETH_IF (0x00000001) ++ ++/*values for Ethernet Page 1 MediaState field */ ++#define MPI2_ETHPG1_MS_DUPLEX_MASK (0x80) ++#define MPI2_ETHPG1_MS_HALF_DUPLEX (0x00) ++#define MPI2_ETHPG1_MS_FULL_DUPLEX (0x80) ++ ++#define MPI2_ETHPG1_MS_DATA_RATE_MASK (0x07) ++#define MPI2_ETHPG1_MS_DATA_RATE_AUTO (0x00) ++#define MPI2_ETHPG1_MS_DATA_RATE_10MBIT (0x01) ++#define MPI2_ETHPG1_MS_DATA_RATE_100MBIT (0x02) ++#define MPI2_ETHPG1_MS_DATA_RATE_1GBIT (0x03) ++ ++ ++/**************************************************************************** ++* Extended Manufacturing Config Pages ++****************************************************************************/ ++ ++/* ++ *Generic structure to use for product-specific extended manufacturing pages ++ *(currently Extended Manufacturing Page 40 through Extended Manufacturing ++ *Page 60). ++ */ ++ ++typedef struct _MPI2_CONFIG_PAGE_EXT_MAN_PS { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ ProductSpecificInfo; /*0x08 */ ++} MPI2_CONFIG_PAGE_EXT_MAN_PS, ++ *PTR_MPI2_CONFIG_PAGE_EXT_MAN_PS, ++ Mpi2ExtManufacturingPagePS_t, ++ *pMpi2ExtManufacturingPagePS_t; ++ ++/*PageVersion should be provided by product-specific code */ ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_init.h b/drivers/scsi/mpt2sas/mpi/mpi2_init.h +new file mode 100644 +index 0000000..bba56b6 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2_init.h +@@ -0,0 +1,581 @@ ++/* ++ * Copyright 2000-2015 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2_init.h ++ * Title: MPI SCSI initiator mode messages and structures ++ * Creation Date: June 23, 2006 ++ * ++ * mpi2_init.h Version: 02.00.20 ++ * ++ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 ++ * prefix are for use only on MPI v2.5 products, and must not be used ++ * with MPI v2.0 products. Unless otherwise noted, names beginning with ++ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products. ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 10-31-07 02.00.01 Fixed name for pMpi2SCSITaskManagementRequest_t. ++ * 12-18-07 02.00.02 Modified Task Management Target Reset Method defines. ++ * 02-29-08 02.00.03 Added Query Task Set and Query Unit Attention. ++ * 03-03-08 02.00.04 Fixed name of struct _MPI2_SCSI_TASK_MANAGE_REPLY. ++ * 05-21-08 02.00.05 Fixed typo in name of Mpi2SepRequest_t. ++ * 10-02-08 02.00.06 Removed Untagged and No Disconnect values from SCSI IO ++ * Control field Task Attribute flags. ++ * Moved LUN field defines to mpi2.h becasue they are ++ * common to many structures. ++ * 05-06-09 02.00.07 Changed task management type of Query Unit Attention to ++ * Query Asynchronous Event. ++ * Defined two new bits in the SlotStatus field of the SCSI ++ * Enclosure Processor Request and Reply. ++ * 10-28-09 02.00.08 Added defines for decoding the ResponseInfo bytes for ++ * both SCSI IO Error Reply and SCSI Task Management Reply. ++ * Added ResponseInfo field to MPI2_SCSI_TASK_MANAGE_REPLY. ++ * Added MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG define. ++ * 02-10-10 02.00.09 Removed unused structure that had "#if 0" around it. ++ * 05-12-10 02.00.10 Added optional vendor-unique region to SCSI IO Request. ++ * 11-10-10 02.00.11 Added MPI2_SCSIIO_NUM_SGLOFFSETS define. ++ * 11-18-11 02.00.12 Incorporating additions for MPI v2.5. ++ * 02-06-12 02.00.13 Added alternate defines for Task Priority / Command ++ * Priority to match SAM-4. ++ * Added EEDPErrorOffset to MPI2_SCSI_IO_REPLY. ++ * 07-10-12 02.00.14 Added MPI2_SCSIIO_CONTROL_SHIFT_DATADIRECTION. ++ * 04-09-13 02.00.15 Added SCSIStatusQualifier field to MPI2_SCSI_IO_REPLY, ++ * replacing the Reserved4 field. ++ * 11-18-14 02.00.16 Updated copyright information. ++ * 03-16-15 02.00.17 Updated for MPI v2.6. ++ * Added MPI26_SCSIIO_IOFLAGS_ESCAPE_PASSTHROUGH. ++ * Added MPI2_SEP_REQ_SLOTSTATUS_DEV_OFF and ++ * MPI2_SEP_REPLY_SLOTSTATUS_DEV_OFF. ++ * 08-26-15 02.00.18 Added SCSITASKMGMT_MSGFLAGS for Target Reset. ++ * 12-18-15 02.00.19 Added EEDPObservedValue added to SCSI IO Reply message. ++ * 01-04-16 02.00.20 Modified EEDP reported values in SCSI IO Reply message. ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_INIT_H ++#define MPI2_INIT_H ++ ++/***************************************************************************** ++* ++* SCSI Initiator Messages ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* SCSI IO messages and associated structures ++****************************************************************************/ ++ ++typedef struct _MPI2_SCSI_IO_CDB_EEDP32 { ++ U8 CDB[20]; /*0x00 */ ++ U32 PrimaryReferenceTag; /*0x14 */ ++ U16 PrimaryApplicationTag; /*0x18 */ ++ U16 PrimaryApplicationTagMask; /*0x1A */ ++ U32 TransferLength; /*0x1C */ ++} MPI2_SCSI_IO_CDB_EEDP32, *PTR_MPI2_SCSI_IO_CDB_EEDP32, ++ Mpi2ScsiIoCdbEedp32_t, *pMpi2ScsiIoCdbEedp32_t; ++ ++/*MPI v2.0 CDB field */ ++typedef union _MPI2_SCSI_IO_CDB_UNION { ++ U8 CDB32[32]; ++ MPI2_SCSI_IO_CDB_EEDP32 EEDP32; ++ MPI2_SGE_SIMPLE_UNION SGE; ++} MPI2_SCSI_IO_CDB_UNION, *PTR_MPI2_SCSI_IO_CDB_UNION, ++ Mpi2ScsiIoCdb_t, *pMpi2ScsiIoCdb_t; ++ ++/*MPI v2.0 SCSI IO Request Message */ ++typedef struct _MPI2_SCSI_IO_REQUEST { ++ U16 DevHandle; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved1; /*0x04 */ ++ U8 Reserved2; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U32 SenseBufferLowAddress; /*0x0C */ ++ U16 SGLFlags; /*0x10 */ ++ U8 SenseBufferLength; /*0x12 */ ++ U8 Reserved4; /*0x13 */ ++ U8 SGLOffset0; /*0x14 */ ++ U8 SGLOffset1; /*0x15 */ ++ U8 SGLOffset2; /*0x16 */ ++ U8 SGLOffset3; /*0x17 */ ++ U32 SkipCount; /*0x18 */ ++ U32 DataLength; /*0x1C */ ++ U32 BidirectionalDataLength; /*0x20 */ ++ U16 IoFlags; /*0x24 */ ++ U16 EEDPFlags; /*0x26 */ ++ U32 EEDPBlockSize; /*0x28 */ ++ U32 SecondaryReferenceTag; /*0x2C */ ++ U16 SecondaryApplicationTag; /*0x30 */ ++ U16 ApplicationTagTranslationMask; /*0x32 */ ++ U8 LUN[8]; /*0x34 */ ++ U32 Control; /*0x3C */ ++ MPI2_SCSI_IO_CDB_UNION CDB; /*0x40 */ ++ ++#ifdef MPI2_SCSI_IO_VENDOR_UNIQUE_REGION /*typically this is left undefined */ ++ MPI2_SCSI_IO_VENDOR_UNIQUE VendorRegion; ++#endif ++ ++ MPI2_SGE_IO_UNION SGL; /*0x60 */ ++ ++} MPI2_SCSI_IO_REQUEST, *PTR_MPI2_SCSI_IO_REQUEST, ++ Mpi2SCSIIORequest_t, *pMpi2SCSIIORequest_t; ++ ++/*SCSI IO MsgFlags bits */ ++ ++/*MsgFlags for SenseBufferAddressSpace */ ++#define MPI2_SCSIIO_MSGFLAGS_MASK_SENSE_ADDR (0x0C) ++#define MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR (0x00) ++#define MPI2_SCSIIO_MSGFLAGS_IOCDDR_SENSE_ADDR (0x04) ++#define MPI2_SCSIIO_MSGFLAGS_IOCPLB_SENSE_ADDR (0x08) ++#define MPI2_SCSIIO_MSGFLAGS_IOCPLBNTA_SENSE_ADDR (0x0C) ++#define MPI26_SCSIIO_MSGFLAGS_IOCCTL_SENSE_ADDR (0x08) ++ ++/*SCSI IO SGLFlags bits */ ++ ++/*base values for Data Location Address Space */ ++#define MPI2_SCSIIO_SGLFLAGS_ADDR_MASK (0x0C) ++#define MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR (0x00) ++#define MPI2_SCSIIO_SGLFLAGS_IOCDDR_ADDR (0x04) ++#define MPI2_SCSIIO_SGLFLAGS_IOCPLB_ADDR (0x08) ++#define MPI2_SCSIIO_SGLFLAGS_IOCPLBNTA_ADDR (0x0C) ++ ++/*base values for Type */ ++#define MPI2_SCSIIO_SGLFLAGS_TYPE_MASK (0x03) ++#define MPI2_SCSIIO_SGLFLAGS_TYPE_MPI (0x00) ++#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE32 (0x01) ++#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE64 (0x02) ++ ++/*shift values for each sub-field */ ++#define MPI2_SCSIIO_SGLFLAGS_SGL3_SHIFT (12) ++#define MPI2_SCSIIO_SGLFLAGS_SGL2_SHIFT (8) ++#define MPI2_SCSIIO_SGLFLAGS_SGL1_SHIFT (4) ++#define MPI2_SCSIIO_SGLFLAGS_SGL0_SHIFT (0) ++ ++/*number of SGLOffset fields */ ++#define MPI2_SCSIIO_NUM_SGLOFFSETS (4) ++ ++/*SCSI IO IoFlags bits */ ++ ++/*Large CDB Address Space */ ++#define MPI2_SCSIIO_CDB_ADDR_MASK (0x6000) ++#define MPI2_SCSIIO_CDB_ADDR_SYSTEM (0x0000) ++#define MPI2_SCSIIO_CDB_ADDR_IOCDDR (0x2000) ++#define MPI2_SCSIIO_CDB_ADDR_IOCPLB (0x4000) ++#define MPI2_SCSIIO_CDB_ADDR_IOCPLBNTA (0x6000) ++ ++#define MPI2_SCSIIO_IOFLAGS_LARGE_CDB (0x1000) ++#define MPI2_SCSIIO_IOFLAGS_BIDIRECTIONAL (0x0800) ++#define MPI2_SCSIIO_IOFLAGS_MULTICAST (0x0400) ++#define MPI2_SCSIIO_IOFLAGS_CMD_DETERMINES_DATA_DIR (0x0200) ++#define MPI2_SCSIIO_IOFLAGS_CDBLENGTH_MASK (0x01FF) ++ ++/*SCSI IO EEDPFlags bits */ ++ ++#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG (0x8000) ++#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_REFTAG (0x4000) ++#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_APPTAG (0x2000) ++#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_APPTAG (0x1000) ++ ++#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG (0x0400) ++#define MPI2_SCSIIO_EEDPFLAGS_CHECK_APPTAG (0x0200) ++#define MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD (0x0100) ++ ++#define MPI2_SCSIIO_EEDPFLAGS_PASSTHRU_REFTAG (0x0008) ++ ++#define MPI2_SCSIIO_EEDPFLAGS_MASK_OP (0x0007) ++#define MPI2_SCSIIO_EEDPFLAGS_NOOP_OP (0x0000) ++#define MPI2_SCSIIO_EEDPFLAGS_CHECK_OP (0x0001) ++#define MPI2_SCSIIO_EEDPFLAGS_STRIP_OP (0x0002) ++#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP (0x0003) ++#define MPI2_SCSIIO_EEDPFLAGS_INSERT_OP (0x0004) ++#define MPI2_SCSIIO_EEDPFLAGS_REPLACE_OP (0x0006) ++#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REGEN_OP (0x0007) ++ ++/*SCSI IO LUN fields: use MPI2_LUN_ from mpi2.h */ ++ ++/*SCSI IO Control bits */ ++#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_MASK (0xFC000000) ++#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26) ++ ++#define MPI2_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000) ++#define MPI2_SCSIIO_CONTROL_SHIFT_DATADIRECTION (24) ++#define MPI2_SCSIIO_CONTROL_NODATATRANSFER (0x00000000) ++#define MPI2_SCSIIO_CONTROL_WRITE (0x01000000) ++#define MPI2_SCSIIO_CONTROL_READ (0x02000000) ++#define MPI2_SCSIIO_CONTROL_BIDIRECTIONAL (0x03000000) ++ ++#define MPI2_SCSIIO_CONTROL_TASKPRI_MASK (0x00007800) ++#define MPI2_SCSIIO_CONTROL_TASKPRI_SHIFT (11) ++/*alternate name for the previous field; called Command Priority in SAM-4 */ ++#define MPI2_SCSIIO_CONTROL_CMDPRI_MASK (0x00007800) ++#define MPI2_SCSIIO_CONTROL_CMDPRI_SHIFT (11) ++ ++#define MPI2_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700) ++#define MPI2_SCSIIO_CONTROL_SIMPLEQ (0x00000000) ++#define MPI2_SCSIIO_CONTROL_HEADOFQ (0x00000100) ++#define MPI2_SCSIIO_CONTROL_ORDEREDQ (0x00000200) ++#define MPI2_SCSIIO_CONTROL_ACAQ (0x00000400) ++ ++#define MPI2_SCSIIO_CONTROL_TLR_MASK (0x000000C0) ++#define MPI2_SCSIIO_CONTROL_NO_TLR (0x00000000) ++#define MPI2_SCSIIO_CONTROL_TLR_ON (0x00000040) ++#define MPI2_SCSIIO_CONTROL_TLR_OFF (0x00000080) ++ ++/*MPI v2.5 CDB field */ ++typedef union _MPI25_SCSI_IO_CDB_UNION { ++ U8 CDB32[32]; ++ MPI2_SCSI_IO_CDB_EEDP32 EEDP32; ++ MPI2_IEEE_SGE_SIMPLE64 SGE; ++} MPI25_SCSI_IO_CDB_UNION, *PTR_MPI25_SCSI_IO_CDB_UNION, ++ Mpi25ScsiIoCdb_t, *pMpi25ScsiIoCdb_t; ++ ++/*MPI v2.5/2.6 SCSI IO Request Message */ ++typedef struct _MPI25_SCSI_IO_REQUEST { ++ U16 DevHandle; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved1; /*0x04 */ ++ U8 Reserved2; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U32 SenseBufferLowAddress; /*0x0C */ ++ U8 DMAFlags; /*0x10 */ ++ U8 Reserved5; /*0x11 */ ++ U8 SenseBufferLength; /*0x12 */ ++ U8 Reserved4; /*0x13 */ ++ U8 SGLOffset0; /*0x14 */ ++ U8 SGLOffset1; /*0x15 */ ++ U8 SGLOffset2; /*0x16 */ ++ U8 SGLOffset3; /*0x17 */ ++ U32 SkipCount; /*0x18 */ ++ U32 DataLength; /*0x1C */ ++ U32 BidirectionalDataLength; /*0x20 */ ++ U16 IoFlags; /*0x24 */ ++ U16 EEDPFlags; /*0x26 */ ++ U16 EEDPBlockSize; /*0x28 */ ++ U16 Reserved6; /*0x2A */ ++ U32 SecondaryReferenceTag; /*0x2C */ ++ U16 SecondaryApplicationTag; /*0x30 */ ++ U16 ApplicationTagTranslationMask; /*0x32 */ ++ U8 LUN[8]; /*0x34 */ ++ U32 Control; /*0x3C */ ++ MPI25_SCSI_IO_CDB_UNION CDB; /*0x40 */ ++ ++#ifdef MPI25_SCSI_IO_VENDOR_UNIQUE_REGION /*typically this is left undefined */ ++ MPI25_SCSI_IO_VENDOR_UNIQUE VendorRegion; ++#endif ++ ++ MPI25_SGE_IO_UNION SGL; /*0x60 */ ++ ++} MPI25_SCSI_IO_REQUEST, *PTR_MPI25_SCSI_IO_REQUEST, ++ Mpi25SCSIIORequest_t, *pMpi25SCSIIORequest_t; ++ ++/*use MPI2_SCSIIO_MSGFLAGS_ defines for the MsgFlags field */ ++ ++/*Defines for the DMAFlags field ++ * Each setting affects 4 SGLS, from SGL0 to SGL3. ++ * D = Data ++ * C = Cache DIF ++ * I = Interleaved ++ * H = Host DIF ++ */ ++#define MPI25_SCSIIO_DMAFLAGS_OP_MASK (0x0F) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_D_D (0x00) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_D_C (0x01) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_D_I (0x02) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_C_C (0x03) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_C_I (0x04) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_I_I (0x05) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_C_C_C (0x06) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_C_C_I (0x07) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_C_I_I (0x08) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_I_I_I (0x09) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_D_D (0x0A) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_D_C (0x0B) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_D_I (0x0C) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_C_C (0x0D) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_C_I (0x0E) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_I_I (0x0F) ++ ++/*number of SGLOffset fields */ ++#define MPI25_SCSIIO_NUM_SGLOFFSETS (4) ++ ++/*defines for the IoFlags field */ ++#define MPI25_SCSIIO_IOFLAGS_IO_PATH_MASK (0xC000) ++#define MPI25_SCSIIO_IOFLAGS_NORMAL_PATH (0x0000) ++#define MPI25_SCSIIO_IOFLAGS_FAST_PATH (0x4000) ++ ++#define MPI26_SCSIIO_IOFLAGS_ESCAPE_PASSTHROUGH (0x2000) ++#define MPI25_SCSIIO_IOFLAGS_LARGE_CDB (0x1000) ++#define MPI25_SCSIIO_IOFLAGS_BIDIRECTIONAL (0x0800) ++#define MPI26_SCSIIO_IOFLAGS_PORT_REQUEST (0x0400) ++#define MPI25_SCSIIO_IOFLAGS_CDBLENGTH_MASK (0x01FF) ++ ++/*MPI v2.5 defines for the EEDPFlags bits */ ++/*use MPI2_SCSIIO_EEDPFLAGS_ defines for the other EEDPFlags bits */ ++#define MPI25_SCSIIO_EEDPFLAGS_ESCAPE_MODE_MASK (0x00C0) ++#define MPI25_SCSIIO_EEDPFLAGS_COMPATIBLE_MODE (0x0000) ++#define MPI25_SCSIIO_EEDPFLAGS_DO_NOT_DISABLE_MODE (0x0040) ++#define MPI25_SCSIIO_EEDPFLAGS_APPTAG_DISABLE_MODE (0x0080) ++#define MPI25_SCSIIO_EEDPFLAGS_APPTAG_REFTAG_DISABLE_MODE (0x00C0) ++ ++#define MPI25_SCSIIO_EEDPFLAGS_HOST_GUARD_METHOD_MASK (0x0030) ++#define MPI25_SCSIIO_EEDPFLAGS_T10_CRC_HOST_GUARD (0x0000) ++#define MPI25_SCSIIO_EEDPFLAGS_IP_CHKSUM_HOST_GUARD (0x0010) ++ ++/*use MPI2_LUN_ defines from mpi2.h for the LUN field */ ++ ++/*use MPI2_SCSIIO_CONTROL_ defines for the Control field */ ++ ++/*NOTE: The SCSI IO Reply is nearly the same for MPI 2.0 and MPI 2.5, so ++ * MPI2_SCSI_IO_REPLY is used for both. ++ */ ++ ++/*SCSI IO Error Reply Message */ ++typedef struct _MPI2_SCSI_IO_REPLY { ++ U16 DevHandle; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved1; /*0x04 */ ++ U8 Reserved2; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U8 SCSIStatus; /*0x0C */ ++ U8 SCSIState; /*0x0D */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U32 TransferCount; /*0x14 */ ++ U32 SenseCount; /*0x18 */ ++ U32 ResponseInfo; /*0x1C */ ++ U16 TaskTag; /*0x20 */ ++ U16 SCSIStatusQualifier; /* 0x22 */ ++ U32 BidirectionalTransferCount; /*0x24 */ ++ /* MPI 2.5+ only; Reserved in MPI 2.0 */ ++ U32 EEDPErrorOffset; /* 0x28 */ ++ /* MPI 2.5+ only; Reserved in MPI 2.0 */ ++ U16 EEDPObservedAppTag; /* 0x2C */ ++ /* MPI 2.5+ only; Reserved in MPI 2.0 */ ++ U16 EEDPObservedGuard; /* 0x2E */ ++ /* MPI 2.5+ only; Reserved in MPI 2.0 */ ++ U32 EEDPObservedRefTag; /* 0x30 */ ++} MPI2_SCSI_IO_REPLY, *PTR_MPI2_SCSI_IO_REPLY, ++ Mpi2SCSIIOReply_t, *pMpi2SCSIIOReply_t; ++ ++/*SCSI IO Reply SCSIStatus values (SAM-4 status codes) */ ++ ++#define MPI2_SCSI_STATUS_GOOD (0x00) ++#define MPI2_SCSI_STATUS_CHECK_CONDITION (0x02) ++#define MPI2_SCSI_STATUS_CONDITION_MET (0x04) ++#define MPI2_SCSI_STATUS_BUSY (0x08) ++#define MPI2_SCSI_STATUS_INTERMEDIATE (0x10) ++#define MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14) ++#define MPI2_SCSI_STATUS_RESERVATION_CONFLICT (0x18) ++#define MPI2_SCSI_STATUS_COMMAND_TERMINATED (0x22) /*obsolete */ ++#define MPI2_SCSI_STATUS_TASK_SET_FULL (0x28) ++#define MPI2_SCSI_STATUS_ACA_ACTIVE (0x30) ++#define MPI2_SCSI_STATUS_TASK_ABORTED (0x40) ++ ++/*SCSI IO Reply SCSIState flags */ ++ ++#define MPI2_SCSI_STATE_RESPONSE_INFO_VALID (0x10) ++#define MPI2_SCSI_STATE_TERMINATED (0x08) ++#define MPI2_SCSI_STATE_NO_SCSI_STATUS (0x04) ++#define MPI2_SCSI_STATE_AUTOSENSE_FAILED (0x02) ++#define MPI2_SCSI_STATE_AUTOSENSE_VALID (0x01) ++ ++/*masks and shifts for the ResponseInfo field */ ++ ++#define MPI2_SCSI_RI_MASK_REASONCODE (0x000000FF) ++#define MPI2_SCSI_RI_SHIFT_REASONCODE (0) ++ ++#define MPI2_SCSI_TASKTAG_UNKNOWN (0xFFFF) ++ ++/**************************************************************************** ++* SCSI Task Management messages ++****************************************************************************/ ++ ++/*SCSI Task Management Request Message */ ++typedef struct _MPI2_SCSI_TASK_MANAGE_REQUEST { ++ U16 DevHandle; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U8 Reserved1; /*0x04 */ ++ U8 TaskType; /*0x05 */ ++ U8 Reserved2; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U8 LUN[8]; /*0x0C */ ++ U32 Reserved4[7]; /*0x14 */ ++ U16 TaskMID; /*0x30 */ ++ U16 Reserved5; /*0x32 */ ++} MPI2_SCSI_TASK_MANAGE_REQUEST, ++ *PTR_MPI2_SCSI_TASK_MANAGE_REQUEST, ++ Mpi2SCSITaskManagementRequest_t, ++ *pMpi2SCSITaskManagementRequest_t; ++ ++/*TaskType values */ ++ ++#define MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK (0x01) ++#define MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET (0x02) ++#define MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x03) ++#define MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET (0x05) ++#define MPI2_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET (0x06) ++#define MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK (0x07) ++#define MPI2_SCSITASKMGMT_TASKTYPE_CLR_ACA (0x08) ++#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_TASK_SET (0x09) ++#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT (0x0A) ++ ++/*obsolete TaskType name */ ++#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_UNIT_ATTENTION \ ++ (MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT) ++ ++/*MsgFlags bits */ ++ ++#define MPI2_SCSITASKMGMT_MSGFLAGS_MASK_TARGET_RESET (0x18) ++#define MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET (0x00) ++#define MPI2_SCSITASKMGMT_MSGFLAGS_NEXUS_RESET_SRST (0x08) ++#define MPI2_SCSITASKMGMT_MSGFLAGS_SAS_HARD_LINK_RESET (0x10) ++ ++#define MPI2_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU (0x01) ++ ++/*SCSI Task Management Reply Message */ ++typedef struct _MPI2_SCSI_TASK_MANAGE_REPLY { ++ U16 DevHandle; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U8 ResponseCode; /*0x04 */ ++ U8 TaskType; /*0x05 */ ++ U8 Reserved1; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved2; /*0x0A */ ++ U16 Reserved3; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U32 TerminationCount; /*0x14 */ ++ U32 ResponseInfo; /*0x18 */ ++} MPI2_SCSI_TASK_MANAGE_REPLY, ++ *PTR_MPI2_SCSI_TASK_MANAGE_REPLY, ++ Mpi2SCSITaskManagementReply_t, *pMpi2SCSIManagementReply_t; ++ ++/*ResponseCode values */ ++ ++#define MPI2_SCSITASKMGMT_RSP_TM_COMPLETE (0x00) ++#define MPI2_SCSITASKMGMT_RSP_INVALID_FRAME (0x02) ++#define MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED (0x04) ++#define MPI2_SCSITASKMGMT_RSP_TM_FAILED (0x05) ++#define MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED (0x08) ++#define MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN (0x09) ++#define MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG (0x0A) ++#define MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC (0x80) ++ ++/*masks and shifts for the ResponseInfo field */ ++ ++#define MPI2_SCSITASKMGMT_RI_MASK_REASONCODE (0x000000FF) ++#define MPI2_SCSITASKMGMT_RI_SHIFT_REASONCODE (0) ++#define MPI2_SCSITASKMGMT_RI_MASK_ARI2 (0x0000FF00) ++#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI2 (8) ++#define MPI2_SCSITASKMGMT_RI_MASK_ARI1 (0x00FF0000) ++#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI1 (16) ++#define MPI2_SCSITASKMGMT_RI_MASK_ARI0 (0xFF000000) ++#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI0 (24) ++ ++/**************************************************************************** ++* SCSI Enclosure Processor messages ++****************************************************************************/ ++ ++/*SCSI Enclosure Processor Request Message */ ++typedef struct _MPI2_SEP_REQUEST { ++ U16 DevHandle; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U8 Action; /*0x04 */ ++ U8 Flags; /*0x05 */ ++ U8 Reserved1; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved2; /*0x0A */ ++ U32 SlotStatus; /*0x0C */ ++ U32 Reserved3; /*0x10 */ ++ U32 Reserved4; /*0x14 */ ++ U32 Reserved5; /*0x18 */ ++ U16 Slot; /*0x1C */ ++ U16 EnclosureHandle; /*0x1E */ ++} MPI2_SEP_REQUEST, *PTR_MPI2_SEP_REQUEST, ++ Mpi2SepRequest_t, *pMpi2SepRequest_t; ++ ++/*Action defines */ ++#define MPI2_SEP_REQ_ACTION_WRITE_STATUS (0x00) ++#define MPI2_SEP_REQ_ACTION_READ_STATUS (0x01) ++ ++/*Flags defines */ ++#define MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS (0x00) ++#define MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS (0x01) ++ ++/*SlotStatus defines */ ++#define MPI2_SEP_REQ_SLOTSTATUS_DEV_OFF (0x00080000) ++#define MPI2_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE (0x00040000) ++#define MPI2_SEP_REQ_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000) ++#define MPI2_SEP_REQ_SLOTSTATUS_REBUILD_STOPPED (0x00000200) ++#define MPI2_SEP_REQ_SLOTSTATUS_HOT_SPARE (0x00000100) ++#define MPI2_SEP_REQ_SLOTSTATUS_UNCONFIGURED (0x00000080) ++#define MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT (0x00000040) ++#define MPI2_SEP_REQ_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010) ++#define MPI2_SEP_REQ_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008) ++#define MPI2_SEP_REQ_SLOTSTATUS_DEV_REBUILDING (0x00000004) ++#define MPI2_SEP_REQ_SLOTSTATUS_DEV_FAULTY (0x00000002) ++#define MPI2_SEP_REQ_SLOTSTATUS_NO_ERROR (0x00000001) ++ ++/*SCSI Enclosure Processor Reply Message */ ++typedef struct _MPI2_SEP_REPLY { ++ U16 DevHandle; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U8 Action; /*0x04 */ ++ U8 Flags; /*0x05 */ ++ U8 Reserved1; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved2; /*0x0A */ ++ U16 Reserved3; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U32 SlotStatus; /*0x14 */ ++ U32 Reserved4; /*0x18 */ ++ U16 Slot; /*0x1C */ ++ U16 EnclosureHandle; /*0x1E */ ++} MPI2_SEP_REPLY, *PTR_MPI2_SEP_REPLY, ++ Mpi2SepReply_t, *pMpi2SepReply_t; ++ ++/*SlotStatus defines */ ++#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_OFF (0x00080000) ++#define MPI2_SEP_REPLY_SLOTSTATUS_REMOVE_READY (0x00040000) ++#define MPI2_SEP_REPLY_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000) ++#define MPI2_SEP_REPLY_SLOTSTATUS_REBUILD_STOPPED (0x00000200) ++#define MPI2_SEP_REPLY_SLOTSTATUS_HOT_SPARE (0x00000100) ++#define MPI2_SEP_REPLY_SLOTSTATUS_UNCONFIGURED (0x00000080) ++#define MPI2_SEP_REPLY_SLOTSTATUS_PREDICTED_FAULT (0x00000040) ++#define MPI2_SEP_REPLY_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010) ++#define MPI2_SEP_REPLY_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008) ++#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_REBUILDING (0x00000004) ++#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_FAULTY (0x00000002) ++#define MPI2_SEP_REPLY_SLOTSTATUS_NO_ERROR (0x00000001) ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h +new file mode 100644 +index 0000000..8bae305 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h +@@ -0,0 +1,1860 @@ ++/* ++ * Copyright 2000-2015 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2_ioc.h ++ * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages ++ * Creation Date: October 11, 2006 ++ * ++ * mpi2_ioc.h Version: 02.00.27 ++ * ++ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 ++ * prefix are for use only on MPI v2.5 products, and must not be used ++ * with MPI v2.0 products. Unless otherwise noted, names beginning with ++ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products. ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 06-04-07 02.00.01 In IOCFacts Reply structure, renamed MaxDevices to ++ * MaxTargets. ++ * Added TotalImageSize field to FWDownload Request. ++ * Added reserved words to FWUpload Request. ++ * 06-26-07 02.00.02 Added IR Configuration Change List Event. ++ * 08-31-07 02.00.03 Removed SystemReplyQueueDepth field from the IOCInit ++ * request and replaced it with ++ * ReplyDescriptorPostQueueDepth and ReplyFreeQueueDepth. ++ * Replaced the MinReplyQueueDepth field of the IOCFacts ++ * reply with MaxReplyDescriptorPostQueueDepth. ++ * Added MPI2_RDPQ_DEPTH_MIN define to specify the minimum ++ * depth for the Reply Descriptor Post Queue. ++ * Added SASAddress field to Initiator Device Table ++ * Overflow Event data. ++ * 10-31-07 02.00.04 Added ReasonCode MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING ++ * for SAS Initiator Device Status Change Event data. ++ * Modified Reason Code defines for SAS Topology Change ++ * List Event data, including adding a bit for PHY Vacant ++ * status, and adding a mask for the Reason Code. ++ * Added define for ++ * MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING. ++ * Added define for MPI2_EXT_IMAGE_TYPE_MEGARAID. ++ * 12-18-07 02.00.05 Added Boot Status defines for the IOCExceptions field of ++ * the IOCFacts Reply. ++ * Removed MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define. ++ * Moved MPI2_VERSION_UNION to mpi2.h. ++ * Changed MPI2_EVENT_NOTIFICATION_REQUEST to use masks ++ * instead of enables, and added SASBroadcastPrimitiveMasks ++ * field. ++ * Added Log Entry Added Event and related structure. ++ * 02-29-08 02.00.06 Added define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID. ++ * Removed define MPI2_IOCFACTS_PROTOCOL_SMP_TARGET. ++ * Added MaxVolumes and MaxPersistentEntries fields to ++ * IOCFacts reply. ++ * Added ProtocalFlags and IOCCapabilities fields to ++ * MPI2_FW_IMAGE_HEADER. ++ * Removed MPI2_PORTENABLE_FLAGS_ENABLE_SINGLE_PORT. ++ * 03-03-08 02.00.07 Fixed MPI2_FW_IMAGE_HEADER by changing Reserved26 to ++ * a U16 (from a U32). ++ * Removed extra 's' from EventMasks name. ++ * 06-27-08 02.00.08 Fixed an offset in a comment. ++ * 10-02-08 02.00.09 Removed SystemReplyFrameSize from MPI2_IOC_INIT_REQUEST. ++ * Removed CurReplyFrameSize from MPI2_IOC_FACTS_REPLY and ++ * renamed MinReplyFrameSize to ReplyFrameSize. ++ * Added MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX. ++ * Added two new RAIDOperation values for Integrated RAID ++ * Operations Status Event data. ++ * Added four new IR Configuration Change List Event data ++ * ReasonCode values. ++ * Added two new ReasonCode defines for SAS Device Status ++ * Change Event data. ++ * Added three new DiscoveryStatus bits for the SAS ++ * Discovery event data. ++ * Added Multiplexing Status Change bit to the PhyStatus ++ * field of the SAS Topology Change List event data. ++ * Removed define for MPI2_INIT_IMAGE_BOOTFLAGS_XMEMCOPY. ++ * BootFlags are now product-specific. ++ * Added defines for the indivdual signature bytes ++ * for MPI2_INIT_IMAGE_FOOTER. ++ * 01-19-09 02.00.10 Added MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY define. ++ * Added MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR ++ * define. ++ * Added MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE ++ * define. ++ * Removed MPI2_EVENT_SAS_DISC_DS_SATA_INIT_FAILURE define. ++ * 05-06-09 02.00.11 Added MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR define. ++ * Added MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX define. ++ * Added two new reason codes for SAS Device Status Change ++ * Event. ++ * Added new event: SAS PHY Counter. ++ * 07-30-09 02.00.12 Added GPIO Interrupt event define and structure. ++ * Added MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define. ++ * Added new product id family for 2208. ++ * 10-28-09 02.00.13 Added HostMSIxVectors field to MPI2_IOC_INIT_REQUEST. ++ * Added MaxMSIxVectors field to MPI2_IOC_FACTS_REPLY. ++ * Added MinDevHandle field to MPI2_IOC_FACTS_REPLY. ++ * Added MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY. ++ * Added MPI2_EVENT_HOST_BASED_DISCOVERY_PHY define. ++ * Added MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER define. ++ * Added Host Based Discovery Phy Event data. ++ * Added defines for ProductID Product field ++ * (MPI2_FW_HEADER_PID_). ++ * Modified values for SAS ProductID Family ++ * (MPI2_FW_HEADER_PID_FAMILY_). ++ * 02-10-10 02.00.14 Added SAS Quiesce Event structure and defines. ++ * Added PowerManagementControl Request structures and ++ * defines. ++ * 05-12-10 02.00.15 Marked Task Set Full Event as obsolete. ++ * Added MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY define. ++ * 11-10-10 02.00.16 Added MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC. ++ * 02-23-11 02.00.17 Added SAS NOTIFY Primitive event, and added ++ * SASNotifyPrimitiveMasks field to ++ * MPI2_EVENT_NOTIFICATION_REQUEST. ++ * Added Temperature Threshold Event. ++ * Added Host Message Event. ++ * Added Send Host Message request and reply. ++ * 05-25-11 02.00.18 For Extended Image Header, added ++ * MPI2_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC and ++ * MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC defines. ++ * Deprecated MPI2_EXT_IMAGE_TYPE_MAX define. ++ * 08-24-11 02.00.19 Added PhysicalPort field to ++ * MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE structure. ++ * Marked MPI2_PM_CONTROL_FEATURE_PCIE_LINK as obsolete. ++ * 11-18-11 02.00.20 Incorporating additions for MPI v2.5. ++ * 03-29-12 02.00.21 Added a product specific range to event values. ++ * 07-26-12 02.00.22 Added MPI2_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE. ++ * Added ElapsedSeconds field to ++ * MPI2_EVENT_DATA_IR_OPERATION_STATUS. ++ * 08-19-13 02.00.23 For IOCInit, added MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE ++ * and MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY. ++ * Added MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE. ++ * Added MPI2_FW_DOWNLOAD_ITYPE_PUBLIC_KEY. ++ * Added Encrypted Hash Extended Image. ++ * 12-05-13 02.00.24 Added MPI25_HASH_IMAGE_TYPE_BIOS. ++ * 11-18-14 02.00.25 Updated copyright information. ++ * 03-16-15 02.00.26 Updated for MPI v2.6. ++ * Added MPI2_EVENT_ACTIVE_CABLE_EXCEPTION and ++ * MPI26_EVENT_DATA_ACTIVE_CABLE_EXCEPT. ++ * Added MPI26_FW_HEADER_PID_FAMILY_3324_SAS and ++ * MPI26_FW_HEADER_PID_FAMILY_3516_SAS. ++ * Added MPI26_CTRL_OP_SHUTDOWN. ++ * 08-25-15 02.00.27 Added IC ARCH Class based signature defines ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_IOC_H ++#define MPI2_IOC_H ++ ++/***************************************************************************** ++* ++* IOC Messages ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* IOCInit message ++****************************************************************************/ ++ ++/*IOCInit Request message */ ++typedef struct _MPI2_IOC_INIT_REQUEST { ++ U8 WhoInit; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 MsgVersion; /*0x0C */ ++ U16 HeaderVersion; /*0x0E */ ++ U32 Reserved5; /*0x10 */ ++ U16 ConfigurationFlags; /* 0x14 */ ++ U8 HostPageSize; /*0x16 */ ++ U8 HostMSIxVectors; /*0x17 */ ++ U16 Reserved8; /*0x18 */ ++ U16 SystemRequestFrameSize; /*0x1A */ ++ U16 ReplyDescriptorPostQueueDepth; /*0x1C */ ++ U16 ReplyFreeQueueDepth; /*0x1E */ ++ U32 SenseBufferAddressHigh; /*0x20 */ ++ U32 SystemReplyAddressHigh; /*0x24 */ ++ U64 SystemRequestFrameBaseAddress; /*0x28 */ ++ U64 ReplyDescriptorPostQueueAddress; /*0x30 */ ++ U64 ReplyFreeQueueAddress; /*0x38 */ ++ U64 TimeStamp; /*0x40 */ ++} MPI2_IOC_INIT_REQUEST, *PTR_MPI2_IOC_INIT_REQUEST, ++ Mpi2IOCInitRequest_t, *pMpi2IOCInitRequest_t; ++ ++/*WhoInit values */ ++#define MPI2_WHOINIT_NOT_INITIALIZED (0x00) ++#define MPI2_WHOINIT_SYSTEM_BIOS (0x01) ++#define MPI2_WHOINIT_ROM_BIOS (0x02) ++#define MPI2_WHOINIT_PCI_PEER (0x03) ++#define MPI2_WHOINIT_HOST_DRIVER (0x04) ++#define MPI2_WHOINIT_MANUFACTURER (0x05) ++ ++/* MsgFlags */ ++#define MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE (0x01) ++ ++ ++/*MsgVersion */ ++#define MPI2_IOCINIT_MSGVERSION_MAJOR_MASK (0xFF00) ++#define MPI2_IOCINIT_MSGVERSION_MAJOR_SHIFT (8) ++#define MPI2_IOCINIT_MSGVERSION_MINOR_MASK (0x00FF) ++#define MPI2_IOCINIT_MSGVERSION_MINOR_SHIFT (0) ++ ++/*HeaderVersion */ ++#define MPI2_IOCINIT_HDRVERSION_UNIT_MASK (0xFF00) ++#define MPI2_IOCINIT_HDRVERSION_UNIT_SHIFT (8) ++#define MPI2_IOCINIT_HDRVERSION_DEV_MASK (0x00FF) ++#define MPI2_IOCINIT_HDRVERSION_DEV_SHIFT (0) ++ ++/*minimum depth for a Reply Descriptor Post Queue */ ++#define MPI2_RDPQ_DEPTH_MIN (16) ++ ++/* Reply Descriptor Post Queue Array Entry */ ++typedef struct _MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY { ++ U64 RDPQBaseAddress; /* 0x00 */ ++ U32 Reserved1; /* 0x08 */ ++ U32 Reserved2; /* 0x0C */ ++} MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY, ++*PTR_MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY, ++Mpi2IOCInitRDPQArrayEntry, *pMpi2IOCInitRDPQArrayEntry; ++ ++ ++/*IOCInit Reply message */ ++typedef struct _MPI2_IOC_INIT_REPLY { ++ U8 WhoInit; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_IOC_INIT_REPLY, *PTR_MPI2_IOC_INIT_REPLY, ++ Mpi2IOCInitReply_t, *pMpi2IOCInitReply_t; ++ ++/**************************************************************************** ++* IOCFacts message ++****************************************************************************/ ++ ++/*IOCFacts Request message */ ++typedef struct _MPI2_IOC_FACTS_REQUEST { ++ U16 Reserved1; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++} MPI2_IOC_FACTS_REQUEST, *PTR_MPI2_IOC_FACTS_REQUEST, ++ Mpi2IOCFactsRequest_t, *pMpi2IOCFactsRequest_t; ++ ++/*IOCFacts Reply message */ ++typedef struct _MPI2_IOC_FACTS_REPLY { ++ U16 MsgVersion; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 HeaderVersion; /*0x04 */ ++ U8 IOCNumber; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U16 IOCExceptions; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U8 MaxChainDepth; /*0x14 */ ++ U8 WhoInit; /*0x15 */ ++ U8 NumberOfPorts; /*0x16 */ ++ U8 MaxMSIxVectors; /*0x17 */ ++ U16 RequestCredit; /*0x18 */ ++ U16 ProductID; /*0x1A */ ++ U32 IOCCapabilities; /*0x1C */ ++ MPI2_VERSION_UNION FWVersion; /*0x20 */ ++ U16 IOCRequestFrameSize; /*0x24 */ ++ U16 IOCMaxChainSegmentSize; /*0x26 */ ++ U16 MaxInitiators; /*0x28 */ ++ U16 MaxTargets; /*0x2A */ ++ U16 MaxSasExpanders; /*0x2C */ ++ U16 MaxEnclosures; /*0x2E */ ++ U16 ProtocolFlags; /*0x30 */ ++ U16 HighPriorityCredit; /*0x32 */ ++ U16 MaxReplyDescriptorPostQueueDepth; /*0x34 */ ++ U8 ReplyFrameSize; /*0x36 */ ++ U8 MaxVolumes; /*0x37 */ ++ U16 MaxDevHandle; /*0x38 */ ++ U16 MaxPersistentEntries; /*0x3A */ ++ U16 MinDevHandle; /*0x3C */ ++ U8 CurrentHostPageSize; /* 0x3E */ ++ U8 Reserved4; /* 0x3F */ ++} MPI2_IOC_FACTS_REPLY, *PTR_MPI2_IOC_FACTS_REPLY, ++ Mpi2IOCFactsReply_t, *pMpi2IOCFactsReply_t; ++ ++/*MsgVersion */ ++#define MPI2_IOCFACTS_MSGVERSION_MAJOR_MASK (0xFF00) ++#define MPI2_IOCFACTS_MSGVERSION_MAJOR_SHIFT (8) ++#define MPI2_IOCFACTS_MSGVERSION_MINOR_MASK (0x00FF) ++#define MPI2_IOCFACTS_MSGVERSION_MINOR_SHIFT (0) ++ ++/*HeaderVersion */ ++#define MPI2_IOCFACTS_HDRVERSION_UNIT_MASK (0xFF00) ++#define MPI2_IOCFACTS_HDRVERSION_UNIT_SHIFT (8) ++#define MPI2_IOCFACTS_HDRVERSION_DEV_MASK (0x00FF) ++#define MPI2_IOCFACTS_HDRVERSION_DEV_SHIFT (0) ++ ++/*IOCExceptions */ ++#define MPI2_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE (0x0200) ++#define MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX (0x0100) ++ ++#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_MASK (0x00E0) ++#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_GOOD (0x0000) ++#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_BACKUP (0x0020) ++#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_RESTORED (0x0040) ++#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_CORRUPT_BACKUP (0x0060) ++ ++#define MPI2_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED (0x0010) ++#define MPI2_IOCFACTS_EXCEPT_MANUFACT_CHECKSUM_FAIL (0x0008) ++#define MPI2_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL (0x0004) ++#define MPI2_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID (0x0002) ++#define MPI2_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (0x0001) ++ ++/*defines for WhoInit field are after the IOCInit Request */ ++ ++/*ProductID field uses MPI2_FW_HEADER_PID_ */ ++ ++/*IOCCapabilities */ ++#define MPI26_IOCFACTS_CAPABILITY_ATOMIC_REQ (0x00080000) ++#define MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE (0x00040000) ++#define MPI25_IOCFACTS_CAPABILITY_FAST_PATH_CAPABLE (0x00020000) ++#define MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY (0x00010000) ++#define MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX (0x00008000) ++#define MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR (0x00004000) ++#define MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY (0x00002000) ++#define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID (0x00001000) ++#define MPI2_IOCFACTS_CAPABILITY_TLR (0x00000800) ++#define MPI2_IOCFACTS_CAPABILITY_MULTICAST (0x00000100) ++#define MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET (0x00000080) ++#define MPI2_IOCFACTS_CAPABILITY_EEDP (0x00000040) ++#define MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER (0x00000020) ++#define MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER (0x00000010) ++#define MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER (0x00000008) ++#define MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING (0x00000004) ++ ++/*ProtocolFlags */ ++#define MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR (0x0002) ++#define MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET (0x0001) ++ ++/**************************************************************************** ++* PortFacts message ++****************************************************************************/ ++ ++/*PortFacts Request message */ ++typedef struct _MPI2_PORT_FACTS_REQUEST { ++ U16 Reserved1; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 PortNumber; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++} MPI2_PORT_FACTS_REQUEST, *PTR_MPI2_PORT_FACTS_REQUEST, ++ Mpi2PortFactsRequest_t, *pMpi2PortFactsRequest_t; ++ ++/*PortFacts Reply message */ ++typedef struct _MPI2_PORT_FACTS_REPLY { ++ U16 Reserved1; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 PortNumber; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U16 Reserved4; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U8 Reserved5; /*0x14 */ ++ U8 PortType; /*0x15 */ ++ U16 Reserved6; /*0x16 */ ++ U16 MaxPostedCmdBuffers; /*0x18 */ ++ U16 Reserved7; /*0x1A */ ++} MPI2_PORT_FACTS_REPLY, *PTR_MPI2_PORT_FACTS_REPLY, ++ Mpi2PortFactsReply_t, *pMpi2PortFactsReply_t; ++ ++/*PortType values */ ++#define MPI2_PORTFACTS_PORTTYPE_INACTIVE (0x00) ++#define MPI2_PORTFACTS_PORTTYPE_FC (0x10) ++#define MPI2_PORTFACTS_PORTTYPE_ISCSI (0x20) ++#define MPI2_PORTFACTS_PORTTYPE_SAS_PHYSICAL (0x30) ++#define MPI2_PORTFACTS_PORTTYPE_SAS_VIRTUAL (0x31) ++ ++/**************************************************************************** ++* PortEnable message ++****************************************************************************/ ++ ++/*PortEnable Request message */ ++typedef struct _MPI2_PORT_ENABLE_REQUEST { ++ U16 Reserved1; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U8 Reserved2; /*0x04 */ ++ U8 PortFlags; /*0x05 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++} MPI2_PORT_ENABLE_REQUEST, *PTR_MPI2_PORT_ENABLE_REQUEST, ++ Mpi2PortEnableRequest_t, *pMpi2PortEnableRequest_t; ++ ++/*PortEnable Reply message */ ++typedef struct _MPI2_PORT_ENABLE_REPLY { ++ U16 Reserved1; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U8 Reserved2; /*0x04 */ ++ U8 PortFlags; /*0x05 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_PORT_ENABLE_REPLY, *PTR_MPI2_PORT_ENABLE_REPLY, ++ Mpi2PortEnableReply_t, *pMpi2PortEnableReply_t; ++ ++/**************************************************************************** ++* EventNotification message ++****************************************************************************/ ++ ++/*EventNotification Request message */ ++#define MPI2_EVENT_NOTIFY_EVENTMASK_WORDS (4) ++ ++typedef struct _MPI2_EVENT_NOTIFICATION_REQUEST { ++ U16 Reserved1; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 Reserved5; /*0x0C */ ++ U32 Reserved6; /*0x10 */ ++ U32 EventMasks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; /*0x14 */ ++ U16 SASBroadcastPrimitiveMasks; /*0x24 */ ++ U16 SASNotifyPrimitiveMasks; /*0x26 */ ++ U32 Reserved8; /*0x28 */ ++} MPI2_EVENT_NOTIFICATION_REQUEST, ++ *PTR_MPI2_EVENT_NOTIFICATION_REQUEST, ++ Mpi2EventNotificationRequest_t, ++ *pMpi2EventNotificationRequest_t; ++ ++/*EventNotification Reply message */ ++typedef struct _MPI2_EVENT_NOTIFICATION_REPLY { ++ U16 EventDataLength; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved1; /*0x04 */ ++ U8 AckRequired; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved2; /*0x0A */ ++ U16 Reserved3; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U16 Event; /*0x14 */ ++ U16 Reserved4; /*0x16 */ ++ U32 EventContext; /*0x18 */ ++ U32 EventData[1]; /*0x1C */ ++} MPI2_EVENT_NOTIFICATION_REPLY, *PTR_MPI2_EVENT_NOTIFICATION_REPLY, ++ Mpi2EventNotificationReply_t, ++ *pMpi2EventNotificationReply_t; ++ ++/*AckRequired */ ++#define MPI2_EVENT_NOTIFICATION_ACK_NOT_REQUIRED (0x00) ++#define MPI2_EVENT_NOTIFICATION_ACK_REQUIRED (0x01) ++ ++/*Event */ ++#define MPI2_EVENT_LOG_DATA (0x0001) ++#define MPI2_EVENT_STATE_CHANGE (0x0002) ++#define MPI2_EVENT_HARD_RESET_RECEIVED (0x0005) ++#define MPI2_EVENT_EVENT_CHANGE (0x000A) ++#define MPI2_EVENT_TASK_SET_FULL (0x000E) /*obsolete */ ++#define MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE (0x000F) ++#define MPI2_EVENT_IR_OPERATION_STATUS (0x0014) ++#define MPI2_EVENT_SAS_DISCOVERY (0x0016) ++#define MPI2_EVENT_SAS_BROADCAST_PRIMITIVE (0x0017) ++#define MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE (0x0018) ++#define MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW (0x0019) ++#define MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST (0x001C) ++#define MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE (0x001D) ++#define MPI2_EVENT_IR_VOLUME (0x001E) ++#define MPI2_EVENT_IR_PHYSICAL_DISK (0x001F) ++#define MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST (0x0020) ++#define MPI2_EVENT_LOG_ENTRY_ADDED (0x0021) ++#define MPI2_EVENT_SAS_PHY_COUNTER (0x0022) ++#define MPI2_EVENT_GPIO_INTERRUPT (0x0023) ++#define MPI2_EVENT_HOST_BASED_DISCOVERY_PHY (0x0024) ++#define MPI2_EVENT_SAS_QUIESCE (0x0025) ++#define MPI2_EVENT_SAS_NOTIFY_PRIMITIVE (0x0026) ++#define MPI2_EVENT_TEMP_THRESHOLD (0x0027) ++#define MPI2_EVENT_HOST_MESSAGE (0x0028) ++#define MPI2_EVENT_POWER_PERFORMANCE_CHANGE (0x0029) ++#define MPI2_EVENT_ACTIVE_CABLE_EXCEPTION (0x0034) ++#define MPI2_EVENT_MIN_PRODUCT_SPECIFIC (0x006E) ++#define MPI2_EVENT_MAX_PRODUCT_SPECIFIC (0x007F) ++ ++/*Log Entry Added Event data */ ++ ++/*the following structure matches MPI2_LOG_0_ENTRY in mpi2_cnfg.h */ ++#define MPI2_EVENT_DATA_LOG_DATA_LENGTH (0x1C) ++ ++typedef struct _MPI2_EVENT_DATA_LOG_ENTRY_ADDED { ++ U64 TimeStamp; /*0x00 */ ++ U32 Reserved1; /*0x08 */ ++ U16 LogSequence; /*0x0C */ ++ U16 LogEntryQualifier; /*0x0E */ ++ U8 VP_ID; /*0x10 */ ++ U8 VF_ID; /*0x11 */ ++ U16 Reserved2; /*0x12 */ ++ U8 LogData[MPI2_EVENT_DATA_LOG_DATA_LENGTH]; /*0x14 */ ++} MPI2_EVENT_DATA_LOG_ENTRY_ADDED, ++ *PTR_MPI2_EVENT_DATA_LOG_ENTRY_ADDED, ++ Mpi2EventDataLogEntryAdded_t, ++ *pMpi2EventDataLogEntryAdded_t; ++ ++/*GPIO Interrupt Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_GPIO_INTERRUPT { ++ U8 GPIONum; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++} MPI2_EVENT_DATA_GPIO_INTERRUPT, ++ *PTR_MPI2_EVENT_DATA_GPIO_INTERRUPT, ++ Mpi2EventDataGpioInterrupt_t, ++ *pMpi2EventDataGpioInterrupt_t; ++ ++/*Temperature Threshold Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_TEMPERATURE { ++ U16 Status; /*0x00 */ ++ U8 SensorNum; /*0x02 */ ++ U8 Reserved1; /*0x03 */ ++ U16 CurrentTemperature; /*0x04 */ ++ U16 Reserved2; /*0x06 */ ++ U32 Reserved3; /*0x08 */ ++ U32 Reserved4; /*0x0C */ ++} MPI2_EVENT_DATA_TEMPERATURE, ++ *PTR_MPI2_EVENT_DATA_TEMPERATURE, ++ Mpi2EventDataTemperature_t, *pMpi2EventDataTemperature_t; ++ ++/*Temperature Threshold Event data Status bits */ ++#define MPI2_EVENT_TEMPERATURE3_EXCEEDED (0x0008) ++#define MPI2_EVENT_TEMPERATURE2_EXCEEDED (0x0004) ++#define MPI2_EVENT_TEMPERATURE1_EXCEEDED (0x0002) ++#define MPI2_EVENT_TEMPERATURE0_EXCEEDED (0x0001) ++ ++/*Host Message Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_HOST_MESSAGE { ++ U8 SourceVF_ID; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U32 Reserved3; /*0x04 */ ++ U32 HostData[1]; /*0x08 */ ++} MPI2_EVENT_DATA_HOST_MESSAGE, *PTR_MPI2_EVENT_DATA_HOST_MESSAGE, ++ Mpi2EventDataHostMessage_t, *pMpi2EventDataHostMessage_t; ++ ++/*Power Performance Change Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_POWER_PERF_CHANGE { ++ U8 CurrentPowerMode; /*0x00 */ ++ U8 PreviousPowerMode; /*0x01 */ ++ U16 Reserved1; /*0x02 */ ++} MPI2_EVENT_DATA_POWER_PERF_CHANGE, ++ *PTR_MPI2_EVENT_DATA_POWER_PERF_CHANGE, ++ Mpi2EventDataPowerPerfChange_t, ++ *pMpi2EventDataPowerPerfChange_t; ++ ++/*defines for CurrentPowerMode and PreviousPowerMode fields */ ++#define MPI2_EVENT_PM_INIT_MASK (0xC0) ++#define MPI2_EVENT_PM_INIT_UNAVAILABLE (0x00) ++#define MPI2_EVENT_PM_INIT_HOST (0x40) ++#define MPI2_EVENT_PM_INIT_IO_UNIT (0x80) ++#define MPI2_EVENT_PM_INIT_PCIE_DPA (0xC0) ++ ++#define MPI2_EVENT_PM_MODE_MASK (0x07) ++#define MPI2_EVENT_PM_MODE_UNAVAILABLE (0x00) ++#define MPI2_EVENT_PM_MODE_UNKNOWN (0x01) ++#define MPI2_EVENT_PM_MODE_FULL_POWER (0x04) ++#define MPI2_EVENT_PM_MODE_REDUCED_POWER (0x05) ++#define MPI2_EVENT_PM_MODE_STANDBY (0x06) ++ ++/* Active Cable Exception Event data */ ++ ++typedef struct _MPI26_EVENT_DATA_ACTIVE_CABLE_EXCEPT { ++ U32 ActiveCablePowerRequirement; /* 0x00 */ ++ U8 ReasonCode; /* 0x04 */ ++ U8 ReceptacleID; /* 0x05 */ ++ U16 Reserved1; /* 0x06 */ ++} MPI26_EVENT_DATA_ACTIVE_CABLE_EXCEPT, ++ *PTR_MPI26_EVENT_DATA_ACTIVE_CABLE_EXCEPT, ++ Mpi26EventDataActiveCableExcept_t, ++ *pMpi26EventDataActiveCableExcept_t; ++ ++/* defines for ReasonCode field */ ++#define MPI26_EVENT_ACTIVE_CABLE_INSUFFICIENT_POWER (0x00) ++ ++/*Hard Reset Received Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_HARD_RESET_RECEIVED { ++ U8 Reserved1; /*0x00 */ ++ U8 Port; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++} MPI2_EVENT_DATA_HARD_RESET_RECEIVED, ++ *PTR_MPI2_EVENT_DATA_HARD_RESET_RECEIVED, ++ Mpi2EventDataHardResetReceived_t, ++ *pMpi2EventDataHardResetReceived_t; ++ ++/*Task Set Full Event data */ ++/* this event is obsolete */ ++ ++typedef struct _MPI2_EVENT_DATA_TASK_SET_FULL { ++ U16 DevHandle; /*0x00 */ ++ U16 CurrentDepth; /*0x02 */ ++} MPI2_EVENT_DATA_TASK_SET_FULL, *PTR_MPI2_EVENT_DATA_TASK_SET_FULL, ++ Mpi2EventDataTaskSetFull_t, *pMpi2EventDataTaskSetFull_t; ++ ++/*SAS Device Status Change Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE { ++ U16 TaskTag; /*0x00 */ ++ U8 ReasonCode; /*0x02 */ ++ U8 PhysicalPort; /*0x03 */ ++ U8 ASC; /*0x04 */ ++ U8 ASCQ; /*0x05 */ ++ U16 DevHandle; /*0x06 */ ++ U32 Reserved2; /*0x08 */ ++ U64 SASAddress; /*0x0C */ ++ U8 LUN[8]; /*0x14 */ ++} MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE, ++ *PTR_MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE, ++ Mpi2EventDataSasDeviceStatusChange_t, ++ *pMpi2EventDataSasDeviceStatusChange_t; ++ ++/*SAS Device Status Change Event data ReasonCode values */ ++#define MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA (0x05) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED (0x07) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET (0x08) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL (0x09) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL (0x0A) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL (0x0B) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL (0x0C) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION (0x0D) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET (0x0E) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL (0x0F) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE (0x10) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY (0x11) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY (0x12) ++ ++/*Integrated RAID Operation Status Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_IR_OPERATION_STATUS { ++ U16 VolDevHandle; /*0x00 */ ++ U16 Reserved1; /*0x02 */ ++ U8 RAIDOperation; /*0x04 */ ++ U8 PercentComplete; /*0x05 */ ++ U16 Reserved2; /*0x06 */ ++ U32 ElapsedSeconds; /*0x08 */ ++} MPI2_EVENT_DATA_IR_OPERATION_STATUS, ++ *PTR_MPI2_EVENT_DATA_IR_OPERATION_STATUS, ++ Mpi2EventDataIrOperationStatus_t, ++ *pMpi2EventDataIrOperationStatus_t; ++ ++/*Integrated RAID Operation Status Event data RAIDOperation values */ ++#define MPI2_EVENT_IR_RAIDOP_RESYNC (0x00) ++#define MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION (0x01) ++#define MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK (0x02) ++#define MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT (0x03) ++#define MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT (0x04) ++ ++/*Integrated RAID Volume Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_IR_VOLUME { ++ U16 VolDevHandle; /*0x00 */ ++ U8 ReasonCode; /*0x02 */ ++ U8 Reserved1; /*0x03 */ ++ U32 NewValue; /*0x04 */ ++ U32 PreviousValue; /*0x08 */ ++} MPI2_EVENT_DATA_IR_VOLUME, *PTR_MPI2_EVENT_DATA_IR_VOLUME, ++ Mpi2EventDataIrVolume_t, *pMpi2EventDataIrVolume_t; ++ ++/*Integrated RAID Volume Event data ReasonCode values */ ++#define MPI2_EVENT_IR_VOLUME_RC_SETTINGS_CHANGED (0x01) ++#define MPI2_EVENT_IR_VOLUME_RC_STATUS_FLAGS_CHANGED (0x02) ++#define MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED (0x03) ++ ++/*Integrated RAID Physical Disk Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_IR_PHYSICAL_DISK { ++ U16 Reserved1; /*0x00 */ ++ U8 ReasonCode; /*0x02 */ ++ U8 PhysDiskNum; /*0x03 */ ++ U16 PhysDiskDevHandle; /*0x04 */ ++ U16 Reserved2; /*0x06 */ ++ U16 Slot; /*0x08 */ ++ U16 EnclosureHandle; /*0x0A */ ++ U32 NewValue; /*0x0C */ ++ U32 PreviousValue; /*0x10 */ ++} MPI2_EVENT_DATA_IR_PHYSICAL_DISK, ++ *PTR_MPI2_EVENT_DATA_IR_PHYSICAL_DISK, ++ Mpi2EventDataIrPhysicalDisk_t, ++ *pMpi2EventDataIrPhysicalDisk_t; ++ ++/*Integrated RAID Physical Disk Event data ReasonCode values */ ++#define MPI2_EVENT_IR_PHYSDISK_RC_SETTINGS_CHANGED (0x01) ++#define MPI2_EVENT_IR_PHYSDISK_RC_STATUS_FLAGS_CHANGED (0x02) ++#define MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED (0x03) ++ ++/*Integrated RAID Configuration Change List Event data */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check NumElements at runtime. ++ */ ++#ifndef MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT ++#define MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT (1) ++#endif ++ ++typedef struct _MPI2_EVENT_IR_CONFIG_ELEMENT { ++ U16 ElementFlags; /*0x00 */ ++ U16 VolDevHandle; /*0x02 */ ++ U8 ReasonCode; /*0x04 */ ++ U8 PhysDiskNum; /*0x05 */ ++ U16 PhysDiskDevHandle; /*0x06 */ ++} MPI2_EVENT_IR_CONFIG_ELEMENT, *PTR_MPI2_EVENT_IR_CONFIG_ELEMENT, ++ Mpi2EventIrConfigElement_t, *pMpi2EventIrConfigElement_t; ++ ++/*IR Configuration Change List Event data ElementFlags values */ ++#define MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK (0x000F) ++#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT (0x0000) ++#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT (0x0001) ++#define MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT (0x0002) ++ ++/*IR Configuration Change List Event data ReasonCode values */ ++#define MPI2_EVENT_IR_CHANGE_RC_ADDED (0x01) ++#define MPI2_EVENT_IR_CHANGE_RC_REMOVED (0x02) ++#define MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE (0x03) ++#define MPI2_EVENT_IR_CHANGE_RC_HIDE (0x04) ++#define MPI2_EVENT_IR_CHANGE_RC_UNHIDE (0x05) ++#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED (0x06) ++#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED (0x07) ++#define MPI2_EVENT_IR_CHANGE_RC_PD_CREATED (0x08) ++#define MPI2_EVENT_IR_CHANGE_RC_PD_DELETED (0x09) ++ ++typedef struct _MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST { ++ U8 NumElements; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 Reserved2; /*0x02 */ ++ U8 ConfigNum; /*0x03 */ ++ U32 Flags; /*0x04 */ ++ MPI2_EVENT_IR_CONFIG_ELEMENT ++ ConfigElement[MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT];/*0x08 */ ++} MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST, ++ *PTR_MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST, ++ Mpi2EventDataIrConfigChangeList_t, ++ *pMpi2EventDataIrConfigChangeList_t; ++ ++/*IR Configuration Change List Event data Flags values */ ++#define MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG (0x00000001) ++ ++/*SAS Discovery Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_DISCOVERY { ++ U8 Flags; /*0x00 */ ++ U8 ReasonCode; /*0x01 */ ++ U8 PhysicalPort; /*0x02 */ ++ U8 Reserved1; /*0x03 */ ++ U32 DiscoveryStatus; /*0x04 */ ++} MPI2_EVENT_DATA_SAS_DISCOVERY, ++ *PTR_MPI2_EVENT_DATA_SAS_DISCOVERY, ++ Mpi2EventDataSasDiscovery_t, *pMpi2EventDataSasDiscovery_t; ++ ++/*SAS Discovery Event data Flags values */ ++#define MPI2_EVENT_SAS_DISC_DEVICE_CHANGE (0x02) ++#define MPI2_EVENT_SAS_DISC_IN_PROGRESS (0x01) ++ ++/*SAS Discovery Event data ReasonCode values */ ++#define MPI2_EVENT_SAS_DISC_RC_STARTED (0x01) ++#define MPI2_EVENT_SAS_DISC_RC_COMPLETED (0x02) ++ ++/*SAS Discovery Event data DiscoveryStatus values */ ++#define MPI2_EVENT_SAS_DISC_DS_MAX_ENCLOSURES_EXCEED (0x80000000) ++#define MPI2_EVENT_SAS_DISC_DS_MAX_EXPANDERS_EXCEED (0x40000000) ++#define MPI2_EVENT_SAS_DISC_DS_MAX_DEVICES_EXCEED (0x20000000) ++#define MPI2_EVENT_SAS_DISC_DS_MAX_TOPO_PHYS_EXCEED (0x10000000) ++#define MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR (0x08000000) ++#define MPI2_EVENT_SAS_DISC_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000) ++#define MPI2_EVENT_SAS_DISC_DS_EXP_MULTI_SUBTRACTIVE (0x00004000) ++#define MPI2_EVENT_SAS_DISC_DS_MULTI_PORT_DOMAIN (0x00002000) ++#define MPI2_EVENT_SAS_DISC_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000) ++#define MPI2_EVENT_SAS_DISC_DS_UNSUPPORTED_DEVICE (0x00000800) ++#define MPI2_EVENT_SAS_DISC_DS_TABLE_LINK (0x00000400) ++#define MPI2_EVENT_SAS_DISC_DS_SUBTRACTIVE_LINK (0x00000200) ++#define MPI2_EVENT_SAS_DISC_DS_SMP_CRC_ERROR (0x00000100) ++#define MPI2_EVENT_SAS_DISC_DS_SMP_FUNCTION_FAILED (0x00000080) ++#define MPI2_EVENT_SAS_DISC_DS_INDEX_NOT_EXIST (0x00000040) ++#define MPI2_EVENT_SAS_DISC_DS_OUT_ROUTE_ENTRIES (0x00000020) ++#define MPI2_EVENT_SAS_DISC_DS_SMP_TIMEOUT (0x00000010) ++#define MPI2_EVENT_SAS_DISC_DS_MULTIPLE_PORTS (0x00000004) ++#define MPI2_EVENT_SAS_DISC_DS_UNADDRESSABLE_DEVICE (0x00000002) ++#define MPI2_EVENT_SAS_DISC_DS_LOOP_DETECTED (0x00000001) ++ ++/*SAS Broadcast Primitive Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE { ++ U8 PhyNum; /*0x00 */ ++ U8 Port; /*0x01 */ ++ U8 PortWidth; /*0x02 */ ++ U8 Primitive; /*0x03 */ ++} MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE, ++ *PTR_MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE, ++ Mpi2EventDataSasBroadcastPrimitive_t, ++ *pMpi2EventDataSasBroadcastPrimitive_t; ++ ++/*defines for the Primitive field */ ++#define MPI2_EVENT_PRIMITIVE_CHANGE (0x01) ++#define MPI2_EVENT_PRIMITIVE_SES (0x02) ++#define MPI2_EVENT_PRIMITIVE_EXPANDER (0x03) ++#define MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT (0x04) ++#define MPI2_EVENT_PRIMITIVE_RESERVED3 (0x05) ++#define MPI2_EVENT_PRIMITIVE_RESERVED4 (0x06) ++#define MPI2_EVENT_PRIMITIVE_CHANGE0_RESERVED (0x07) ++#define MPI2_EVENT_PRIMITIVE_CHANGE1_RESERVED (0x08) ++ ++/*SAS Notify Primitive Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE { ++ U8 PhyNum; /*0x00 */ ++ U8 Port; /*0x01 */ ++ U8 Reserved1; /*0x02 */ ++ U8 Primitive; /*0x03 */ ++} MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE, ++ *PTR_MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE, ++ Mpi2EventDataSasNotifyPrimitive_t, ++ *pMpi2EventDataSasNotifyPrimitive_t; ++ ++/*defines for the Primitive field */ ++#define MPI2_EVENT_NOTIFY_ENABLE_SPINUP (0x01) ++#define MPI2_EVENT_NOTIFY_POWER_LOSS_EXPECTED (0x02) ++#define MPI2_EVENT_NOTIFY_RESERVED1 (0x03) ++#define MPI2_EVENT_NOTIFY_RESERVED2 (0x04) ++ ++/*SAS Initiator Device Status Change Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE { ++ U8 ReasonCode; /*0x00 */ ++ U8 PhysicalPort; /*0x01 */ ++ U16 DevHandle; /*0x02 */ ++ U64 SASAddress; /*0x04 */ ++} MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE, ++ *PTR_MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE, ++ Mpi2EventDataSasInitDevStatusChange_t, ++ *pMpi2EventDataSasInitDevStatusChange_t; ++ ++/*SAS Initiator Device Status Change event ReasonCode values */ ++#define MPI2_EVENT_SAS_INIT_RC_ADDED (0x01) ++#define MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING (0x02) ++ ++/*SAS Initiator Device Table Overflow Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW { ++ U16 MaxInit; /*0x00 */ ++ U16 CurrentInit; /*0x02 */ ++ U64 SASAddress; /*0x04 */ ++} MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW, ++ *PTR_MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW, ++ Mpi2EventDataSasInitTableOverflow_t, ++ *pMpi2EventDataSasInitTableOverflow_t; ++ ++/*SAS Topology Change List Event data */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check NumEntries at runtime. ++ */ ++#ifndef MPI2_EVENT_SAS_TOPO_PHY_COUNT ++#define MPI2_EVENT_SAS_TOPO_PHY_COUNT (1) ++#endif ++ ++typedef struct _MPI2_EVENT_SAS_TOPO_PHY_ENTRY { ++ U16 AttachedDevHandle; /*0x00 */ ++ U8 LinkRate; /*0x02 */ ++ U8 PhyStatus; /*0x03 */ ++} MPI2_EVENT_SAS_TOPO_PHY_ENTRY, *PTR_MPI2_EVENT_SAS_TOPO_PHY_ENTRY, ++ Mpi2EventSasTopoPhyEntry_t, *pMpi2EventSasTopoPhyEntry_t; ++ ++typedef struct _MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST { ++ U16 EnclosureHandle; /*0x00 */ ++ U16 ExpanderDevHandle; /*0x02 */ ++ U8 NumPhys; /*0x04 */ ++ U8 Reserved1; /*0x05 */ ++ U16 Reserved2; /*0x06 */ ++ U8 NumEntries; /*0x08 */ ++ U8 StartPhyNum; /*0x09 */ ++ U8 ExpStatus; /*0x0A */ ++ U8 PhysicalPort; /*0x0B */ ++ MPI2_EVENT_SAS_TOPO_PHY_ENTRY ++ PHY[MPI2_EVENT_SAS_TOPO_PHY_COUNT]; /*0x0C */ ++} MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST, ++ *PTR_MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST, ++ Mpi2EventDataSasTopologyChangeList_t, ++ *pMpi2EventDataSasTopologyChangeList_t; ++ ++/*values for the ExpStatus field */ ++#define MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER (0x00) ++#define MPI2_EVENT_SAS_TOPO_ES_ADDED (0x01) ++#define MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING (0x02) ++#define MPI2_EVENT_SAS_TOPO_ES_RESPONDING (0x03) ++#define MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING (0x04) ++ ++/*defines for the LinkRate field */ ++#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_MASK (0xF0) ++#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_SHIFT (4) ++#define MPI2_EVENT_SAS_TOPO_LR_PREV_MASK (0x0F) ++#define MPI2_EVENT_SAS_TOPO_LR_PREV_SHIFT (0) ++ ++#define MPI2_EVENT_SAS_TOPO_LR_UNKNOWN_LINK_RATE (0x00) ++#define MPI2_EVENT_SAS_TOPO_LR_PHY_DISABLED (0x01) ++#define MPI2_EVENT_SAS_TOPO_LR_NEGOTIATION_FAILED (0x02) ++#define MPI2_EVENT_SAS_TOPO_LR_SATA_OOB_COMPLETE (0x03) ++#define MPI2_EVENT_SAS_TOPO_LR_PORT_SELECTOR (0x04) ++#define MPI2_EVENT_SAS_TOPO_LR_SMP_RESET_IN_PROGRESS (0x05) ++#define MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY (0x06) ++#define MPI2_EVENT_SAS_TOPO_LR_RATE_1_5 (0x08) ++#define MPI2_EVENT_SAS_TOPO_LR_RATE_3_0 (0x09) ++#define MPI2_EVENT_SAS_TOPO_LR_RATE_6_0 (0x0A) ++#define MPI25_EVENT_SAS_TOPO_LR_RATE_12_0 (0x0B) ++ ++/*values for the PhyStatus field */ ++#define MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT (0x80) ++#define MPI2_EVENT_SAS_TOPO_PS_MULTIPLEX_CHANGE (0x10) ++/*values for the PhyStatus ReasonCode sub-field */ ++#define MPI2_EVENT_SAS_TOPO_RC_MASK (0x0F) ++#define MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED (0x01) ++#define MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING (0x02) ++#define MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED (0x03) ++#define MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE (0x04) ++#define MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING (0x05) ++ ++/*SAS Enclosure Device Status Change Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE { ++ U16 EnclosureHandle; /*0x00 */ ++ U8 ReasonCode; /*0x02 */ ++ U8 PhysicalPort; /*0x03 */ ++ U64 EnclosureLogicalID; /*0x04 */ ++ U16 NumSlots; /*0x0C */ ++ U16 StartSlot; /*0x0E */ ++ U32 PhyBits; /*0x10 */ ++} MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE, ++ *PTR_MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE, ++ Mpi2EventDataSasEnclDevStatusChange_t, ++ *pMpi2EventDataSasEnclDevStatusChange_t; ++ ++/*SAS Enclosure Device Status Change event ReasonCode values */ ++#define MPI2_EVENT_SAS_ENCL_RC_ADDED (0x01) ++#define MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING (0x02) ++ ++/*SAS PHY Counter Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_PHY_COUNTER { ++ U64 TimeStamp; /*0x00 */ ++ U32 Reserved1; /*0x08 */ ++ U8 PhyEventCode; /*0x0C */ ++ U8 PhyNum; /*0x0D */ ++ U16 Reserved2; /*0x0E */ ++ U32 PhyEventInfo; /*0x10 */ ++ U8 CounterType; /*0x14 */ ++ U8 ThresholdWindow; /*0x15 */ ++ U8 TimeUnits; /*0x16 */ ++ U8 Reserved3; /*0x17 */ ++ U32 EventThreshold; /*0x18 */ ++ U16 ThresholdFlags; /*0x1C */ ++ U16 Reserved4; /*0x1E */ ++} MPI2_EVENT_DATA_SAS_PHY_COUNTER, ++ *PTR_MPI2_EVENT_DATA_SAS_PHY_COUNTER, ++ Mpi2EventDataSasPhyCounter_t, ++ *pMpi2EventDataSasPhyCounter_t; ++ ++/*use MPI2_SASPHY3_EVENT_CODE_ values from mpi2_cnfg.h ++ *for the PhyEventCode field */ ++ ++/*use MPI2_SASPHY3_COUNTER_TYPE_ values from mpi2_cnfg.h ++ *for the CounterType field */ ++ ++/*use MPI2_SASPHY3_TIME_UNITS_ values from mpi2_cnfg.h ++ *for the TimeUnits field */ ++ ++/*use MPI2_SASPHY3_TFLAGS_ values from mpi2_cnfg.h ++ *for the ThresholdFlags field */ ++ ++/*SAS Quiesce Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_QUIESCE { ++ U8 ReasonCode; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U32 Reserved3; /*0x04 */ ++} MPI2_EVENT_DATA_SAS_QUIESCE, ++ *PTR_MPI2_EVENT_DATA_SAS_QUIESCE, ++ Mpi2EventDataSasQuiesce_t, *pMpi2EventDataSasQuiesce_t; ++ ++/*SAS Quiesce Event data ReasonCode values */ ++#define MPI2_EVENT_SAS_QUIESCE_RC_STARTED (0x01) ++#define MPI2_EVENT_SAS_QUIESCE_RC_COMPLETED (0x02) ++ ++/*Host Based Discovery Phy Event data */ ++ ++typedef struct _MPI2_EVENT_HBD_PHY_SAS { ++ U8 Flags; /*0x00 */ ++ U8 NegotiatedLinkRate; /*0x01 */ ++ U8 PhyNum; /*0x02 */ ++ U8 PhysicalPort; /*0x03 */ ++ U32 Reserved1; /*0x04 */ ++ U8 InitialFrame[28]; /*0x08 */ ++} MPI2_EVENT_HBD_PHY_SAS, *PTR_MPI2_EVENT_HBD_PHY_SAS, ++ Mpi2EventHbdPhySas_t, *pMpi2EventHbdPhySas_t; ++ ++/*values for the Flags field */ ++#define MPI2_EVENT_HBD_SAS_FLAGS_FRAME_VALID (0x02) ++#define MPI2_EVENT_HBD_SAS_FLAGS_SATA_FRAME (0x01) ++ ++/*use MPI2_SAS_NEG_LINK_RATE_ defines from mpi2_cnfg.h ++ *for the NegotiatedLinkRate field */ ++ ++typedef union _MPI2_EVENT_HBD_DESCRIPTOR { ++ MPI2_EVENT_HBD_PHY_SAS Sas; ++} MPI2_EVENT_HBD_DESCRIPTOR, *PTR_MPI2_EVENT_HBD_DESCRIPTOR, ++ Mpi2EventHbdDescriptor_t, *pMpi2EventHbdDescriptor_t; ++ ++typedef struct _MPI2_EVENT_DATA_HBD_PHY { ++ U8 DescriptorType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U32 Reserved3; /*0x04 */ ++ MPI2_EVENT_HBD_DESCRIPTOR Descriptor; /*0x08 */ ++} MPI2_EVENT_DATA_HBD_PHY, *PTR_MPI2_EVENT_DATA_HBD_PHY, ++ Mpi2EventDataHbdPhy_t, ++ *pMpi2EventDataMpi2EventDataHbdPhy_t; ++ ++/*values for the DescriptorType field */ ++#define MPI2_EVENT_HBD_DT_SAS (0x01) ++ ++/**************************************************************************** ++* EventAck message ++****************************************************************************/ ++ ++/*EventAck Request message */ ++typedef struct _MPI2_EVENT_ACK_REQUEST { ++ U16 Reserved1; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Event; /*0x0C */ ++ U16 Reserved5; /*0x0E */ ++ U32 EventContext; /*0x10 */ ++} MPI2_EVENT_ACK_REQUEST, *PTR_MPI2_EVENT_ACK_REQUEST, ++ Mpi2EventAckRequest_t, *pMpi2EventAckRequest_t; ++ ++/*EventAck Reply message */ ++typedef struct _MPI2_EVENT_ACK_REPLY { ++ U16 Reserved1; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_EVENT_ACK_REPLY, *PTR_MPI2_EVENT_ACK_REPLY, ++ Mpi2EventAckReply_t, *pMpi2EventAckReply_t; ++ ++/**************************************************************************** ++* SendHostMessage message ++****************************************************************************/ ++ ++/*SendHostMessage Request message */ ++typedef struct _MPI2_SEND_HOST_MESSAGE_REQUEST { ++ U16 HostDataLength; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved1; /*0x04 */ ++ U8 Reserved2; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U8 Reserved4; /*0x0C */ ++ U8 DestVF_ID; /*0x0D */ ++ U16 Reserved5; /*0x0E */ ++ U32 Reserved6; /*0x10 */ ++ U32 Reserved7; /*0x14 */ ++ U32 Reserved8; /*0x18 */ ++ U32 Reserved9; /*0x1C */ ++ U32 Reserved10; /*0x20 */ ++ U32 HostData[1]; /*0x24 */ ++} MPI2_SEND_HOST_MESSAGE_REQUEST, ++ *PTR_MPI2_SEND_HOST_MESSAGE_REQUEST, ++ Mpi2SendHostMessageRequest_t, ++ *pMpi2SendHostMessageRequest_t; ++ ++/*SendHostMessage Reply message */ ++typedef struct _MPI2_SEND_HOST_MESSAGE_REPLY { ++ U16 HostDataLength; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved1; /*0x04 */ ++ U8 Reserved2; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U16 Reserved4; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_SEND_HOST_MESSAGE_REPLY, *PTR_MPI2_SEND_HOST_MESSAGE_REPLY, ++ Mpi2SendHostMessageReply_t, *pMpi2SendHostMessageReply_t; ++ ++/**************************************************************************** ++* FWDownload message ++****************************************************************************/ ++ ++/*MPI v2.0 FWDownload Request message */ ++typedef struct _MPI2_FW_DOWNLOAD_REQUEST { ++ U8 ImageType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 TotalImageSize; /*0x0C */ ++ U32 Reserved5; /*0x10 */ ++ MPI2_MPI_SGE_UNION SGL; /*0x14 */ ++} MPI2_FW_DOWNLOAD_REQUEST, *PTR_MPI2_FW_DOWNLOAD_REQUEST, ++ Mpi2FWDownloadRequest, *pMpi2FWDownloadRequest; ++ ++#define MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT (0x01) ++ ++#define MPI2_FW_DOWNLOAD_ITYPE_FW (0x01) ++#define MPI2_FW_DOWNLOAD_ITYPE_BIOS (0x02) ++#define MPI2_FW_DOWNLOAD_ITYPE_MANUFACTURING (0x06) ++#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_1 (0x07) ++#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_2 (0x08) ++#define MPI2_FW_DOWNLOAD_ITYPE_MEGARAID (0x09) ++#define MPI2_FW_DOWNLOAD_ITYPE_COMPLETE (0x0A) ++#define MPI2_FW_DOWNLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B) ++#define MPI2_FW_DOWNLOAD_ITYPE_PUBLIC_KEY (0x0C) ++#define MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC (0xF0) ++ ++/*MPI v2.0 FWDownload TransactionContext Element */ ++typedef struct _MPI2_FW_DOWNLOAD_TCSGE { ++ U8 Reserved1; /*0x00 */ ++ U8 ContextSize; /*0x01 */ ++ U8 DetailsLength; /*0x02 */ ++ U8 Flags; /*0x03 */ ++ U32 Reserved2; /*0x04 */ ++ U32 ImageOffset; /*0x08 */ ++ U32 ImageSize; /*0x0C */ ++} MPI2_FW_DOWNLOAD_TCSGE, *PTR_MPI2_FW_DOWNLOAD_TCSGE, ++ Mpi2FWDownloadTCSGE_t, *pMpi2FWDownloadTCSGE_t; ++ ++/*MPI v2.5 FWDownload Request message */ ++typedef struct _MPI25_FW_DOWNLOAD_REQUEST { ++ U8 ImageType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 TotalImageSize; /*0x0C */ ++ U32 Reserved5; /*0x10 */ ++ U32 Reserved6; /*0x14 */ ++ U32 ImageOffset; /*0x18 */ ++ U32 ImageSize; /*0x1C */ ++ MPI25_SGE_IO_UNION SGL; /*0x20 */ ++} MPI25_FW_DOWNLOAD_REQUEST, *PTR_MPI25_FW_DOWNLOAD_REQUEST, ++ Mpi25FWDownloadRequest, *pMpi25FWDownloadRequest; ++ ++/*FWDownload Reply message */ ++typedef struct _MPI2_FW_DOWNLOAD_REPLY { ++ U8 ImageType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_FW_DOWNLOAD_REPLY, *PTR_MPI2_FW_DOWNLOAD_REPLY, ++ Mpi2FWDownloadReply_t, *pMpi2FWDownloadReply_t; ++ ++/**************************************************************************** ++* FWUpload message ++****************************************************************************/ ++ ++/*MPI v2.0 FWUpload Request message */ ++typedef struct _MPI2_FW_UPLOAD_REQUEST { ++ U8 ImageType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 Reserved5; /*0x0C */ ++ U32 Reserved6; /*0x10 */ ++ MPI2_MPI_SGE_UNION SGL; /*0x14 */ ++} MPI2_FW_UPLOAD_REQUEST, *PTR_MPI2_FW_UPLOAD_REQUEST, ++ Mpi2FWUploadRequest_t, *pMpi2FWUploadRequest_t; ++ ++#define MPI2_FW_UPLOAD_ITYPE_FW_CURRENT (0x00) ++#define MPI2_FW_UPLOAD_ITYPE_FW_FLASH (0x01) ++#define MPI2_FW_UPLOAD_ITYPE_BIOS_FLASH (0x02) ++#define MPI2_FW_UPLOAD_ITYPE_FW_BACKUP (0x05) ++#define MPI2_FW_UPLOAD_ITYPE_MANUFACTURING (0x06) ++#define MPI2_FW_UPLOAD_ITYPE_CONFIG_1 (0x07) ++#define MPI2_FW_UPLOAD_ITYPE_CONFIG_2 (0x08) ++#define MPI2_FW_UPLOAD_ITYPE_MEGARAID (0x09) ++#define MPI2_FW_UPLOAD_ITYPE_COMPLETE (0x0A) ++#define MPI2_FW_UPLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B) ++#define MPI2_FW_UPLOAD_ITYPE_CBB_BACKUP (0x0D) ++ ++/*MPI v2.0 FWUpload TransactionContext Element */ ++typedef struct _MPI2_FW_UPLOAD_TCSGE { ++ U8 Reserved1; /*0x00 */ ++ U8 ContextSize; /*0x01 */ ++ U8 DetailsLength; /*0x02 */ ++ U8 Flags; /*0x03 */ ++ U32 Reserved2; /*0x04 */ ++ U32 ImageOffset; /*0x08 */ ++ U32 ImageSize; /*0x0C */ ++} MPI2_FW_UPLOAD_TCSGE, *PTR_MPI2_FW_UPLOAD_TCSGE, ++ Mpi2FWUploadTCSGE_t, *pMpi2FWUploadTCSGE_t; ++ ++/*MPI v2.5 FWUpload Request message */ ++typedef struct _MPI25_FW_UPLOAD_REQUEST { ++ U8 ImageType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 Reserved5; /*0x0C */ ++ U32 Reserved6; /*0x10 */ ++ U32 Reserved7; /*0x14 */ ++ U32 ImageOffset; /*0x18 */ ++ U32 ImageSize; /*0x1C */ ++ MPI25_SGE_IO_UNION SGL; /*0x20 */ ++} MPI25_FW_UPLOAD_REQUEST, *PTR_MPI25_FW_UPLOAD_REQUEST, ++ Mpi25FWUploadRequest_t, *pMpi25FWUploadRequest_t; ++ ++/*FWUpload Reply message */ ++typedef struct _MPI2_FW_UPLOAD_REPLY { ++ U8 ImageType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U32 ActualImageSize; /*0x14 */ ++} MPI2_FW_UPLOAD_REPLY, *PTR_MPI2_FW_UPLOAD_REPLY, ++ Mpi2FWUploadReply_t, *pMPi2FWUploadReply_t; ++ ++/*FW Image Header */ ++typedef struct _MPI2_FW_IMAGE_HEADER { ++ U32 Signature; /*0x00 */ ++ U32 Signature0; /*0x04 */ ++ U32 Signature1; /*0x08 */ ++ U32 Signature2; /*0x0C */ ++ MPI2_VERSION_UNION MPIVersion; /*0x10 */ ++ MPI2_VERSION_UNION FWVersion; /*0x14 */ ++ MPI2_VERSION_UNION NVDATAVersion; /*0x18 */ ++ MPI2_VERSION_UNION PackageVersion; /*0x1C */ ++ U16 VendorID; /*0x20 */ ++ U16 ProductID; /*0x22 */ ++ U16 ProtocolFlags; /*0x24 */ ++ U16 Reserved26; /*0x26 */ ++ U32 IOCCapabilities; /*0x28 */ ++ U32 ImageSize; /*0x2C */ ++ U32 NextImageHeaderOffset; /*0x30 */ ++ U32 Checksum; /*0x34 */ ++ U32 Reserved38; /*0x38 */ ++ U32 Reserved3C; /*0x3C */ ++ U32 Reserved40; /*0x40 */ ++ U32 Reserved44; /*0x44 */ ++ U32 Reserved48; /*0x48 */ ++ U32 Reserved4C; /*0x4C */ ++ U32 Reserved50; /*0x50 */ ++ U32 Reserved54; /*0x54 */ ++ U32 Reserved58; /*0x58 */ ++ U32 Reserved5C; /*0x5C */ ++ U32 BootFlags; /*0x60 */ ++ U32 FirmwareVersionNameWhat; /*0x64 */ ++ U8 FirmwareVersionName[32]; /*0x68 */ ++ U32 VendorNameWhat; /*0x88 */ ++ U8 VendorName[32]; /*0x8C */ ++ U32 PackageNameWhat; /*0x88 */ ++ U8 PackageName[32]; /*0x8C */ ++ U32 ReservedD0; /*0xD0 */ ++ U32 ReservedD4; /*0xD4 */ ++ U32 ReservedD8; /*0xD8 */ ++ U32 ReservedDC; /*0xDC */ ++ U32 ReservedE0; /*0xE0 */ ++ U32 ReservedE4; /*0xE4 */ ++ U32 ReservedE8; /*0xE8 */ ++ U32 ReservedEC; /*0xEC */ ++ U32 ReservedF0; /*0xF0 */ ++ U32 ReservedF4; /*0xF4 */ ++ U32 ReservedF8; /*0xF8 */ ++ U32 ReservedFC; /*0xFC */ ++} MPI2_FW_IMAGE_HEADER, *PTR_MPI2_FW_IMAGE_HEADER, ++ Mpi2FWImageHeader_t, *pMpi2FWImageHeader_t; ++ ++/*Signature field */ ++#define MPI2_FW_HEADER_SIGNATURE_OFFSET (0x00) ++#define MPI2_FW_HEADER_SIGNATURE_MASK (0xFF000000) ++#define MPI2_FW_HEADER_SIGNATURE (0xEA000000) ++#define MPI26_FW_HEADER_SIGNATURE (0xEB000000) ++ ++/*Signature0 field */ ++#define MPI2_FW_HEADER_SIGNATURE0_OFFSET (0x04) ++#define MPI2_FW_HEADER_SIGNATURE0 (0x5AFAA55A) ++/* Last byte is defined by architecture */ ++#define MPI26_FW_HEADER_SIGNATURE0_BASE (0x5AEAA500) ++#define MPI26_FW_HEADER_SIGNATURE0_ARC_0 (0x5A) ++#define MPI26_FW_HEADER_SIGNATURE0_ARC_1 (0x00) ++#define MPI26_FW_HEADER_SIGNATURE0_ARC_2 (0x01) ++/* legacy (0x5AEAA55A) */ ++#define MPI26_FW_HEADER_SIGNATURE0 \ ++ (MPI26_FW_HEADER_SIGNATURE0_BASE+MPI26_FW_HEADER_SIGNATURE0_ARC_0) ++#define MPI26_FW_HEADER_SIGNATURE0_3516 \ ++ (MPI26_FW_HEADER_SIGNATURE0_BASE+MPI26_FW_HEADER_SIGNATURE0_ARC_1) ++ ++/*Signature1 field */ ++#define MPI2_FW_HEADER_SIGNATURE1_OFFSET (0x08) ++#define MPI2_FW_HEADER_SIGNATURE1 (0xA55AFAA5) ++#define MPI26_FW_HEADER_SIGNATURE1 (0xA55AEAA5) ++ ++/*Signature2 field */ ++#define MPI2_FW_HEADER_SIGNATURE2_OFFSET (0x0C) ++#define MPI2_FW_HEADER_SIGNATURE2 (0x5AA55AFA) ++#define MPI26_FW_HEADER_SIGNATURE2 (0x5AA55AEA) ++ ++/*defines for using the ProductID field */ ++#define MPI2_FW_HEADER_PID_TYPE_MASK (0xF000) ++#define MPI2_FW_HEADER_PID_TYPE_SAS (0x2000) ++ ++#define MPI2_FW_HEADER_PID_PROD_MASK (0x0F00) ++#define MPI2_FW_HEADER_PID_PROD_A (0x0000) ++#define MPI2_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI (0x0200) ++#define MPI2_FW_HEADER_PID_PROD_IR_SCSI (0x0700) ++ ++#define MPI2_FW_HEADER_PID_FAMILY_MASK (0x00FF) ++/*SAS ProductID Family bits */ ++#define MPI2_FW_HEADER_PID_FAMILY_2108_SAS (0x0013) ++#define MPI2_FW_HEADER_PID_FAMILY_2208_SAS (0x0014) ++#define MPI25_FW_HEADER_PID_FAMILY_3108_SAS (0x0021) ++#define MPI26_FW_HEADER_PID_FAMILY_3324_SAS (0x0028) ++#define MPI26_FW_HEADER_PID_FAMILY_3516_SAS (0x0031) ++ ++/*use MPI2_IOCFACTS_PROTOCOL_ defines for ProtocolFlags field */ ++ ++/*use MPI2_IOCFACTS_CAPABILITY_ defines for IOCCapabilities field */ ++ ++#define MPI2_FW_HEADER_IMAGESIZE_OFFSET (0x2C) ++#define MPI2_FW_HEADER_NEXTIMAGE_OFFSET (0x30) ++#define MPI26_FW_HEADER_BOOTFLAGS_OFFSET (0x60) ++#define MPI2_FW_HEADER_VERNMHWAT_OFFSET (0x64) ++ ++#define MPI2_FW_HEADER_WHAT_SIGNATURE (0x29232840) ++ ++#define MPI2_FW_HEADER_SIZE (0x100) ++ ++/*Extended Image Header */ ++typedef struct _MPI2_EXT_IMAGE_HEADER { ++ U8 ImageType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U32 Checksum; /*0x04 */ ++ U32 ImageSize; /*0x08 */ ++ U32 NextImageHeaderOffset; /*0x0C */ ++ U32 PackageVersion; /*0x10 */ ++ U32 Reserved3; /*0x14 */ ++ U32 Reserved4; /*0x18 */ ++ U32 Reserved5; /*0x1C */ ++ U8 IdentifyString[32]; /*0x20 */ ++} MPI2_EXT_IMAGE_HEADER, *PTR_MPI2_EXT_IMAGE_HEADER, ++ Mpi2ExtImageHeader_t, *pMpi2ExtImageHeader_t; ++ ++/*useful offsets */ ++#define MPI2_EXT_IMAGE_IMAGETYPE_OFFSET (0x00) ++#define MPI2_EXT_IMAGE_IMAGESIZE_OFFSET (0x08) ++#define MPI2_EXT_IMAGE_NEXTIMAGE_OFFSET (0x0C) ++ ++#define MPI2_EXT_IMAGE_HEADER_SIZE (0x40) ++ ++/*defines for the ImageType field */ ++#define MPI2_EXT_IMAGE_TYPE_UNSPECIFIED (0x00) ++#define MPI2_EXT_IMAGE_TYPE_FW (0x01) ++#define MPI2_EXT_IMAGE_TYPE_NVDATA (0x03) ++#define MPI2_EXT_IMAGE_TYPE_BOOTLOADER (0x04) ++#define MPI2_EXT_IMAGE_TYPE_INITIALIZATION (0x05) ++#define MPI2_EXT_IMAGE_TYPE_FLASH_LAYOUT (0x06) ++#define MPI2_EXT_IMAGE_TYPE_SUPPORTED_DEVICES (0x07) ++#define MPI2_EXT_IMAGE_TYPE_MEGARAID (0x08) ++#define MPI2_EXT_IMAGE_TYPE_ENCRYPTED_HASH (0x09) ++#define MPI2_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC (0x80) ++#define MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC (0xFF) ++ ++#define MPI2_EXT_IMAGE_TYPE_MAX (MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC) ++ ++/*FLASH Layout Extended Image Data */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check RegionsPerLayout at runtime. ++ */ ++#ifndef MPI2_FLASH_NUMBER_OF_REGIONS ++#define MPI2_FLASH_NUMBER_OF_REGIONS (1) ++#endif ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check NumberOfLayouts at runtime. ++ */ ++#ifndef MPI2_FLASH_NUMBER_OF_LAYOUTS ++#define MPI2_FLASH_NUMBER_OF_LAYOUTS (1) ++#endif ++ ++typedef struct _MPI2_FLASH_REGION { ++ U8 RegionType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U32 RegionOffset; /*0x04 */ ++ U32 RegionSize; /*0x08 */ ++ U32 Reserved3; /*0x0C */ ++} MPI2_FLASH_REGION, *PTR_MPI2_FLASH_REGION, ++ Mpi2FlashRegion_t, *pMpi2FlashRegion_t; ++ ++typedef struct _MPI2_FLASH_LAYOUT { ++ U32 FlashSize; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 Reserved2; /*0x08 */ ++ U32 Reserved3; /*0x0C */ ++ MPI2_FLASH_REGION Region[MPI2_FLASH_NUMBER_OF_REGIONS]; /*0x10 */ ++} MPI2_FLASH_LAYOUT, *PTR_MPI2_FLASH_LAYOUT, ++ Mpi2FlashLayout_t, *pMpi2FlashLayout_t; ++ ++typedef struct _MPI2_FLASH_LAYOUT_DATA { ++ U8 ImageRevision; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 SizeOfRegion; /*0x02 */ ++ U8 Reserved2; /*0x03 */ ++ U16 NumberOfLayouts; /*0x04 */ ++ U16 RegionsPerLayout; /*0x06 */ ++ U16 MinimumSectorAlignment; /*0x08 */ ++ U16 Reserved3; /*0x0A */ ++ U32 Reserved4; /*0x0C */ ++ MPI2_FLASH_LAYOUT Layout[MPI2_FLASH_NUMBER_OF_LAYOUTS]; /*0x10 */ ++} MPI2_FLASH_LAYOUT_DATA, *PTR_MPI2_FLASH_LAYOUT_DATA, ++ Mpi2FlashLayoutData_t, *pMpi2FlashLayoutData_t; ++ ++/*defines for the RegionType field */ ++#define MPI2_FLASH_REGION_UNUSED (0x00) ++#define MPI2_FLASH_REGION_FIRMWARE (0x01) ++#define MPI2_FLASH_REGION_BIOS (0x02) ++#define MPI2_FLASH_REGION_NVDATA (0x03) ++#define MPI2_FLASH_REGION_FIRMWARE_BACKUP (0x05) ++#define MPI2_FLASH_REGION_MFG_INFORMATION (0x06) ++#define MPI2_FLASH_REGION_CONFIG_1 (0x07) ++#define MPI2_FLASH_REGION_CONFIG_2 (0x08) ++#define MPI2_FLASH_REGION_MEGARAID (0x09) ++#define MPI2_FLASH_REGION_COMMON_BOOT_BLOCK (0x0A) ++#define MPI2_FLASH_REGION_INIT (MPI2_FLASH_REGION_COMMON_BOOT_BLOCK) ++#define MPI2_FLASH_REGION_CBB_BACKUP (0x0D) ++ ++/*ImageRevision */ ++#define MPI2_FLASH_LAYOUT_IMAGE_REVISION (0x00) ++ ++/*Supported Devices Extended Image Data */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check NumberOfDevices at runtime. ++ */ ++#ifndef MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES ++#define MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES (1) ++#endif ++ ++typedef struct _MPI2_SUPPORTED_DEVICE { ++ U16 DeviceID; /*0x00 */ ++ U16 VendorID; /*0x02 */ ++ U16 DeviceIDMask; /*0x04 */ ++ U16 Reserved1; /*0x06 */ ++ U8 LowPCIRev; /*0x08 */ ++ U8 HighPCIRev; /*0x09 */ ++ U16 Reserved2; /*0x0A */ ++ U32 Reserved3; /*0x0C */ ++} MPI2_SUPPORTED_DEVICE, *PTR_MPI2_SUPPORTED_DEVICE, ++ Mpi2SupportedDevice_t, *pMpi2SupportedDevice_t; ++ ++typedef struct _MPI2_SUPPORTED_DEVICES_DATA { ++ U8 ImageRevision; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 NumberOfDevices; /*0x02 */ ++ U8 Reserved2; /*0x03 */ ++ U32 Reserved3; /*0x04 */ ++ MPI2_SUPPORTED_DEVICE ++ SupportedDevice[MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES];/*0x08 */ ++} MPI2_SUPPORTED_DEVICES_DATA, *PTR_MPI2_SUPPORTED_DEVICES_DATA, ++ Mpi2SupportedDevicesData_t, *pMpi2SupportedDevicesData_t; ++ ++/*ImageRevision */ ++#define MPI2_SUPPORTED_DEVICES_IMAGE_REVISION (0x00) ++ ++/*Init Extended Image Data */ ++ ++typedef struct _MPI2_INIT_IMAGE_FOOTER { ++ U32 BootFlags; /*0x00 */ ++ U32 ImageSize; /*0x04 */ ++ U32 Signature0; /*0x08 */ ++ U32 Signature1; /*0x0C */ ++ U32 Signature2; /*0x10 */ ++ U32 ResetVector; /*0x14 */ ++} MPI2_INIT_IMAGE_FOOTER, *PTR_MPI2_INIT_IMAGE_FOOTER, ++ Mpi2InitImageFooter_t, *pMpi2InitImageFooter_t; ++ ++/*defines for the BootFlags field */ ++#define MPI2_INIT_IMAGE_BOOTFLAGS_OFFSET (0x00) ++ ++/*defines for the ImageSize field */ ++#define MPI2_INIT_IMAGE_IMAGESIZE_OFFSET (0x04) ++ ++/*defines for the Signature0 field */ ++#define MPI2_INIT_IMAGE_SIGNATURE0_OFFSET (0x08) ++#define MPI2_INIT_IMAGE_SIGNATURE0 (0x5AA55AEA) ++ ++/*defines for the Signature1 field */ ++#define MPI2_INIT_IMAGE_SIGNATURE1_OFFSET (0x0C) ++#define MPI2_INIT_IMAGE_SIGNATURE1 (0xA55AEAA5) ++ ++/*defines for the Signature2 field */ ++#define MPI2_INIT_IMAGE_SIGNATURE2_OFFSET (0x10) ++#define MPI2_INIT_IMAGE_SIGNATURE2 (0x5AEAA55A) ++ ++/*Signature fields as individual bytes */ ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_0 (0xEA) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_1 (0x5A) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_2 (0xA5) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_3 (0x5A) ++ ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_4 (0xA5) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_5 (0xEA) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_6 (0x5A) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_7 (0xA5) ++ ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_8 (0x5A) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_9 (0xA5) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_A (0xEA) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_B (0x5A) ++ ++/*defines for the ResetVector field */ ++#define MPI2_INIT_IMAGE_RESETVECTOR_OFFSET (0x14) ++ ++ ++/* Encrypted Hash Extended Image Data */ ++ ++typedef struct _MPI25_ENCRYPTED_HASH_ENTRY { ++ U8 HashImageType; /* 0x00 */ ++ U8 HashAlgorithm; /* 0x01 */ ++ U8 EncryptionAlgorithm; /* 0x02 */ ++ U8 Reserved1; /* 0x03 */ ++ U32 Reserved2; /* 0x04 */ ++ U32 EncryptedHash[1]; /* 0x08 */ /* variable length */ ++} MPI25_ENCRYPTED_HASH_ENTRY, *PTR_MPI25_ENCRYPTED_HASH_ENTRY, ++Mpi25EncryptedHashEntry_t, *pMpi25EncryptedHashEntry_t; ++ ++/* values for HashImageType */ ++#define MPI25_HASH_IMAGE_TYPE_UNUSED (0x00) ++#define MPI25_HASH_IMAGE_TYPE_FIRMWARE (0x01) ++#define MPI25_HASH_IMAGE_TYPE_BIOS (0x02) ++ ++/* values for HashAlgorithm */ ++#define MPI25_HASH_ALGORITHM_UNUSED (0x00) ++#define MPI25_HASH_ALGORITHM_SHA256 (0x01) ++ ++/* values for EncryptionAlgorithm */ ++#define MPI25_ENCRYPTION_ALG_UNUSED (0x00) ++#define MPI25_ENCRYPTION_ALG_RSA256 (0x01) ++ ++typedef struct _MPI25_ENCRYPTED_HASH_DATA { ++ U8 ImageVersion; /* 0x00 */ ++ U8 NumHash; /* 0x01 */ ++ U16 Reserved1; /* 0x02 */ ++ U32 Reserved2; /* 0x04 */ ++ MPI25_ENCRYPTED_HASH_ENTRY EncryptedHashEntry[1]; /* 0x08 */ ++} MPI25_ENCRYPTED_HASH_DATA, *PTR_MPI25_ENCRYPTED_HASH_DATA, ++Mpi25EncryptedHashData_t, *pMpi25EncryptedHashData_t; ++ ++ ++/**************************************************************************** ++* PowerManagementControl message ++****************************************************************************/ ++ ++/*PowerManagementControl Request message */ ++typedef struct _MPI2_PWR_MGMT_CONTROL_REQUEST { ++ U8 Feature; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U8 Parameter1; /*0x0C */ ++ U8 Parameter2; /*0x0D */ ++ U8 Parameter3; /*0x0E */ ++ U8 Parameter4; /*0x0F */ ++ U32 Reserved5; /*0x10 */ ++ U32 Reserved6; /*0x14 */ ++} MPI2_PWR_MGMT_CONTROL_REQUEST, *PTR_MPI2_PWR_MGMT_CONTROL_REQUEST, ++ Mpi2PwrMgmtControlRequest_t, *pMpi2PwrMgmtControlRequest_t; ++ ++/*defines for the Feature field */ ++#define MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND (0x01) ++#define MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION (0x02) ++#define MPI2_PM_CONTROL_FEATURE_PCIE_LINK (0x03) /*obsolete */ ++#define MPI2_PM_CONTROL_FEATURE_IOC_SPEED (0x04) ++#define MPI2_PM_CONTROL_FEATURE_GLOBAL_PWR_MGMT_MODE (0x05) ++#define MPI2_PM_CONTROL_FEATURE_MIN_PRODUCT_SPECIFIC (0x80) ++#define MPI2_PM_CONTROL_FEATURE_MAX_PRODUCT_SPECIFIC (0xFF) ++ ++/*parameter usage for the MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND Feature */ ++/*Parameter1 contains a PHY number */ ++/*Parameter2 indicates power condition action using these defines */ ++#define MPI2_PM_CONTROL_PARAM2_PARTIAL (0x01) ++#define MPI2_PM_CONTROL_PARAM2_SLUMBER (0x02) ++#define MPI2_PM_CONTROL_PARAM2_EXIT_PWR_MGMT (0x03) ++/*Parameter3 and Parameter4 are reserved */ ++ ++/*parameter usage for the MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION ++ * Feature */ ++/*Parameter1 contains SAS port width modulation group number */ ++/*Parameter2 indicates IOC action using these defines */ ++#define MPI2_PM_CONTROL_PARAM2_REQUEST_OWNERSHIP (0x01) ++#define MPI2_PM_CONTROL_PARAM2_CHANGE_MODULATION (0x02) ++#define MPI2_PM_CONTROL_PARAM2_RELINQUISH_OWNERSHIP (0x03) ++/*Parameter3 indicates desired modulation level using these defines */ ++#define MPI2_PM_CONTROL_PARAM3_25_PERCENT (0x00) ++#define MPI2_PM_CONTROL_PARAM3_50_PERCENT (0x01) ++#define MPI2_PM_CONTROL_PARAM3_75_PERCENT (0x02) ++#define MPI2_PM_CONTROL_PARAM3_100_PERCENT (0x03) ++/*Parameter4 is reserved */ ++ ++/*this next set (_PCIE_LINK) is obsolete */ ++/*parameter usage for the MPI2_PM_CONTROL_FEATURE_PCIE_LINK Feature */ ++/*Parameter1 indicates desired PCIe link speed using these defines */ ++#define MPI2_PM_CONTROL_PARAM1_PCIE_2_5_GBPS (0x00) /*obsolete */ ++#define MPI2_PM_CONTROL_PARAM1_PCIE_5_0_GBPS (0x01) /*obsolete */ ++#define MPI2_PM_CONTROL_PARAM1_PCIE_8_0_GBPS (0x02) /*obsolete */ ++/*Parameter2 indicates desired PCIe link width using these defines */ ++#define MPI2_PM_CONTROL_PARAM2_WIDTH_X1 (0x01) /*obsolete */ ++#define MPI2_PM_CONTROL_PARAM2_WIDTH_X2 (0x02) /*obsolete */ ++#define MPI2_PM_CONTROL_PARAM2_WIDTH_X4 (0x04) /*obsolete */ ++#define MPI2_PM_CONTROL_PARAM2_WIDTH_X8 (0x08) /*obsolete */ ++/*Parameter3 and Parameter4 are reserved */ ++ ++/*parameter usage for the MPI2_PM_CONTROL_FEATURE_IOC_SPEED Feature */ ++/*Parameter1 indicates desired IOC hardware clock speed using these defines */ ++#define MPI2_PM_CONTROL_PARAM1_FULL_IOC_SPEED (0x01) ++#define MPI2_PM_CONTROL_PARAM1_HALF_IOC_SPEED (0x02) ++#define MPI2_PM_CONTROL_PARAM1_QUARTER_IOC_SPEED (0x04) ++#define MPI2_PM_CONTROL_PARAM1_EIGHTH_IOC_SPEED (0x08) ++/*Parameter2, Parameter3, and Parameter4 are reserved */ ++ ++/*parameter usage for the MPI2_PM_CONTROL_FEATURE_GLOBAL_PWR_MGMT_MODE Feature*/ ++/*Parameter1 indicates host action regarding global power management mode */ ++#define MPI2_PM_CONTROL_PARAM1_TAKE_CONTROL (0x01) ++#define MPI2_PM_CONTROL_PARAM1_CHANGE_GLOBAL_MODE (0x02) ++#define MPI2_PM_CONTROL_PARAM1_RELEASE_CONTROL (0x03) ++/*Parameter2 indicates the requested global power management mode */ ++#define MPI2_PM_CONTROL_PARAM2_FULL_PWR_PERF (0x01) ++#define MPI2_PM_CONTROL_PARAM2_REDUCED_PWR_PERF (0x08) ++#define MPI2_PM_CONTROL_PARAM2_STANDBY (0x40) ++/*Parameter3 and Parameter4 are reserved */ ++ ++/*PowerManagementControl Reply message */ ++typedef struct _MPI2_PWR_MGMT_CONTROL_REPLY { ++ U8 Feature; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_PWR_MGMT_CONTROL_REPLY, *PTR_MPI2_PWR_MGMT_CONTROL_REPLY, ++ Mpi2PwrMgmtControlReply_t, *pMpi2PwrMgmtControlReply_t; ++ ++/**************************************************************************** ++* IO Unit Control messages (MPI v2.6 and later only.) ++****************************************************************************/ ++ ++/* IO Unit Control Request Message */ ++typedef struct _MPI26_IOUNIT_CONTROL_REQUEST { ++ U8 Operation; /* 0x00 */ ++ U8 Reserved1; /* 0x01 */ ++ U8 ChainOffset; /* 0x02 */ ++ U8 Function; /* 0x03 */ ++ U16 DevHandle; /* 0x04 */ ++ U8 IOCParameter; /* 0x06 */ ++ U8 MsgFlags; /* 0x07 */ ++ U8 VP_ID; /* 0x08 */ ++ U8 VF_ID; /* 0x09 */ ++ U16 Reserved3; /* 0x0A */ ++ U16 Reserved4; /* 0x0C */ ++ U8 PhyNum; /* 0x0E */ ++ U8 PrimFlags; /* 0x0F */ ++ U32 Primitive; /* 0x10 */ ++ U8 LookupMethod; /* 0x14 */ ++ U8 Reserved5; /* 0x15 */ ++ U16 SlotNumber; /* 0x16 */ ++ U64 LookupAddress; /* 0x18 */ ++ U32 IOCParameterValue; /* 0x20 */ ++ U32 Reserved7; /* 0x24 */ ++ U32 Reserved8; /* 0x28 */ ++} MPI26_IOUNIT_CONTROL_REQUEST, ++ *PTR_MPI26_IOUNIT_CONTROL_REQUEST, ++ Mpi26IoUnitControlRequest_t, ++ *pMpi26IoUnitControlRequest_t; ++ ++/* values for the Operation field */ ++#define MPI26_CTRL_OP_CLEAR_ALL_PERSISTENT (0x02) ++#define MPI26_CTRL_OP_SAS_PHY_LINK_RESET (0x06) ++#define MPI26_CTRL_OP_SAS_PHY_HARD_RESET (0x07) ++#define MPI26_CTRL_OP_PHY_CLEAR_ERROR_LOG (0x08) ++#define MPI26_CTRL_OP_LINK_CLEAR_ERROR_LOG (0x09) ++#define MPI26_CTRL_OP_SAS_SEND_PRIMITIVE (0x0A) ++#define MPI26_CTRL_OP_FORCE_FULL_DISCOVERY (0x0B) ++#define MPI26_CTRL_OP_REMOVE_DEVICE (0x0D) ++#define MPI26_CTRL_OP_LOOKUP_MAPPING (0x0E) ++#define MPI26_CTRL_OP_SET_IOC_PARAMETER (0x0F) ++#define MPI26_CTRL_OP_ENABLE_FP_DEVICE (0x10) ++#define MPI26_CTRL_OP_DISABLE_FP_DEVICE (0x11) ++#define MPI26_CTRL_OP_ENABLE_FP_ALL (0x12) ++#define MPI26_CTRL_OP_DISABLE_FP_ALL (0x13) ++#define MPI26_CTRL_OP_DEV_ENABLE_NCQ (0x14) ++#define MPI26_CTRL_OP_DEV_DISABLE_NCQ (0x15) ++#define MPI26_CTRL_OP_SHUTDOWN (0x16) ++#define MPI26_CTRL_OP_DEV_ENABLE_PERSIST_CONNECTION (0x17) ++#define MPI26_CTRL_OP_DEV_DISABLE_PERSIST_CONNECTION (0x18) ++#define MPI26_CTRL_OP_DEV_CLOSE_PERSIST_CONNECTION (0x19) ++#define MPI26_CTRL_OP_PRODUCT_SPECIFIC_MIN (0x80) ++ ++/* values for the PrimFlags field */ ++#define MPI26_CTRL_PRIMFLAGS_SINGLE (0x08) ++#define MPI26_CTRL_PRIMFLAGS_TRIPLE (0x02) ++#define MPI26_CTRL_PRIMFLAGS_REDUNDANT (0x01) ++ ++/* values for the LookupMethod field */ ++#define MPI26_CTRL_LOOKUP_METHOD_WWID_ADDRESS (0x01) ++#define MPI26_CTRL_LOOKUP_METHOD_ENCLOSURE_SLOT (0x02) ++#define MPI26_CTRL_LOOKUP_METHOD_SAS_DEVICE_NAME (0x03) ++ ++ ++/* IO Unit Control Reply Message */ ++typedef struct _MPI26_IOUNIT_CONTROL_REPLY { ++ U8 Operation; /* 0x00 */ ++ U8 Reserved1; /* 0x01 */ ++ U8 MsgLength; /* 0x02 */ ++ U8 Function; /* 0x03 */ ++ U16 DevHandle; /* 0x04 */ ++ U8 IOCParameter; /* 0x06 */ ++ U8 MsgFlags; /* 0x07 */ ++ U8 VP_ID; /* 0x08 */ ++ U8 VF_ID; /* 0x09 */ ++ U16 Reserved3; /* 0x0A */ ++ U16 Reserved4; /* 0x0C */ ++ U16 IOCStatus; /* 0x0E */ ++ U32 IOCLogInfo; /* 0x10 */ ++} MPI26_IOUNIT_CONTROL_REPLY, ++ *PTR_MPI26_IOUNIT_CONTROL_REPLY, ++ Mpi26IoUnitControlReply_t, ++ *pMpi26IoUnitControlReply_t; ++ ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_raid.h b/drivers/scsi/mpt2sas/mpi/mpi2_raid.h +new file mode 100644 +index 0000000..1c0eeee +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2_raid.h +@@ -0,0 +1,355 @@ ++/* ++ * Copyright 2000-2014 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2_raid.h ++ * Title: MPI Integrated RAID messages and structures ++ * Creation Date: April 26, 2007 ++ * ++ * mpi2_raid.h Version: 02.00.11 ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 08-31-07 02.00.01 Modifications to RAID Action request and reply, ++ * including the Actions and ActionData. ++ * 02-29-08 02.00.02 Added MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD. ++ * 05-21-08 02.00.03 Added MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS so that ++ * the PhysDisk array in MPI2_RAID_VOLUME_CREATION_STRUCT ++ * can be sized by the build environment. ++ * 07-30-09 02.00.04 Added proper define for the Use Default Settings bit of ++ * VolumeCreationFlags and marked the old one as obsolete. ++ * 05-12-10 02.00.05 Added MPI2_RAID_VOL_FLAGS_OP_MDC define. ++ * 08-24-10 02.00.06 Added MPI2_RAID_ACTION_COMPATIBILITY_CHECK along with ++ * related structures and defines. ++ * Added product-specific range to RAID Action values. ++ * 11-18-11 02.00.07 Incorporating additions for MPI v2.5. ++ * 02-06-12 02.00.08 Added MPI2_RAID_ACTION_PHYSDISK_HIDDEN. ++ * 07-26-12 02.00.09 Added ElapsedSeconds field to MPI2_RAID_VOL_INDICATOR. ++ * Added MPI2_RAID_VOL_FLAGS_ELAPSED_SECONDS_VALID define. ++ * 04-17-13 02.00.10 Added MPI25_RAID_ACTION_ADATA_ALLOW_PI. ++ * 11-18-14 02.00.11 Updated copyright information. ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_RAID_H ++#define MPI2_RAID_H ++ ++/***************************************************************************** ++* ++* Integrated RAID Messages ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* RAID Action messages ++****************************************************************************/ ++ ++/* ActionDataWord defines for use with MPI2_RAID_ACTION_CREATE_VOLUME action */ ++#define MPI25_RAID_ACTION_ADATA_ALLOW_PI (0x80000000) ++ ++/*ActionDataWord defines for use with MPI2_RAID_ACTION_DELETE_VOLUME action */ ++#define MPI2_RAID_ACTION_ADATA_KEEP_LBA0 (0x00000000) ++#define MPI2_RAID_ACTION_ADATA_ZERO_LBA0 (0x00000001) ++ ++/*use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for ++ *MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */ ++ ++/*ActionDataWord defines for use with ++ *MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES action */ ++#define MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD (0x00000001) ++ ++/*ActionDataWord for MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE Action */ ++typedef struct _MPI2_RAID_ACTION_RATE_DATA { ++ U8 RateToChange; /*0x00 */ ++ U8 RateOrMode; /*0x01 */ ++ U16 DataScrubDuration; /*0x02 */ ++} MPI2_RAID_ACTION_RATE_DATA, *PTR_MPI2_RAID_ACTION_RATE_DATA, ++ Mpi2RaidActionRateData_t, *pMpi2RaidActionRateData_t; ++ ++#define MPI2_RAID_ACTION_SET_RATE_RESYNC (0x00) ++#define MPI2_RAID_ACTION_SET_RATE_DATA_SCRUB (0x01) ++#define MPI2_RAID_ACTION_SET_RATE_POWERSAVE_MODE (0x02) ++ ++/*ActionDataWord for MPI2_RAID_ACTION_START_RAID_FUNCTION Action */ ++typedef struct _MPI2_RAID_ACTION_START_RAID_FUNCTION { ++ U8 RAIDFunction; /*0x00 */ ++ U8 Flags; /*0x01 */ ++ U16 Reserved1; /*0x02 */ ++} MPI2_RAID_ACTION_START_RAID_FUNCTION, ++ *PTR_MPI2_RAID_ACTION_START_RAID_FUNCTION, ++ Mpi2RaidActionStartRaidFunction_t, ++ *pMpi2RaidActionStartRaidFunction_t; ++ ++/*defines for the RAIDFunction field */ ++#define MPI2_RAID_ACTION_START_BACKGROUND_INIT (0x00) ++#define MPI2_RAID_ACTION_START_ONLINE_CAP_EXPANSION (0x01) ++#define MPI2_RAID_ACTION_START_CONSISTENCY_CHECK (0x02) ++ ++/*defines for the Flags field */ ++#define MPI2_RAID_ACTION_START_NEW (0x00) ++#define MPI2_RAID_ACTION_START_RESUME (0x01) ++ ++/*ActionDataWord for MPI2_RAID_ACTION_STOP_RAID_FUNCTION Action */ ++typedef struct _MPI2_RAID_ACTION_STOP_RAID_FUNCTION { ++ U8 RAIDFunction; /*0x00 */ ++ U8 Flags; /*0x01 */ ++ U16 Reserved1; /*0x02 */ ++} MPI2_RAID_ACTION_STOP_RAID_FUNCTION, ++ *PTR_MPI2_RAID_ACTION_STOP_RAID_FUNCTION, ++ Mpi2RaidActionStopRaidFunction_t, ++ *pMpi2RaidActionStopRaidFunction_t; ++ ++/*defines for the RAIDFunction field */ ++#define MPI2_RAID_ACTION_STOP_BACKGROUND_INIT (0x00) ++#define MPI2_RAID_ACTION_STOP_ONLINE_CAP_EXPANSION (0x01) ++#define MPI2_RAID_ACTION_STOP_CONSISTENCY_CHECK (0x02) ++ ++/*defines for the Flags field */ ++#define MPI2_RAID_ACTION_STOP_ABORT (0x00) ++#define MPI2_RAID_ACTION_STOP_PAUSE (0x01) ++ ++/*ActionDataWord for MPI2_RAID_ACTION_CREATE_HOT_SPARE Action */ ++typedef struct _MPI2_RAID_ACTION_HOT_SPARE { ++ U8 HotSparePool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 DevHandle; /*0x02 */ ++} MPI2_RAID_ACTION_HOT_SPARE, *PTR_MPI2_RAID_ACTION_HOT_SPARE, ++ Mpi2RaidActionHotSpare_t, *pMpi2RaidActionHotSpare_t; ++ ++/*ActionDataWord for MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE Action */ ++typedef struct _MPI2_RAID_ACTION_FW_UPDATE_MODE { ++ U8 Flags; /*0x00 */ ++ U8 DeviceFirmwareUpdateModeTimeout; /*0x01 */ ++ U16 Reserved1; /*0x02 */ ++} MPI2_RAID_ACTION_FW_UPDATE_MODE, ++ *PTR_MPI2_RAID_ACTION_FW_UPDATE_MODE, ++ Mpi2RaidActionFwUpdateMode_t, ++ *pMpi2RaidActionFwUpdateMode_t; ++ ++/*ActionDataWord defines for use with ++ *MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE action */ ++#define MPI2_RAID_ACTION_ADATA_DISABLE_FW_UPDATE (0x00) ++#define MPI2_RAID_ACTION_ADATA_ENABLE_FW_UPDATE (0x01) ++ ++typedef union _MPI2_RAID_ACTION_DATA { ++ U32 Word; ++ MPI2_RAID_ACTION_RATE_DATA Rates; ++ MPI2_RAID_ACTION_START_RAID_FUNCTION StartRaidFunction; ++ MPI2_RAID_ACTION_STOP_RAID_FUNCTION StopRaidFunction; ++ MPI2_RAID_ACTION_HOT_SPARE HotSpare; ++ MPI2_RAID_ACTION_FW_UPDATE_MODE FwUpdateMode; ++} MPI2_RAID_ACTION_DATA, *PTR_MPI2_RAID_ACTION_DATA, ++ Mpi2RaidActionData_t, *pMpi2RaidActionData_t; ++ ++/*RAID Action Request Message */ ++typedef struct _MPI2_RAID_ACTION_REQUEST { ++ U8 Action; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 VolDevHandle; /*0x04 */ ++ U8 PhysDiskNum; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved2; /*0x0A */ ++ U32 Reserved3; /*0x0C */ ++ MPI2_RAID_ACTION_DATA ActionDataWord; /*0x10 */ ++ MPI2_SGE_SIMPLE_UNION ActionDataSGE; /*0x14 */ ++} MPI2_RAID_ACTION_REQUEST, *PTR_MPI2_RAID_ACTION_REQUEST, ++ Mpi2RaidActionRequest_t, *pMpi2RaidActionRequest_t; ++ ++/*RAID Action request Action values */ ++ ++#define MPI2_RAID_ACTION_INDICATOR_STRUCT (0x01) ++#define MPI2_RAID_ACTION_CREATE_VOLUME (0x02) ++#define MPI2_RAID_ACTION_DELETE_VOLUME (0x03) ++#define MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES (0x04) ++#define MPI2_RAID_ACTION_ENABLE_ALL_VOLUMES (0x05) ++#define MPI2_RAID_ACTION_PHYSDISK_OFFLINE (0x0A) ++#define MPI2_RAID_ACTION_PHYSDISK_ONLINE (0x0B) ++#define MPI2_RAID_ACTION_FAIL_PHYSDISK (0x0F) ++#define MPI2_RAID_ACTION_ACTIVATE_VOLUME (0x11) ++#define MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE (0x15) ++#define MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE (0x17) ++#define MPI2_RAID_ACTION_SET_VOLUME_NAME (0x18) ++#define MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE (0x19) ++#define MPI2_RAID_ACTION_ENABLE_FAILED_VOLUME (0x1C) ++#define MPI2_RAID_ACTION_CREATE_HOT_SPARE (0x1D) ++#define MPI2_RAID_ACTION_DELETE_HOT_SPARE (0x1E) ++#define MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED (0x20) ++#define MPI2_RAID_ACTION_START_RAID_FUNCTION (0x21) ++#define MPI2_RAID_ACTION_STOP_RAID_FUNCTION (0x22) ++#define MPI2_RAID_ACTION_COMPATIBILITY_CHECK (0x23) ++#define MPI2_RAID_ACTION_PHYSDISK_HIDDEN (0x24) ++#define MPI2_RAID_ACTION_MIN_PRODUCT_SPECIFIC (0x80) ++#define MPI2_RAID_ACTION_MAX_PRODUCT_SPECIFIC (0xFF) ++ ++/*RAID Volume Creation Structure */ ++ ++/* ++ *The following define can be customized for the targeted product. ++ */ ++#ifndef MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS ++#define MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS (1) ++#endif ++ ++typedef struct _MPI2_RAID_VOLUME_PHYSDISK { ++ U8 RAIDSetNum; /*0x00 */ ++ U8 PhysDiskMap; /*0x01 */ ++ U16 PhysDiskDevHandle; /*0x02 */ ++} MPI2_RAID_VOLUME_PHYSDISK, *PTR_MPI2_RAID_VOLUME_PHYSDISK, ++ Mpi2RaidVolumePhysDisk_t, *pMpi2RaidVolumePhysDisk_t; ++ ++/*defines for the PhysDiskMap field */ ++#define MPI2_RAIDACTION_PHYSDISK_PRIMARY (0x01) ++#define MPI2_RAIDACTION_PHYSDISK_SECONDARY (0x02) ++ ++typedef struct _MPI2_RAID_VOLUME_CREATION_STRUCT { ++ U8 NumPhysDisks; /*0x00 */ ++ U8 VolumeType; /*0x01 */ ++ U16 Reserved1; /*0x02 */ ++ U32 VolumeCreationFlags; /*0x04 */ ++ U32 VolumeSettings; /*0x08 */ ++ U8 Reserved2; /*0x0C */ ++ U8 ResyncRate; /*0x0D */ ++ U16 DataScrubDuration; /*0x0E */ ++ U64 VolumeMaxLBA; /*0x10 */ ++ U32 StripeSize; /*0x18 */ ++ U8 Name[16]; /*0x1C */ ++ MPI2_RAID_VOLUME_PHYSDISK ++ PhysDisk[MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS]; /*0x2C */ ++} MPI2_RAID_VOLUME_CREATION_STRUCT, ++ *PTR_MPI2_RAID_VOLUME_CREATION_STRUCT, ++ Mpi2RaidVolumeCreationStruct_t, ++ *pMpi2RaidVolumeCreationStruct_t; ++ ++/*use MPI2_RAID_VOL_TYPE_ defines from mpi2_cnfg.h for VolumeType */ ++ ++/*defines for the VolumeCreationFlags field */ ++#define MPI2_RAID_VOL_CREATION_DEFAULT_SETTINGS (0x80000000) ++#define MPI2_RAID_VOL_CREATION_BACKGROUND_INIT (0x00000004) ++#define MPI2_RAID_VOL_CREATION_LOW_LEVEL_INIT (0x00000002) ++#define MPI2_RAID_VOL_CREATION_MIGRATE_DATA (0x00000001) ++/*The following is an obsolete define. ++ *It must be shifted left 24 bits in order to set the proper bit. ++ */ ++#define MPI2_RAID_VOL_CREATION_USE_DEFAULT_SETTINGS (0x80) ++ ++/*RAID Online Capacity Expansion Structure */ ++ ++typedef struct _MPI2_RAID_ONLINE_CAPACITY_EXPANSION { ++ U32 Flags; /*0x00 */ ++ U16 DevHandle0; /*0x04 */ ++ U16 Reserved1; /*0x06 */ ++ U16 DevHandle1; /*0x08 */ ++ U16 Reserved2; /*0x0A */ ++} MPI2_RAID_ONLINE_CAPACITY_EXPANSION, ++ *PTR_MPI2_RAID_ONLINE_CAPACITY_EXPANSION, ++ Mpi2RaidOnlineCapacityExpansion_t, ++ *pMpi2RaidOnlineCapacityExpansion_t; ++ ++/*RAID Compatibility Input Structure */ ++ ++typedef struct _MPI2_RAID_COMPATIBILITY_INPUT_STRUCT { ++ U16 SourceDevHandle; /*0x00 */ ++ U16 CandidateDevHandle; /*0x02 */ ++ U32 Flags; /*0x04 */ ++ U32 Reserved1; /*0x08 */ ++ U32 Reserved2; /*0x0C */ ++} MPI2_RAID_COMPATIBILITY_INPUT_STRUCT, ++ *PTR_MPI2_RAID_COMPATIBILITY_INPUT_STRUCT, ++ Mpi2RaidCompatibilityInputStruct_t, ++ *pMpi2RaidCompatibilityInputStruct_t; ++ ++/*defines for RAID Compatibility Structure Flags field */ ++#define MPI2_RAID_COMPAT_SOURCE_IS_VOLUME_FLAG (0x00000002) ++#define MPI2_RAID_COMPAT_REPORT_SOURCE_INFO_FLAG (0x00000001) ++ ++/*RAID Volume Indicator Structure */ ++ ++typedef struct _MPI2_RAID_VOL_INDICATOR { ++ U64 TotalBlocks; /*0x00 */ ++ U64 BlocksRemaining; /*0x08 */ ++ U32 Flags; /*0x10 */ ++ U32 ElapsedSeconds; /* 0x14 */ ++} MPI2_RAID_VOL_INDICATOR, *PTR_MPI2_RAID_VOL_INDICATOR, ++ Mpi2RaidVolIndicator_t, *pMpi2RaidVolIndicator_t; ++ ++/*defines for RAID Volume Indicator Flags field */ ++#define MPI2_RAID_VOL_FLAGS_ELAPSED_SECONDS_VALID (0x80000000) ++#define MPI2_RAID_VOL_FLAGS_OP_MASK (0x0000000F) ++#define MPI2_RAID_VOL_FLAGS_OP_BACKGROUND_INIT (0x00000000) ++#define MPI2_RAID_VOL_FLAGS_OP_ONLINE_CAP_EXPANSION (0x00000001) ++#define MPI2_RAID_VOL_FLAGS_OP_CONSISTENCY_CHECK (0x00000002) ++#define MPI2_RAID_VOL_FLAGS_OP_RESYNC (0x00000003) ++#define MPI2_RAID_VOL_FLAGS_OP_MDC (0x00000004) ++ ++/*RAID Compatibility Result Structure */ ++ ++typedef struct _MPI2_RAID_COMPATIBILITY_RESULT_STRUCT { ++ U8 State; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U32 GenericAttributes; /*0x04 */ ++ U32 OEMSpecificAttributes; /*0x08 */ ++ U32 Reserved3; /*0x0C */ ++ U32 Reserved4; /*0x10 */ ++} MPI2_RAID_COMPATIBILITY_RESULT_STRUCT, ++ *PTR_MPI2_RAID_COMPATIBILITY_RESULT_STRUCT, ++ Mpi2RaidCompatibilityResultStruct_t, ++ *pMpi2RaidCompatibilityResultStruct_t; ++ ++/*defines for RAID Compatibility Result Structure State field */ ++#define MPI2_RAID_COMPAT_STATE_COMPATIBLE (0x00) ++#define MPI2_RAID_COMPAT_STATE_NOT_COMPATIBLE (0x01) ++ ++/*defines for RAID Compatibility Result Structure GenericAttributes field */ ++#define MPI2_RAID_COMPAT_GENATTRIB_4K_SECTOR (0x00000010) ++ ++#define MPI2_RAID_COMPAT_GENATTRIB_MEDIA_MASK (0x0000000C) ++#define MPI2_RAID_COMPAT_GENATTRIB_SOLID_STATE_DRIVE (0x00000008) ++#define MPI2_RAID_COMPAT_GENATTRIB_HARD_DISK_DRIVE (0x00000004) ++ ++#define MPI2_RAID_COMPAT_GENATTRIB_PROTOCOL_MASK (0x00000003) ++#define MPI2_RAID_COMPAT_GENATTRIB_SAS_PROTOCOL (0x00000002) ++#define MPI2_RAID_COMPAT_GENATTRIB_SATA_PROTOCOL (0x00000001) ++ ++/*RAID Action Reply ActionData union */ ++typedef union _MPI2_RAID_ACTION_REPLY_DATA { ++ U32 Word[6]; ++ MPI2_RAID_VOL_INDICATOR RaidVolumeIndicator; ++ U16 VolDevHandle; ++ U8 VolumeState; ++ U8 PhysDiskNum; ++ MPI2_RAID_COMPATIBILITY_RESULT_STRUCT RaidCompatibilityResult; ++} MPI2_RAID_ACTION_REPLY_DATA, *PTR_MPI2_RAID_ACTION_REPLY_DATA, ++ Mpi2RaidActionReplyData_t, *pMpi2RaidActionReplyData_t; ++ ++/*use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for ++ *MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */ ++ ++/*RAID Action Reply Message */ ++typedef struct _MPI2_RAID_ACTION_REPLY { ++ U8 Action; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 VolDevHandle; /*0x04 */ ++ U8 PhysDiskNum; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved2; /*0x0A */ ++ U16 Reserved3; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ MPI2_RAID_ACTION_REPLY_DATA ActionData; /*0x14 */ ++} MPI2_RAID_ACTION_REPLY, *PTR_MPI2_RAID_ACTION_REPLY, ++ Mpi2RaidActionReply_t, *pMpi2RaidActionReply_t; ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_sas.h b/drivers/scsi/mpt2sas/mpi/mpi2_sas.h +new file mode 100644 +index 0000000..c10c2c0 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2_sas.h +@@ -0,0 +1,303 @@ ++/* ++ * Copyright 2000-2015 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2_sas.h ++ * Title: MPI Serial Attached SCSI structures and definitions ++ * Creation Date: February 9, 2007 ++ * ++ * mpi2_sas.h Version: 02.00.10 ++ * ++ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 ++ * prefix are for use only on MPI v2.5 products, and must not be used ++ * with MPI v2.0 products. Unless otherwise noted, names beginning with ++ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products. ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 06-26-07 02.00.01 Added Clear All Persistent Operation to SAS IO Unit ++ * Control Request. ++ * 10-02-08 02.00.02 Added Set IOC Parameter Operation to SAS IO Unit Control ++ * Request. ++ * 10-28-09 02.00.03 Changed the type of SGL in MPI2_SATA_PASSTHROUGH_REQUEST ++ * to MPI2_SGE_IO_UNION since it supports chained SGLs. ++ * 05-12-10 02.00.04 Modified some comments. ++ * 08-11-10 02.00.05 Added NCQ operations to SAS IO Unit Control. ++ * 11-18-11 02.00.06 Incorporating additions for MPI v2.5. ++ * 07-10-12 02.00.07 Added MPI2_SATA_PT_SGE_UNION for use in the SATA ++ * Passthrough Request message. ++ * 08-19-13 02.00.08 Made MPI2_SAS_OP_TRANSMIT_PORT_SELECT_SIGNAL obsolete ++ * for anything newer than MPI v2.0. ++ * 11-18-14 02.00.09 Updated copyright information. ++ * 03-16-15 02.00.10 Updated for MPI v2.6. ++ * Added MPI2_SATA_PT_REQ_PT_FLAGS_FPDMA. ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_SAS_H ++#define MPI2_SAS_H ++ ++/* ++ *Values for SASStatus. ++ */ ++#define MPI2_SASSTATUS_SUCCESS (0x00) ++#define MPI2_SASSTATUS_UNKNOWN_ERROR (0x01) ++#define MPI2_SASSTATUS_INVALID_FRAME (0x02) ++#define MPI2_SASSTATUS_UTC_BAD_DEST (0x03) ++#define MPI2_SASSTATUS_UTC_BREAK_RECEIVED (0x04) ++#define MPI2_SASSTATUS_UTC_CONNECT_RATE_NOT_SUPPORTED (0x05) ++#define MPI2_SASSTATUS_UTC_PORT_LAYER_REQUEST (0x06) ++#define MPI2_SASSTATUS_UTC_PROTOCOL_NOT_SUPPORTED (0x07) ++#define MPI2_SASSTATUS_UTC_STP_RESOURCES_BUSY (0x08) ++#define MPI2_SASSTATUS_UTC_WRONG_DESTINATION (0x09) ++#define MPI2_SASSTATUS_SHORT_INFORMATION_UNIT (0x0A) ++#define MPI2_SASSTATUS_LONG_INFORMATION_UNIT (0x0B) ++#define MPI2_SASSTATUS_XFER_RDY_INCORRECT_WRITE_DATA (0x0C) ++#define MPI2_SASSTATUS_XFER_RDY_REQUEST_OFFSET_ERROR (0x0D) ++#define MPI2_SASSTATUS_XFER_RDY_NOT_EXPECTED (0x0E) ++#define MPI2_SASSTATUS_DATA_INCORRECT_DATA_LENGTH (0x0F) ++#define MPI2_SASSTATUS_DATA_TOO_MUCH_READ_DATA (0x10) ++#define MPI2_SASSTATUS_DATA_OFFSET_ERROR (0x11) ++#define MPI2_SASSTATUS_SDSF_NAK_RECEIVED (0x12) ++#define MPI2_SASSTATUS_SDSF_CONNECTION_FAILED (0x13) ++#define MPI2_SASSTATUS_INITIATOR_RESPONSE_TIMEOUT (0x14) ++ ++/* ++ *Values for the SAS DeviceInfo field used in SAS Device Status Change Event ++ *data and SAS Configuration pages. ++ */ ++#define MPI2_SAS_DEVICE_INFO_SEP (0x00004000) ++#define MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE (0x00002000) ++#define MPI2_SAS_DEVICE_INFO_LSI_DEVICE (0x00001000) ++#define MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH (0x00000800) ++#define MPI2_SAS_DEVICE_INFO_SSP_TARGET (0x00000400) ++#define MPI2_SAS_DEVICE_INFO_STP_TARGET (0x00000200) ++#define MPI2_SAS_DEVICE_INFO_SMP_TARGET (0x00000100) ++#define MPI2_SAS_DEVICE_INFO_SATA_DEVICE (0x00000080) ++#define MPI2_SAS_DEVICE_INFO_SSP_INITIATOR (0x00000040) ++#define MPI2_SAS_DEVICE_INFO_STP_INITIATOR (0x00000020) ++#define MPI2_SAS_DEVICE_INFO_SMP_INITIATOR (0x00000010) ++#define MPI2_SAS_DEVICE_INFO_SATA_HOST (0x00000008) ++ ++#define MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE (0x00000007) ++#define MPI2_SAS_DEVICE_INFO_NO_DEVICE (0x00000000) ++#define MPI2_SAS_DEVICE_INFO_END_DEVICE (0x00000001) ++#define MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER (0x00000002) ++#define MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER (0x00000003) ++ ++/***************************************************************************** ++* ++* SAS Messages ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* SMP Passthrough messages ++****************************************************************************/ ++ ++/*SMP Passthrough Request Message */ ++typedef struct _MPI2_SMP_PASSTHROUGH_REQUEST { ++ U8 PassthroughFlags; /*0x00 */ ++ U8 PhysicalPort; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 RequestDataLength; /*0x04 */ ++ U8 SGLFlags; /*0x06*//*MPI v2.0 only. Reserved on MPI v2.5*/ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U32 Reserved2; /*0x0C */ ++ U64 SASAddress; /*0x10 */ ++ U32 Reserved3; /*0x18 */ ++ U32 Reserved4; /*0x1C */ ++ MPI2_SIMPLE_SGE_UNION SGL;/*0x20 */ ++} MPI2_SMP_PASSTHROUGH_REQUEST, *PTR_MPI2_SMP_PASSTHROUGH_REQUEST, ++ Mpi2SmpPassthroughRequest_t, *pMpi2SmpPassthroughRequest_t; ++ ++/*values for PassthroughFlags field */ ++#define MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE (0x80) ++ ++/*MPI v2.0: use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ ++ ++/*SMP Passthrough Reply Message */ ++typedef struct _MPI2_SMP_PASSTHROUGH_REPLY { ++ U8 PassthroughFlags; /*0x00 */ ++ U8 PhysicalPort; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 ResponseDataLength; /*0x04 */ ++ U8 SGLFlags; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U8 Reserved2; /*0x0C */ ++ U8 SASStatus; /*0x0D */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U32 Reserved3; /*0x14 */ ++ U8 ResponseData[4]; /*0x18 */ ++} MPI2_SMP_PASSTHROUGH_REPLY, *PTR_MPI2_SMP_PASSTHROUGH_REPLY, ++ Mpi2SmpPassthroughReply_t, *pMpi2SmpPassthroughReply_t; ++ ++/*values for PassthroughFlags field */ ++#define MPI2_SMP_PT_REPLY_PT_FLAGS_IMMEDIATE (0x80) ++ ++/*values for SASStatus field are at the top of this file */ ++ ++/**************************************************************************** ++* SATA Passthrough messages ++****************************************************************************/ ++ ++typedef union _MPI2_SATA_PT_SGE_UNION { ++ MPI2_SGE_SIMPLE_UNION MpiSimple; /*MPI v2.0 only */ ++ MPI2_SGE_CHAIN_UNION MpiChain; /*MPI v2.0 only */ ++ MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple; ++ MPI2_IEEE_SGE_CHAIN_UNION IeeeChain; /*MPI v2.0 only */ ++ MPI25_IEEE_SGE_CHAIN64 IeeeChain64; /*MPI v2.5 only */ ++} MPI2_SATA_PT_SGE_UNION, *PTR_MPI2_SATA_PT_SGE_UNION, ++ Mpi2SataPTSGEUnion_t, *pMpi2SataPTSGEUnion_t; ++ ++/*SATA Passthrough Request Message */ ++typedef struct _MPI2_SATA_PASSTHROUGH_REQUEST { ++ U16 DevHandle; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 PassthroughFlags; /*0x04 */ ++ U8 SGLFlags; /*0x06*//*MPI v2.0 only. Reserved on MPI v2.5*/ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U32 Reserved2; /*0x0C */ ++ U32 Reserved3; /*0x10 */ ++ U32 Reserved4; /*0x14 */ ++ U32 DataLength; /*0x18 */ ++ U8 CommandFIS[20]; /*0x1C */ ++ MPI2_SATA_PT_SGE_UNION SGL;/*0x30*//*MPI v2.5: IEEE 64 elements only*/ ++} MPI2_SATA_PASSTHROUGH_REQUEST, *PTR_MPI2_SATA_PASSTHROUGH_REQUEST, ++ Mpi2SataPassthroughRequest_t, ++ *pMpi2SataPassthroughRequest_t; ++ ++/*values for PassthroughFlags field */ ++#define MPI2_SATA_PT_REQ_PT_FLAGS_EXECUTE_DIAG (0x0100) ++#define MPI2_SATA_PT_REQ_PT_FLAGS_FPDMA (0x0040) ++#define MPI2_SATA_PT_REQ_PT_FLAGS_DMA (0x0020) ++#define MPI2_SATA_PT_REQ_PT_FLAGS_PIO (0x0010) ++#define MPI2_SATA_PT_REQ_PT_FLAGS_UNSPECIFIED_VU (0x0004) ++#define MPI2_SATA_PT_REQ_PT_FLAGS_WRITE (0x0002) ++#define MPI2_SATA_PT_REQ_PT_FLAGS_READ (0x0001) ++ ++/*MPI v2.0: use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ ++ ++/*SATA Passthrough Reply Message */ ++typedef struct _MPI2_SATA_PASSTHROUGH_REPLY { ++ U16 DevHandle; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 PassthroughFlags; /*0x04 */ ++ U8 SGLFlags; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U8 Reserved2; /*0x0C */ ++ U8 SASStatus; /*0x0D */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U8 StatusFIS[20]; /*0x14 */ ++ U32 StatusControlRegisters; /*0x28 */ ++ U32 TransferCount; /*0x2C */ ++} MPI2_SATA_PASSTHROUGH_REPLY, *PTR_MPI2_SATA_PASSTHROUGH_REPLY, ++ Mpi2SataPassthroughReply_t, *pMpi2SataPassthroughReply_t; ++ ++/*values for SASStatus field are at the top of this file */ ++ ++/**************************************************************************** ++* SAS IO Unit Control messages ++* (MPI v2.5 and earlier only. ++* Replaced by IO Unit Control messages in MPI v2.6 and later.) ++****************************************************************************/ ++ ++/*SAS IO Unit Control Request Message */ ++typedef struct _MPI2_SAS_IOUNIT_CONTROL_REQUEST { ++ U8 Operation; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 DevHandle; /*0x04 */ ++ U8 IOCParameter; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U16 Reserved4; /*0x0C */ ++ U8 PhyNum; /*0x0E */ ++ U8 PrimFlags; /*0x0F */ ++ U32 Primitive; /*0x10 */ ++ U8 LookupMethod; /*0x14 */ ++ U8 Reserved5; /*0x15 */ ++ U16 SlotNumber; /*0x16 */ ++ U64 LookupAddress; /*0x18 */ ++ U32 IOCParameterValue; /*0x20 */ ++ U32 Reserved7; /*0x24 */ ++ U32 Reserved8; /*0x28 */ ++} MPI2_SAS_IOUNIT_CONTROL_REQUEST, ++ *PTR_MPI2_SAS_IOUNIT_CONTROL_REQUEST, ++ Mpi2SasIoUnitControlRequest_t, ++ *pMpi2SasIoUnitControlRequest_t; ++ ++/*values for the Operation field */ ++#define MPI2_SAS_OP_CLEAR_ALL_PERSISTENT (0x02) ++#define MPI2_SAS_OP_PHY_LINK_RESET (0x06) ++#define MPI2_SAS_OP_PHY_HARD_RESET (0x07) ++#define MPI2_SAS_OP_PHY_CLEAR_ERROR_LOG (0x08) ++#define MPI2_SAS_OP_SEND_PRIMITIVE (0x0A) ++#define MPI2_SAS_OP_FORCE_FULL_DISCOVERY (0x0B) ++#define MPI2_SAS_OP_TRANSMIT_PORT_SELECT_SIGNAL (0x0C) /* MPI v2.0 only */ ++#define MPI2_SAS_OP_REMOVE_DEVICE (0x0D) ++#define MPI2_SAS_OP_LOOKUP_MAPPING (0x0E) ++#define MPI2_SAS_OP_SET_IOC_PARAMETER (0x0F) ++#define MPI25_SAS_OP_ENABLE_FP_DEVICE (0x10) ++#define MPI25_SAS_OP_DISABLE_FP_DEVICE (0x11) ++#define MPI25_SAS_OP_ENABLE_FP_ALL (0x12) ++#define MPI25_SAS_OP_DISABLE_FP_ALL (0x13) ++#define MPI2_SAS_OP_DEV_ENABLE_NCQ (0x14) ++#define MPI2_SAS_OP_DEV_DISABLE_NCQ (0x15) ++#define MPI2_SAS_OP_PRODUCT_SPECIFIC_MIN (0x80) ++ ++/*values for the PrimFlags field */ ++#define MPI2_SAS_PRIMFLAGS_SINGLE (0x08) ++#define MPI2_SAS_PRIMFLAGS_TRIPLE (0x02) ++#define MPI2_SAS_PRIMFLAGS_REDUNDANT (0x01) ++ ++/*values for the LookupMethod field */ ++#define MPI2_SAS_LOOKUP_METHOD_SAS_ADDRESS (0x01) ++#define MPI2_SAS_LOOKUP_METHOD_SAS_ENCLOSURE_SLOT (0x02) ++#define MPI2_SAS_LOOKUP_METHOD_SAS_DEVICE_NAME (0x03) ++ ++/*SAS IO Unit Control Reply Message */ ++typedef struct _MPI2_SAS_IOUNIT_CONTROL_REPLY { ++ U8 Operation; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 DevHandle; /*0x04 */ ++ U8 IOCParameter; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U16 Reserved4; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_SAS_IOUNIT_CONTROL_REPLY, ++ *PTR_MPI2_SAS_IOUNIT_CONTROL_REPLY, ++ Mpi2SasIoUnitControlReply_t, *pMpi2SasIoUnitControlReply_t; ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h +new file mode 100644 +index 0000000..5f9289a +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h +@@ -0,0 +1,483 @@ ++/* ++ * Copyright 2000-2014 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2_tool.h ++ * Title: MPI diagnostic tool structures and definitions ++ * Creation Date: March 26, 2007 ++ * ++ * mpi2_tool.h Version: 02.00.13 ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 12-18-07 02.00.01 Added Diagnostic Buffer Post and Diagnostic Release ++ * structures and defines. ++ * 02-29-08 02.00.02 Modified various names to make them 32-character unique. ++ * 05-06-09 02.00.03 Added ISTWI Read Write Tool and Diagnostic CLI Tool. ++ * 07-30-09 02.00.04 Added ExtendedType field to DiagnosticBufferPost request ++ * and reply messages. ++ * Added MPI2_DIAG_BUF_TYPE_EXTENDED. ++ * Incremented MPI2_DIAG_BUF_TYPE_COUNT. ++ * 05-12-10 02.00.05 Added Diagnostic Data Upload tool. ++ * 08-11-10 02.00.06 Added defines that were missing for Diagnostic Buffer ++ * Post Request. ++ * 05-25-11 02.00.07 Added Flags field and related defines to ++ * MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST. ++ * 11-18-11 02.00.08 Incorporating additions for MPI v2.5. ++ * 07-10-12 02.00.09 Add MPI v2.5 Toolbox Diagnostic CLI Tool Request ++ * message. ++ * 07-26-12 02.00.10 Modified MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST so that ++ * it uses MPI Chain SGE as well as MPI Simple SGE. ++ * 08-19-13 02.00.11 Added MPI2_TOOLBOX_TEXT_DISPLAY_TOOL and related info. ++ * 01-08-14 02.00.12 Added MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC. ++ * 11-18-14 02.00.13 Updated copyright information. ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_TOOL_H ++#define MPI2_TOOL_H ++ ++/***************************************************************************** ++* ++* Toolbox Messages ++* ++*****************************************************************************/ ++ ++/*defines for the Tools */ ++#define MPI2_TOOLBOX_CLEAN_TOOL (0x00) ++#define MPI2_TOOLBOX_MEMORY_MOVE_TOOL (0x01) ++#define MPI2_TOOLBOX_DIAG_DATA_UPLOAD_TOOL (0x02) ++#define MPI2_TOOLBOX_ISTWI_READ_WRITE_TOOL (0x03) ++#define MPI2_TOOLBOX_BEACON_TOOL (0x05) ++#define MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL (0x06) ++#define MPI2_TOOLBOX_TEXT_DISPLAY_TOOL (0x07) ++ ++/**************************************************************************** ++* Toolbox reply ++****************************************************************************/ ++ ++typedef struct _MPI2_TOOLBOX_REPLY { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_TOOLBOX_REPLY, *PTR_MPI2_TOOLBOX_REPLY, ++ Mpi2ToolboxReply_t, *pMpi2ToolboxReply_t; ++ ++/**************************************************************************** ++* Toolbox Clean Tool request ++****************************************************************************/ ++ ++typedef struct _MPI2_TOOLBOX_CLEAN_REQUEST { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 Flags; /*0x0C */ ++} MPI2_TOOLBOX_CLEAN_REQUEST, *PTR_MPI2_TOOLBOX_CLEAN_REQUEST, ++ Mpi2ToolboxCleanRequest_t, *pMpi2ToolboxCleanRequest_t; ++ ++/*values for the Flags field */ ++#define MPI2_TOOLBOX_CLEAN_BOOT_SERVICES (0x80000000) ++#define MPI2_TOOLBOX_CLEAN_PERSIST_MANUFACT_PAGES (0x40000000) ++#define MPI2_TOOLBOX_CLEAN_OTHER_PERSIST_PAGES (0x20000000) ++#define MPI2_TOOLBOX_CLEAN_FW_CURRENT (0x10000000) ++#define MPI2_TOOLBOX_CLEAN_FW_BACKUP (0x08000000) ++#define MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC (0x04000000) ++#define MPI2_TOOLBOX_CLEAN_MEGARAID (0x02000000) ++#define MPI2_TOOLBOX_CLEAN_INITIALIZATION (0x01000000) ++#define MPI2_TOOLBOX_CLEAN_FLASH (0x00000004) ++#define MPI2_TOOLBOX_CLEAN_SEEPROM (0x00000002) ++#define MPI2_TOOLBOX_CLEAN_NVSRAM (0x00000001) ++ ++/**************************************************************************** ++* Toolbox Memory Move request ++****************************************************************************/ ++ ++typedef struct _MPI2_TOOLBOX_MEM_MOVE_REQUEST { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ MPI2_SGE_SIMPLE_UNION SGL; /*0x0C */ ++} MPI2_TOOLBOX_MEM_MOVE_REQUEST, *PTR_MPI2_TOOLBOX_MEM_MOVE_REQUEST, ++ Mpi2ToolboxMemMoveRequest_t, *pMpi2ToolboxMemMoveRequest_t; ++ ++/**************************************************************************** ++* Toolbox Diagnostic Data Upload request ++****************************************************************************/ ++ ++typedef struct _MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U8 SGLFlags; /*0x0C */ ++ U8 Reserved5; /*0x0D */ ++ U16 Reserved6; /*0x0E */ ++ U32 Flags; /*0x10 */ ++ U32 DataLength; /*0x14 */ ++ MPI2_SGE_SIMPLE_UNION SGL; /*0x18 */ ++} MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST, ++ *PTR_MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST, ++ Mpi2ToolboxDiagDataUploadRequest_t, ++ *pMpi2ToolboxDiagDataUploadRequest_t; ++ ++/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ ++ ++typedef struct _MPI2_DIAG_DATA_UPLOAD_HEADER { ++ U32 DiagDataLength; /*00h */ ++ U8 FormatCode; /*04h */ ++ U8 Reserved1; /*05h */ ++ U16 Reserved2; /*06h */ ++} MPI2_DIAG_DATA_UPLOAD_HEADER, *PTR_MPI2_DIAG_DATA_UPLOAD_HEADER, ++ Mpi2DiagDataUploadHeader_t, *pMpi2DiagDataUploadHeader_t; ++ ++/**************************************************************************** ++* Toolbox ISTWI Read Write Tool ++****************************************************************************/ ++ ++/*Toolbox ISTWI Read Write Tool request message */ ++typedef struct _MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 Reserved5; /*0x0C */ ++ U32 Reserved6; /*0x10 */ ++ U8 DevIndex; /*0x14 */ ++ U8 Action; /*0x15 */ ++ U8 SGLFlags; /*0x16 */ ++ U8 Flags; /*0x17 */ ++ U16 TxDataLength; /*0x18 */ ++ U16 RxDataLength; /*0x1A */ ++ U32 Reserved8; /*0x1C */ ++ U32 Reserved9; /*0x20 */ ++ U32 Reserved10; /*0x24 */ ++ U32 Reserved11; /*0x28 */ ++ U32 Reserved12; /*0x2C */ ++ MPI2_SGE_SIMPLE_UNION SGL; /*0x30 */ ++} MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST, ++ *PTR_MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST, ++ Mpi2ToolboxIstwiReadWriteRequest_t, ++ *pMpi2ToolboxIstwiReadWriteRequest_t; ++ ++/*values for the Action field */ ++#define MPI2_TOOL_ISTWI_ACTION_READ_DATA (0x01) ++#define MPI2_TOOL_ISTWI_ACTION_WRITE_DATA (0x02) ++#define MPI2_TOOL_ISTWI_ACTION_SEQUENCE (0x03) ++#define MPI2_TOOL_ISTWI_ACTION_RESERVE_BUS (0x10) ++#define MPI2_TOOL_ISTWI_ACTION_RELEASE_BUS (0x11) ++#define MPI2_TOOL_ISTWI_ACTION_RESET (0x12) ++ ++/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ ++ ++/*values for the Flags field */ ++#define MPI2_TOOL_ISTWI_FLAG_AUTO_RESERVE_RELEASE (0x80) ++#define MPI2_TOOL_ISTWI_FLAG_PAGE_ADDR_MASK (0x07) ++ ++/*Toolbox ISTWI Read Write Tool reply message */ ++typedef struct _MPI2_TOOLBOX_ISTWI_REPLY { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U8 DevIndex; /*0x14 */ ++ U8 Action; /*0x15 */ ++ U8 IstwiStatus; /*0x16 */ ++ U8 Reserved6; /*0x17 */ ++ U16 TxDataCount; /*0x18 */ ++ U16 RxDataCount; /*0x1A */ ++} MPI2_TOOLBOX_ISTWI_REPLY, *PTR_MPI2_TOOLBOX_ISTWI_REPLY, ++ Mpi2ToolboxIstwiReply_t, *pMpi2ToolboxIstwiReply_t; ++ ++/**************************************************************************** ++* Toolbox Beacon Tool request ++****************************************************************************/ ++ ++typedef struct _MPI2_TOOLBOX_BEACON_REQUEST { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U8 Reserved5; /*0x0C */ ++ U8 PhysicalPort; /*0x0D */ ++ U8 Reserved6; /*0x0E */ ++ U8 Flags; /*0x0F */ ++} MPI2_TOOLBOX_BEACON_REQUEST, *PTR_MPI2_TOOLBOX_BEACON_REQUEST, ++ Mpi2ToolboxBeaconRequest_t, *pMpi2ToolboxBeaconRequest_t; ++ ++/*values for the Flags field */ ++#define MPI2_TOOLBOX_FLAGS_BEACONMODE_OFF (0x00) ++#define MPI2_TOOLBOX_FLAGS_BEACONMODE_ON (0x01) ++ ++/**************************************************************************** ++* Toolbox Diagnostic CLI Tool ++****************************************************************************/ ++ ++#define MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH (0x5C) ++ ++/*MPI v2.0 Toolbox Diagnostic CLI Tool request message */ ++typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U8 SGLFlags; /*0x0C */ ++ U8 Reserved5; /*0x0D */ ++ U16 Reserved6; /*0x0E */ ++ U32 DataLength; /*0x10 */ ++ U8 DiagnosticCliCommand[MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH];/*0x14 */ ++ MPI2_MPI_SGE_IO_UNION SGL; /*0x70 */ ++} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, ++ *PTR_MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, ++ Mpi2ToolboxDiagnosticCliRequest_t, ++ *pMpi2ToolboxDiagnosticCliRequest_t; ++ ++/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ ++ ++/*MPI v2.5 Toolbox Diagnostic CLI Tool request message */ ++typedef struct _MPI25_TOOLBOX_DIAGNOSTIC_CLI_REQUEST { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 Reserved5; /*0x0C */ ++ U32 DataLength; /*0x10 */ ++ U8 DiagnosticCliCommand[MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH];/*0x14 */ ++ MPI25_SGE_IO_UNION SGL; /* 0x70 */ ++} MPI25_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, ++ *PTR_MPI25_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, ++ Mpi25ToolboxDiagnosticCliRequest_t, ++ *pMpi25ToolboxDiagnosticCliRequest_t; ++ ++/*Toolbox Diagnostic CLI Tool reply message */ ++typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U32 ReturnedDataLength; /*0x14 */ ++} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY, ++ *PTR_MPI2_TOOLBOX_DIAG_CLI_REPLY, ++ Mpi2ToolboxDiagnosticCliReply_t, ++ *pMpi2ToolboxDiagnosticCliReply_t; ++ ++ ++/**************************************************************************** ++* Toolbox Console Text Display Tool ++****************************************************************************/ ++ ++/* Toolbox Console Text Display Tool request message */ ++typedef struct _MPI2_TOOLBOX_TEXT_DISPLAY_REQUEST { ++ U8 Tool; /* 0x00 */ ++ U8 Reserved1; /* 0x01 */ ++ U8 ChainOffset; /* 0x02 */ ++ U8 Function; /* 0x03 */ ++ U16 Reserved2; /* 0x04 */ ++ U8 Reserved3; /* 0x06 */ ++ U8 MsgFlags; /* 0x07 */ ++ U8 VP_ID; /* 0x08 */ ++ U8 VF_ID; /* 0x09 */ ++ U16 Reserved4; /* 0x0A */ ++ U8 Console; /* 0x0C */ ++ U8 Flags; /* 0x0D */ ++ U16 Reserved6; /* 0x0E */ ++ U8 TextToDisplay[4]; /* 0x10 */ ++} MPI2_TOOLBOX_TEXT_DISPLAY_REQUEST, ++*PTR_MPI2_TOOLBOX_TEXT_DISPLAY_REQUEST, ++Mpi2ToolboxTextDisplayRequest_t, ++*pMpi2ToolboxTextDisplayRequest_t; ++ ++/* defines for the Console field */ ++#define MPI2_TOOLBOX_CONSOLE_TYPE_MASK (0xF0) ++#define MPI2_TOOLBOX_CONSOLE_TYPE_DEFAULT (0x00) ++#define MPI2_TOOLBOX_CONSOLE_TYPE_UART (0x10) ++#define MPI2_TOOLBOX_CONSOLE_TYPE_ETHERNET (0x20) ++ ++#define MPI2_TOOLBOX_CONSOLE_NUMBER_MASK (0x0F) ++ ++/* defines for the Flags field */ ++#define MPI2_TOOLBOX_CONSOLE_FLAG_TIMESTAMP (0x01) ++ ++ ++ ++/***************************************************************************** ++* ++* Diagnostic Buffer Messages ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* Diagnostic Buffer Post request ++****************************************************************************/ ++ ++typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST { ++ U8 ExtendedType; /*0x00 */ ++ U8 BufferType; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U64 BufferAddress; /*0x0C */ ++ U32 BufferLength; /*0x14 */ ++ U32 Reserved5; /*0x18 */ ++ U32 Reserved6; /*0x1C */ ++ U32 Flags; /*0x20 */ ++ U32 ProductSpecific[23]; /*0x24 */ ++} MPI2_DIAG_BUFFER_POST_REQUEST, *PTR_MPI2_DIAG_BUFFER_POST_REQUEST, ++ Mpi2DiagBufferPostRequest_t, *pMpi2DiagBufferPostRequest_t; ++ ++/*values for the ExtendedType field */ ++#define MPI2_DIAG_EXTENDED_TYPE_UTILIZATION (0x02) ++ ++/*values for the BufferType field */ ++#define MPI2_DIAG_BUF_TYPE_TRACE (0x00) ++#define MPI2_DIAG_BUF_TYPE_SNAPSHOT (0x01) ++#define MPI2_DIAG_BUF_TYPE_EXTENDED (0x02) ++/*count of the number of buffer types */ ++#define MPI2_DIAG_BUF_TYPE_COUNT (0x03) ++ ++/*values for the Flags field */ ++#define MPI2_DIAG_BUF_FLAG_RELEASE_ON_FULL (0x00000002) ++#define MPI2_DIAG_BUF_FLAG_IMMEDIATE_RELEASE (0x00000001) ++ ++/**************************************************************************** ++* Diagnostic Buffer Post reply ++****************************************************************************/ ++ ++typedef struct _MPI2_DIAG_BUFFER_POST_REPLY { ++ U8 ExtendedType; /*0x00 */ ++ U8 BufferType; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U32 TransferLength; /*0x14 */ ++} MPI2_DIAG_BUFFER_POST_REPLY, *PTR_MPI2_DIAG_BUFFER_POST_REPLY, ++ Mpi2DiagBufferPostReply_t, *pMpi2DiagBufferPostReply_t; ++ ++/**************************************************************************** ++* Diagnostic Release request ++****************************************************************************/ ++ ++typedef struct _MPI2_DIAG_RELEASE_REQUEST { ++ U8 Reserved1; /*0x00 */ ++ U8 BufferType; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++} MPI2_DIAG_RELEASE_REQUEST, *PTR_MPI2_DIAG_RELEASE_REQUEST, ++ Mpi2DiagReleaseRequest_t, *pMpi2DiagReleaseRequest_t; ++ ++/**************************************************************************** ++* Diagnostic Buffer Post reply ++****************************************************************************/ ++ ++typedef struct _MPI2_DIAG_RELEASE_REPLY { ++ U8 Reserved1; /*0x00 */ ++ U8 BufferType; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_DIAG_RELEASE_REPLY, *PTR_MPI2_DIAG_RELEASE_REPLY, ++ Mpi2DiagReleaseReply_t, *pMpi2DiagReleaseReply_t; ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_type.h b/drivers/scsi/mpt2sas/mpi/mpi2_type.h +new file mode 100644 +index 0000000..92a81ab +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2_type.h +@@ -0,0 +1,57 @@ ++/* ++ * Copyright 2000-2014 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2_type.h ++ * Title: MPI basic type definitions ++ * Creation Date: August 16, 2006 ++ * ++ * mpi2_type.h Version: 02.00.01 ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 11-18-14 02.00.01 Updated copyright information. ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_TYPE_H ++#define MPI2_TYPE_H ++ ++/******************************************************************************* ++ * Define * if it hasn't already been defined. By default ++ * * is defined to be a near pointer. MPI2_POINTER can be defined as ++ * a far pointer by defining * as "far *" before this header file is ++ * included. ++ */ ++ ++/* the basic types may have already been included by mpi_type.h */ ++#ifndef MPI_TYPE_H ++/***************************************************************************** ++* ++* Basic Types ++* ++*****************************************************************************/ ++ ++typedef u8 U8; ++typedef __le16 U16; ++typedef __le32 U32; ++typedef __le64 U64 __attribute__ ((aligned(4))); ++ ++/***************************************************************************** ++* ++* Pointer Types ++* ++*****************************************************************************/ ++ ++typedef U8 *PU8; ++typedef U16 *PU16; ++typedef U32 *PU32; ++typedef U64 *PU64; ++ ++#endif ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpt3sas_base.c b/drivers/scsi/mpt2sas/mpt3sas_base.c +new file mode 100644 +index 0000000..224bf9d +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_base.c +@@ -0,0 +1,5713 @@ ++/* ++ * This is the Fusion MPT base driver providing common API layer interface ++ * for access to MPT (Message Passing Technology) firmware. ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.c ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#include "mpt3sas_base.h" ++ ++static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS]; ++ ++ ++#define FAULT_POLLING_INTERVAL 1000 /* in milliseconds */ ++ ++ /* maximum controller queue depth */ ++#define MAX_HBA_QUEUE_DEPTH 30000 ++#define MAX_CHAIN_DEPTH 100000 ++static int max_queue_depth = -1; ++module_param(max_queue_depth, int, 0); ++MODULE_PARM_DESC(max_queue_depth, " max controller queue depth "); ++ ++static int max_sgl_entries = -1; ++module_param(max_sgl_entries, int, 0); ++MODULE_PARM_DESC(max_sgl_entries, " max sg entries "); ++ ++static int msix_disable = -1; ++module_param(msix_disable, int, 0); ++MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)"); ++ ++static int smp_affinity_enable = 1; ++module_param(smp_affinity_enable, int, S_IRUGO); ++MODULE_PARM_DESC(smp_affinity_enable, "SMP affinity feature enable/disbale Default: enable(1)"); ++ ++static int max_msix_vectors = -1; ++module_param(max_msix_vectors, int, 0); ++MODULE_PARM_DESC(max_msix_vectors, ++ " max msix vectors"); ++ ++static int mpt2sas_fwfault_debug; ++MODULE_PARM_DESC(mpt2sas_fwfault_debug, ++ " enable detection of firmware fault and halt firmware - (default=0)"); ++ ++static int ++_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag); ++ ++/** ++ * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug. ++ * ++ */ ++static int ++_scsih_set_fwfault_debug(const char *val, struct kernel_param *kp) ++{ ++ int ret = param_set_int(val, kp); ++ struct MPT3SAS_ADAPTER *ioc; ++ ++ if (ret) ++ return ret; ++ ++ /* global ioc spinlock to protect controller list on list operations */ ++ pr_info("setting fwfault_debug(%d)\n", mpt2sas_fwfault_debug); ++ spin_lock(&gioc_lock_mpt2sas); ++ list_for_each_entry(ioc, &mpt2sas_ioc_list, list) ++ ioc->fwfault_debug = mpt2sas_fwfault_debug; ++ spin_unlock(&gioc_lock_mpt2sas); ++ return 0; ++} ++module_param_call(mpt2sas_fwfault_debug, _scsih_set_fwfault_debug, ++ param_get_int, &mpt2sas_fwfault_debug, 0644); ++ ++/** ++ * mpt2sas_remove_dead_ioc_func - kthread context to remove dead ioc ++ * @arg: input argument, used to derive ioc ++ * ++ * Return 0 if controller is removed from pci subsystem. ++ * Return -1 for other case. ++ */ ++static int mpt2sas_remove_dead_ioc_func(void *arg) ++{ ++ struct MPT3SAS_ADAPTER *ioc = (struct MPT3SAS_ADAPTER *)arg; ++ struct pci_dev *pdev; ++ ++ if ((ioc == NULL)) ++ return -1; ++ ++ pdev = ioc->pdev; ++ if ((pdev == NULL)) ++ return -1; ++ pci_stop_and_remove_bus_device_locked(pdev); ++ return 0; ++} ++ ++/** ++ * _base_fault_reset_work - workq handling ioc fault conditions ++ * @work: input argument, used to derive ioc ++ * Context: sleep. ++ * ++ * Return nothing. ++ */ ++static void ++_base_fault_reset_work(struct work_struct *work) ++{ ++ struct MPT3SAS_ADAPTER *ioc = ++ container_of(work, struct MPT3SAS_ADAPTER, fault_reset_work.work); ++ unsigned long flags; ++ u32 doorbell; ++ int rc; ++ struct task_struct *p; ++ ++ ++ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ++ if (ioc->shost_recovery || ioc->pci_error_recovery) ++ goto rearm_timer; ++ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); ++ ++ doorbell = mpt2sas_base_get_iocstate(ioc, 0); ++ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_MASK) { ++ pr_err(MPT3SAS_FMT "SAS host is non-operational !!!!\n", ++ ioc->name); ++ ++ /* It may be possible that EEH recovery can resolve some of ++ * pci bus failure issues rather removing the dead ioc function ++ * by considering controller is in a non-operational state. So ++ * here priority is given to the EEH recovery. If it doesn't ++ * not resolve this issue, mpt3sas driver will consider this ++ * controller to non-operational state and remove the dead ioc ++ * function. ++ */ ++ if (ioc->non_operational_loop++ < 5) { ++ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, ++ flags); ++ goto rearm_timer; ++ } ++ ++ /* ++ * Call _scsih_flush_pending_cmds callback so that we flush all ++ * pending commands back to OS. This call is required to aovid ++ * deadlock at block layer. Dead IOC will fail to do diag reset, ++ * and this call is safe since dead ioc will never return any ++ * command back from HW. ++ */ ++ ioc->schedule_dead_ioc_flush_running_cmds(ioc); ++ /* ++ * Set remove_host flag early since kernel thread will ++ * take some time to execute. ++ */ ++ ioc->remove_host = 1; ++ /*Remove the Dead Host */ ++ p = kthread_run(mpt2sas_remove_dead_ioc_func, ioc, ++ "%s_dead_ioc_%d", ioc->driver_name, ioc->id); ++ if (IS_ERR(p)) ++ pr_err(MPT3SAS_FMT ++ "%s: Running mpt2sas_dead_ioc thread failed !!!!\n", ++ ioc->name, __func__); ++ else ++ pr_err(MPT3SAS_FMT ++ "%s: Running mpt2sas_dead_ioc thread success !!!!\n", ++ ioc->name, __func__); ++ return; /* don't rearm timer */ ++ } ++ ++ ioc->non_operational_loop = 0; ++ ++ if ((doorbell & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL) { ++ rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ pr_warn(MPT3SAS_FMT "%s: hard reset: %s\n", ioc->name, ++ __func__, (rc == 0) ? "success" : "failed"); ++ doorbell = mpt2sas_base_get_iocstate(ioc, 0); ++ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) ++ mpt2sas_base_fault_info(ioc, doorbell & ++ MPI2_DOORBELL_DATA_MASK); ++ if (rc && (doorbell & MPI2_IOC_STATE_MASK) != ++ MPI2_IOC_STATE_OPERATIONAL) ++ return; /* don't rearm timer */ ++ } ++ ++ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ++ rearm_timer: ++ if (ioc->fault_reset_work_q) ++ queue_delayed_work(ioc->fault_reset_work_q, ++ &ioc->fault_reset_work, ++ msecs_to_jiffies(FAULT_POLLING_INTERVAL)); ++ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); ++} ++ ++/** ++ * mpt2sas_base_start_watchdog - start the fault_reset_work_q ++ * @ioc: per adapter object ++ * Context: sleep. ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_start_watchdog(struct MPT3SAS_ADAPTER *ioc) ++{ ++ unsigned long flags; ++ ++ if (ioc->fault_reset_work_q) ++ return; ++ ++ /* initialize fault polling */ ++ ++ INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work); ++ snprintf(ioc->fault_reset_work_q_name, ++ sizeof(ioc->fault_reset_work_q_name), "poll_%s%d_status", ++ ioc->driver_name, ioc->id); ++ ioc->fault_reset_work_q = ++ create_singlethread_workqueue(ioc->fault_reset_work_q_name); ++ if (!ioc->fault_reset_work_q) { ++ pr_err(MPT3SAS_FMT "%s: failed (line=%d)\n", ++ ioc->name, __func__, __LINE__); ++ return; ++ } ++ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ++ if (ioc->fault_reset_work_q) ++ queue_delayed_work(ioc->fault_reset_work_q, ++ &ioc->fault_reset_work, ++ msecs_to_jiffies(FAULT_POLLING_INTERVAL)); ++ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); ++} ++ ++/** ++ * mpt2sas_base_stop_watchdog - stop the fault_reset_work_q ++ * @ioc: per adapter object ++ * Context: sleep. ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_stop_watchdog(struct MPT3SAS_ADAPTER *ioc) ++{ ++ unsigned long flags; ++ struct workqueue_struct *wq; ++ ++ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ++ wq = ioc->fault_reset_work_q; ++ ioc->fault_reset_work_q = NULL; ++ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); ++ if (wq) { ++ if (!cancel_delayed_work_sync(&ioc->fault_reset_work)) ++ flush_workqueue(wq); ++ destroy_workqueue(wq); ++ } ++} ++ ++/** ++ * mpt2sas_base_fault_info - verbose translation of firmware FAULT code ++ * @ioc: per adapter object ++ * @fault_code: fault code ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_fault_info(struct MPT3SAS_ADAPTER *ioc , u16 fault_code) ++{ ++ pr_err(MPT3SAS_FMT "fault_state(0x%04x)!\n", ++ ioc->name, fault_code); ++} ++ ++/** ++ * mpt2sas_halt_firmware - halt's mpt controller firmware ++ * @ioc: per adapter object ++ * ++ * For debugging timeout related issues. Writing 0xCOFFEE00 ++ * to the doorbell register will halt controller firmware. With ++ * the purpose to stop both driver and firmware, the enduser can ++ * obtain a ring buffer from controller UART. ++ */ ++void ++mpt2sas_halt_firmware(struct MPT3SAS_ADAPTER *ioc) ++{ ++ u32 doorbell; ++ ++ if (!ioc->fwfault_debug) ++ return; ++ ++ dump_stack(); ++ ++ doorbell = readl(&ioc->chip->Doorbell); ++ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) ++ mpt2sas_base_fault_info(ioc , doorbell); ++ else { ++ writel(0xC0FFEE00, &ioc->chip->Doorbell); ++ pr_err(MPT3SAS_FMT "Firmware is halted due to command timeout\n", ++ ioc->name); ++ } ++ ++ if (ioc->fwfault_debug == 2) ++ for (;;) ++ ; ++ else ++ panic("panic in %s\n", __func__); ++} ++ ++/** ++ * _base_sas_ioc_info - verbose translation of the ioc status ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @request_hdr: request mf ++ * ++ * Return nothing. ++ */ ++static void ++_base_sas_ioc_info(struct MPT3SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply, ++ MPI2RequestHeader_t *request_hdr) ++{ ++ u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ char *desc = NULL; ++ u16 frame_sz; ++ char *func_str = NULL; ++ ++ /* SCSI_IO, RAID_PASS are handled from _scsih_scsi_ioc_info */ ++ if (request_hdr->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || ++ request_hdr->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH || ++ request_hdr->Function == MPI2_FUNCTION_EVENT_NOTIFICATION) ++ return; ++ ++ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) ++ return; ++ ++ switch (ioc_status) { ++ ++/**************************************************************************** ++* Common IOCStatus values for all replies ++****************************************************************************/ ++ ++ case MPI2_IOCSTATUS_INVALID_FUNCTION: ++ desc = "invalid function"; ++ break; ++ case MPI2_IOCSTATUS_BUSY: ++ desc = "busy"; ++ break; ++ case MPI2_IOCSTATUS_INVALID_SGL: ++ desc = "invalid sgl"; ++ break; ++ case MPI2_IOCSTATUS_INTERNAL_ERROR: ++ desc = "internal error"; ++ break; ++ case MPI2_IOCSTATUS_INVALID_VPID: ++ desc = "invalid vpid"; ++ break; ++ case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES: ++ desc = "insufficient resources"; ++ break; ++ case MPI2_IOCSTATUS_INSUFFICIENT_POWER: ++ desc = "insufficient power"; ++ break; ++ case MPI2_IOCSTATUS_INVALID_FIELD: ++ desc = "invalid field"; ++ break; ++ case MPI2_IOCSTATUS_INVALID_STATE: ++ desc = "invalid state"; ++ break; ++ case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED: ++ desc = "op state not supported"; ++ break; ++ ++/**************************************************************************** ++* Config IOCStatus values ++****************************************************************************/ ++ ++ case MPI2_IOCSTATUS_CONFIG_INVALID_ACTION: ++ desc = "config invalid action"; ++ break; ++ case MPI2_IOCSTATUS_CONFIG_INVALID_TYPE: ++ desc = "config invalid type"; ++ break; ++ case MPI2_IOCSTATUS_CONFIG_INVALID_PAGE: ++ desc = "config invalid page"; ++ break; ++ case MPI2_IOCSTATUS_CONFIG_INVALID_DATA: ++ desc = "config invalid data"; ++ break; ++ case MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS: ++ desc = "config no defaults"; ++ break; ++ case MPI2_IOCSTATUS_CONFIG_CANT_COMMIT: ++ desc = "config cant commit"; ++ break; ++ ++/**************************************************************************** ++* SCSI IO Reply ++****************************************************************************/ ++ ++ case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: ++ case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE: ++ case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: ++ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: ++ case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: ++ case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: ++ case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: ++ case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: ++ case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: ++ case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: ++ case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: ++ case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: ++ break; ++ ++/**************************************************************************** ++* For use by SCSI Initiator and SCSI Target end-to-end data protection ++****************************************************************************/ ++ ++ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: ++ desc = "eedp guard error"; ++ break; ++ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR: ++ desc = "eedp ref tag error"; ++ break; ++ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR: ++ desc = "eedp app tag error"; ++ break; ++ ++/**************************************************************************** ++* SCSI Target values ++****************************************************************************/ ++ ++ case MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX: ++ desc = "target invalid io index"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_ABORTED: ++ desc = "target aborted"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE: ++ desc = "target no conn retryable"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_NO_CONNECTION: ++ desc = "target no connection"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH: ++ desc = "target xfer count mismatch"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR: ++ desc = "target data offset error"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA: ++ desc = "target too much write data"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT: ++ desc = "target iu too short"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT: ++ desc = "target ack nak timeout"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_NAK_RECEIVED: ++ desc = "target nak received"; ++ break; ++ ++/**************************************************************************** ++* Serial Attached SCSI values ++****************************************************************************/ ++ ++ case MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED: ++ desc = "smp request failed"; ++ break; ++ case MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN: ++ desc = "smp data overrun"; ++ break; ++ ++/**************************************************************************** ++* Diagnostic Buffer Post / Diagnostic Release values ++****************************************************************************/ ++ ++ case MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED: ++ desc = "diagnostic released"; ++ break; ++ default: ++ break; ++ } ++ ++ if (!desc) ++ return; ++ ++ switch (request_hdr->Function) { ++ case MPI2_FUNCTION_CONFIG: ++ frame_sz = sizeof(Mpi2ConfigRequest_t) + ioc->sge_size; ++ func_str = "config_page"; ++ break; ++ case MPI2_FUNCTION_SCSI_TASK_MGMT: ++ frame_sz = sizeof(Mpi2SCSITaskManagementRequest_t); ++ func_str = "task_mgmt"; ++ break; ++ case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: ++ frame_sz = sizeof(Mpi2SasIoUnitControlRequest_t); ++ func_str = "sas_iounit_ctl"; ++ break; ++ case MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR: ++ frame_sz = sizeof(Mpi2SepRequest_t); ++ func_str = "enclosure"; ++ break; ++ case MPI2_FUNCTION_IOC_INIT: ++ frame_sz = sizeof(Mpi2IOCInitRequest_t); ++ func_str = "ioc_init"; ++ break; ++ case MPI2_FUNCTION_PORT_ENABLE: ++ frame_sz = sizeof(Mpi2PortEnableRequest_t); ++ func_str = "port_enable"; ++ break; ++ case MPI2_FUNCTION_SMP_PASSTHROUGH: ++ frame_sz = sizeof(Mpi2SmpPassthroughRequest_t) + ioc->sge_size; ++ func_str = "smp_passthru"; ++ break; ++ default: ++ frame_sz = 32; ++ func_str = "unknown"; ++ break; ++ } ++ ++ pr_warn(MPT3SAS_FMT "ioc_status: %s(0x%04x), request(0x%p),(%s)\n", ++ ioc->name, desc, ioc_status, request_hdr, func_str); ++ ++ _debug_dump_mf(request_hdr, frame_sz/4); ++} ++ ++/** ++ * _base_display_event_data - verbose translation of firmware asyn events ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * ++ * Return nothing. ++ */ ++static void ++_base_display_event_data(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventNotificationReply_t *mpi_reply) ++{ ++ char *desc = NULL; ++ u16 event; ++ ++ if (!(ioc->logging_level & MPT_DEBUG_EVENTS)) ++ return; ++ ++ event = le16_to_cpu(mpi_reply->Event); ++ ++ switch (event) { ++ case MPI2_EVENT_LOG_DATA: ++ desc = "Log Data"; ++ break; ++ case MPI2_EVENT_STATE_CHANGE: ++ desc = "Status Change"; ++ break; ++ case MPI2_EVENT_HARD_RESET_RECEIVED: ++ desc = "Hard Reset Received"; ++ break; ++ case MPI2_EVENT_EVENT_CHANGE: ++ desc = "Event Change"; ++ break; ++ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: ++ desc = "Device Status Change"; ++ break; ++ case MPI2_EVENT_IR_OPERATION_STATUS: ++ if (!ioc->hide_ir_msg) ++ desc = "IR Operation Status"; ++ break; ++ case MPI2_EVENT_SAS_DISCOVERY: ++ { ++ Mpi2EventDataSasDiscovery_t *event_data = ++ (Mpi2EventDataSasDiscovery_t *)mpi_reply->EventData; ++ pr_info(MPT3SAS_FMT "Discovery: (%s)", ioc->name, ++ (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ? ++ "start" : "stop"); ++ if (event_data->DiscoveryStatus) ++ pr_info("discovery_status(0x%08x)", ++ le32_to_cpu(event_data->DiscoveryStatus)); ++ pr_info("\n"); ++ return; ++ } ++ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: ++ desc = "SAS Broadcast Primitive"; ++ break; ++ case MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE: ++ desc = "SAS Init Device Status Change"; ++ break; ++ case MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW: ++ desc = "SAS Init Table Overflow"; ++ break; ++ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: ++ desc = "SAS Topology Change List"; ++ break; ++ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: ++ desc = "SAS Enclosure Device Status Change"; ++ break; ++ case MPI2_EVENT_IR_VOLUME: ++ if (!ioc->hide_ir_msg) ++ desc = "IR Volume"; ++ break; ++ case MPI2_EVENT_IR_PHYSICAL_DISK: ++ if (!ioc->hide_ir_msg) ++ desc = "IR Physical Disk"; ++ break; ++ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: ++ if (!ioc->hide_ir_msg) ++ desc = "IR Configuration Change List"; ++ break; ++ case MPI2_EVENT_LOG_ENTRY_ADDED: ++ if (!ioc->hide_ir_msg) ++ desc = "Log Entry Added"; ++ break; ++ case MPI2_EVENT_TEMP_THRESHOLD: ++ desc = "Temperature Threshold"; ++ break; ++ case MPI2_EVENT_ACTIVE_CABLE_EXCEPTION: ++ desc = "Active cable exception"; ++ break; ++ } ++ ++ if (!desc) ++ return; ++ ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, desc); ++} ++ ++/** ++ * _base_sas_log_info - verbose translation of firmware log info ++ * @ioc: per adapter object ++ * @log_info: log info ++ * ++ * Return nothing. ++ */ ++static void ++_base_sas_log_info(struct MPT3SAS_ADAPTER *ioc , u32 log_info) ++{ ++ union loginfo_type { ++ u32 loginfo; ++ struct { ++ u32 subcode:16; ++ u32 code:8; ++ u32 originator:4; ++ u32 bus_type:4; ++ } dw; ++ }; ++ union loginfo_type sas_loginfo; ++ char *originator_str = NULL; ++ ++ sas_loginfo.loginfo = log_info; ++ if (sas_loginfo.dw.bus_type != 3 /*SAS*/) ++ return; ++ ++ /* each nexus loss loginfo */ ++ if (log_info == 0x31170000) ++ return; ++ ++ /* eat the loginfos associated with task aborts */ ++ if (ioc->ignore_loginfos && (log_info == 0x30050000 || log_info == ++ 0x31140000 || log_info == 0x31130000)) ++ return; ++ ++ switch (sas_loginfo.dw.originator) { ++ case 0: ++ originator_str = "IOP"; ++ break; ++ case 1: ++ originator_str = "PL"; ++ break; ++ case 2: ++ if (!ioc->hide_ir_msg) ++ originator_str = "IR"; ++ else ++ originator_str = "WarpDrive"; ++ break; ++ } ++ ++ pr_warn(MPT3SAS_FMT ++ "log_info(0x%08x): originator(%s), code(0x%02x), sub_code(0x%04x)\n", ++ ioc->name, log_info, ++ originator_str, sas_loginfo.dw.code, ++ sas_loginfo.dw.subcode); ++} ++ ++/** ++ * _base_display_reply_info - ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * ++ * Return nothing. ++ */ ++static void ++_base_display_reply_info(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ u16 ioc_status; ++ u32 loginfo = 0; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (unlikely(!mpi_reply)) { ++ pr_err(MPT3SAS_FMT "mpi_reply not valid at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus); ++ ++ if ((ioc_status & MPI2_IOCSTATUS_MASK) && ++ (ioc->logging_level & MPT_DEBUG_REPLY)) { ++ _base_sas_ioc_info(ioc , mpi_reply, ++ mpt2sas_base_get_msg_frame(ioc, smid)); ++ } ++ ++ if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) { ++ loginfo = le32_to_cpu(mpi_reply->IOCLogInfo); ++ _base_sas_log_info(ioc, loginfo); ++ } ++ ++ if (ioc_status || loginfo) { ++ ioc_status &= MPI2_IOCSTATUS_MASK; ++ mpt2sas_trigger_mpi(ioc, ioc_status, loginfo); ++ } ++} ++ ++/** ++ * mpt2sas_base_done - base internal command completion routine ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_base_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (mpi_reply && mpi_reply->Function == MPI2_FUNCTION_EVENT_ACK) ++ return mpt2sas_check_for_pending_internal_cmds(ioc, smid); ++ ++ if (ioc->base_cmds.status == MPT3_CMD_NOT_USED) ++ return 1; ++ ++ ioc->base_cmds.status |= MPT3_CMD_COMPLETE; ++ if (mpi_reply) { ++ ioc->base_cmds.status |= MPT3_CMD_REPLY_VALID; ++ memcpy(ioc->base_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); ++ } ++ ioc->base_cmds.status &= ~MPT3_CMD_PENDING; ++ ++ complete(&ioc->base_cmds.done); ++ return 1; ++} ++ ++/** ++ * _base_async_event - main callback handler for firmware asyn events ++ * @ioc: per adapter object ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_base_async_event(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, u32 reply) ++{ ++ Mpi2EventNotificationReply_t *mpi_reply; ++ Mpi2EventAckRequest_t *ack_request; ++ u16 smid; ++ struct _event_ack_list *delayed_event_ack; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (!mpi_reply) ++ return 1; ++ if (mpi_reply->Function != MPI2_FUNCTION_EVENT_NOTIFICATION) ++ return 1; ++ ++ _base_display_event_data(ioc, mpi_reply); ++ ++ if (!(mpi_reply->AckRequired & MPI2_EVENT_NOTIFICATION_ACK_REQUIRED)) ++ goto out; ++ smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); ++ if (!smid) { ++ delayed_event_ack = kzalloc(sizeof(*delayed_event_ack), ++ GFP_ATOMIC); ++ if (!delayed_event_ack) ++ goto out; ++ INIT_LIST_HEAD(&delayed_event_ack->list); ++ delayed_event_ack->Event = mpi_reply->Event; ++ delayed_event_ack->EventContext = mpi_reply->EventContext; ++ list_add_tail(&delayed_event_ack->list, ++ &ioc->delayed_event_ack_list); ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "DELAYED: EVENT ACK: event (0x%04x)\n", ++ ioc->name, le16_to_cpu(mpi_reply->Event))); ++ goto out; ++ } ++ ++ ack_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ memset(ack_request, 0, sizeof(Mpi2EventAckRequest_t)); ++ ack_request->Function = MPI2_FUNCTION_EVENT_ACK; ++ ack_request->Event = mpi_reply->Event; ++ ack_request->EventContext = mpi_reply->EventContext; ++ ack_request->VF_ID = 0; /* TODO */ ++ ack_request->VP_ID = 0; ++ mpt2sas_base_put_smid_default(ioc, smid); ++ ++ out: ++ ++ /* scsih callback handler */ ++ mpt2sas_scsih_event_callback(ioc, msix_index, reply); ++ ++ /* ctl callback handler */ ++ mpt2sas_ctl_event_callback(ioc, msix_index, reply); ++ ++ return 1; ++} ++ ++/** ++ * _base_get_cb_idx - obtain the callback index ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Return callback index. ++ */ ++static u8 ++_base_get_cb_idx(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ int i; ++ u8 cb_idx; ++ ++ if (smid < ioc->hi_priority_smid) { ++ i = smid - 1; ++ cb_idx = ioc->scsi_lookup[i].cb_idx; ++ } else if (smid < ioc->internal_smid) { ++ i = smid - ioc->hi_priority_smid; ++ cb_idx = ioc->hpr_lookup[i].cb_idx; ++ } else if (smid <= ioc->hba_queue_depth) { ++ i = smid - ioc->internal_smid; ++ cb_idx = ioc->internal_lookup[i].cb_idx; ++ } else ++ cb_idx = 0xFF; ++ return cb_idx; ++} ++ ++/** ++ * _base_mask_interrupts - disable interrupts ++ * @ioc: per adapter object ++ * ++ * Disabling ResetIRQ, Reply and Doorbell Interrupts ++ * ++ * Return nothing. ++ */ ++static void ++_base_mask_interrupts(struct MPT3SAS_ADAPTER *ioc) ++{ ++ u32 him_register; ++ ++ ioc->mask_interrupts = 1; ++ him_register = readl(&ioc->chip->HostInterruptMask); ++ him_register |= MPI2_HIM_DIM + MPI2_HIM_RIM + MPI2_HIM_RESET_IRQ_MASK; ++ writel(him_register, &ioc->chip->HostInterruptMask); ++ readl(&ioc->chip->HostInterruptMask); ++} ++ ++/** ++ * _base_unmask_interrupts - enable interrupts ++ * @ioc: per adapter object ++ * ++ * Enabling only Reply Interrupts ++ * ++ * Return nothing. ++ */ ++static void ++_base_unmask_interrupts(struct MPT3SAS_ADAPTER *ioc) ++{ ++ u32 him_register; ++ ++ him_register = readl(&ioc->chip->HostInterruptMask); ++ him_register &= ~MPI2_HIM_RIM; ++ writel(him_register, &ioc->chip->HostInterruptMask); ++ ioc->mask_interrupts = 0; ++} ++ ++union reply_descriptor { ++ u64 word; ++ struct { ++ u32 low; ++ u32 high; ++ } u; ++}; ++ ++/** ++ * _base_interrupt - MPT adapter (IOC) specific interrupt handler. ++ * @irq: irq number (not used) ++ * @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure ++ * @r: pt_regs pointer (not used) ++ * ++ * Return IRQ_HANDLE if processed, else IRQ_NONE. ++ */ ++static irqreturn_t ++_base_interrupt(int irq, void *bus_id) ++{ ++ struct adapter_reply_queue *reply_q = bus_id; ++ union reply_descriptor rd; ++ u32 completed_cmds; ++ u8 request_desript_type; ++ u16 smid; ++ u8 cb_idx; ++ u32 reply; ++ u8 msix_index = reply_q->msix_index; ++ struct MPT3SAS_ADAPTER *ioc = reply_q->ioc; ++ Mpi2ReplyDescriptorsUnion_t *rpf; ++ u8 rc; ++ ++ if (ioc->mask_interrupts) ++ return IRQ_NONE; ++ ++ if (!atomic_add_unless(&reply_q->busy, 1, 1)) ++ return IRQ_NONE; ++ ++ rpf = &reply_q->reply_post_free[reply_q->reply_post_host_index]; ++ request_desript_type = rpf->Default.ReplyFlags ++ & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; ++ if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) { ++ atomic_dec(&reply_q->busy); ++ return IRQ_NONE; ++ } ++ ++ completed_cmds = 0; ++ cb_idx = 0xFF; ++ do { ++ rd.word = le64_to_cpu(rpf->Words); ++ if (rd.u.low == UINT_MAX || rd.u.high == UINT_MAX) ++ goto out; ++ reply = 0; ++ smid = le16_to_cpu(rpf->Default.DescriptorTypeDependent1); ++ if (request_desript_type == ++ MPI25_RPY_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO_SUCCESS || ++ request_desript_type == ++ MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS) { ++ cb_idx = _base_get_cb_idx(ioc, smid); ++ if ((likely(cb_idx < MPT_MAX_CALLBACKS)) && ++ (likely(mpt_callbacks[cb_idx] != NULL))) { ++ rc = mpt_callbacks[cb_idx](ioc, smid, ++ msix_index, 0); ++ if (rc) ++ mpt2sas_base_free_smid(ioc, smid); ++ } ++ } else if (request_desript_type == ++ MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) { ++ reply = le32_to_cpu( ++ rpf->AddressReply.ReplyFrameAddress); ++ if (reply > ioc->reply_dma_max_address || ++ reply < ioc->reply_dma_min_address) ++ reply = 0; ++ if (smid) { ++ cb_idx = _base_get_cb_idx(ioc, smid); ++ if ((likely(cb_idx < MPT_MAX_CALLBACKS)) && ++ (likely(mpt_callbacks[cb_idx] != NULL))) { ++ rc = mpt_callbacks[cb_idx](ioc, smid, ++ msix_index, reply); ++ if (reply) ++ _base_display_reply_info(ioc, ++ smid, msix_index, reply); ++ if (rc) ++ mpt2sas_base_free_smid(ioc, ++ smid); ++ } ++ } else { ++ _base_async_event(ioc, msix_index, reply); ++ } ++ ++ /* reply free queue handling */ ++ if (reply) { ++ ioc->reply_free_host_index = ++ (ioc->reply_free_host_index == ++ (ioc->reply_free_queue_depth - 1)) ? ++ 0 : ioc->reply_free_host_index + 1; ++ ioc->reply_free[ioc->reply_free_host_index] = ++ cpu_to_le32(reply); ++ wmb(); ++ writel(ioc->reply_free_host_index, ++ &ioc->chip->ReplyFreeHostIndex); ++ } ++ } ++ ++ rpf->Words = cpu_to_le64(ULLONG_MAX); ++ reply_q->reply_post_host_index = ++ (reply_q->reply_post_host_index == ++ (ioc->reply_post_queue_depth - 1)) ? 0 : ++ reply_q->reply_post_host_index + 1; ++ request_desript_type = ++ reply_q->reply_post_free[reply_q->reply_post_host_index]. ++ Default.ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; ++ completed_cmds++; ++ if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) ++ goto out; ++ if (!reply_q->reply_post_host_index) ++ rpf = reply_q->reply_post_free; ++ else ++ rpf++; ++ } while (1); ++ ++ out: ++ ++ if (!completed_cmds) { ++ atomic_dec(&reply_q->busy); ++ return IRQ_NONE; ++ } ++ ++ wmb(); ++ if (ioc->is_warpdrive) { ++ writel(reply_q->reply_post_host_index, ++ ioc->reply_post_host_index[msix_index]); ++ atomic_dec(&reply_q->busy); ++ return IRQ_HANDLED; ++ } ++ ++ /* Update Reply Post Host Index. ++ * For those HBA's which support combined reply queue feature ++ * 1. Get the correct Supplemental Reply Post Host Index Register. ++ * i.e. (msix_index / 8)th entry from Supplemental Reply Post Host ++ * Index Register address bank i.e replyPostRegisterIndex[], ++ * 2. Then update this register with new reply host index value ++ * in ReplyPostIndex field and the MSIxIndex field with ++ * msix_index value reduced to a value between 0 and 7, ++ * using a modulo 8 operation. Since each Supplemental Reply Post ++ * Host Index Register supports 8 MSI-X vectors. ++ * ++ * For other HBA's just update the Reply Post Host Index register with ++ * new reply host index value in ReplyPostIndex Field and msix_index ++ * value in MSIxIndex field. ++ */ ++ if (ioc->msix96_vector) ++ writel(reply_q->reply_post_host_index | ((msix_index & 7) << ++ MPI2_RPHI_MSIX_INDEX_SHIFT), ++ ioc->replyPostRegisterIndex[msix_index/8]); ++ else ++ writel(reply_q->reply_post_host_index | (msix_index << ++ MPI2_RPHI_MSIX_INDEX_SHIFT), ++ &ioc->chip->ReplyPostHostIndex); ++ atomic_dec(&reply_q->busy); ++ return IRQ_HANDLED; ++} ++ ++/** ++ * _base_is_controller_msix_enabled - is controller support muli-reply queues ++ * @ioc: per adapter object ++ * ++ */ ++static inline int ++_base_is_controller_msix_enabled(struct MPT3SAS_ADAPTER *ioc) ++{ ++ return (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable; ++} ++ ++/** ++ * mpt2sas_base_sync_reply_irqs - flush pending MSIX interrupts ++ * @ioc: per adapter object ++ * Context: non ISR conext ++ * ++ * Called when a Task Management request has completed. ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_sync_reply_irqs(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct adapter_reply_queue *reply_q; ++ ++ /* If MSIX capability is turned off ++ * then multi-queues are not enabled ++ */ ++ if (!_base_is_controller_msix_enabled(ioc)) ++ return; ++ ++ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { ++ if (ioc->shost_recovery || ioc->remove_host || ++ ioc->pci_error_recovery) ++ return; ++ /* TMs are on msix_index == 0 */ ++ if (reply_q->msix_index == 0) ++ continue; ++ synchronize_irq(reply_q->vector); ++ } ++} ++ ++/** ++ * mpt2sas_base_release_callback_handler - clear interrupt callback handler ++ * @cb_idx: callback index ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_release_callback_handler(u8 cb_idx) ++{ ++ mpt_callbacks[cb_idx] = NULL; ++} ++ ++/** ++ * mpt2sas_base_register_callback_handler - obtain index for the interrupt callback handler ++ * @cb_func: callback function ++ * ++ * Returns cb_func. ++ */ ++u8 ++mpt2sas_base_register_callback_handler(MPT_CALLBACK cb_func) ++{ ++ u8 cb_idx; ++ ++ for (cb_idx = MPT_MAX_CALLBACKS-1; cb_idx; cb_idx--) ++ if (mpt_callbacks[cb_idx] == NULL) ++ break; ++ ++ mpt_callbacks[cb_idx] = cb_func; ++ return cb_idx; ++} ++ ++/** ++ * mpt2sas_base_initialize_callback_handler - initialize the interrupt callback handler ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_initialize_callback_handler(void) ++{ ++ u8 cb_idx; ++ ++ for (cb_idx = 0; cb_idx < MPT_MAX_CALLBACKS; cb_idx++) ++ mpt2sas_base_release_callback_handler(cb_idx); ++} ++ ++ ++/** ++ * _base_build_zero_len_sge - build zero length sg entry ++ * @ioc: per adapter object ++ * @paddr: virtual address for SGE ++ * ++ * Create a zero length scatter gather entry to insure the IOCs hardware has ++ * something to use if the target device goes brain dead and tries ++ * to send data even when none is asked for. ++ * ++ * Return nothing. ++ */ ++static void ++_base_build_zero_len_sge(struct MPT3SAS_ADAPTER *ioc, void *paddr) ++{ ++ u32 flags_length = (u32)((MPI2_SGE_FLAGS_LAST_ELEMENT | ++ MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST | ++ MPI2_SGE_FLAGS_SIMPLE_ELEMENT) << ++ MPI2_SGE_FLAGS_SHIFT); ++ ioc->base_add_sg_single(paddr, flags_length, -1); ++} ++ ++/** ++ * _base_add_sg_single_32 - Place a simple 32 bit SGE at address pAddr. ++ * @paddr: virtual address for SGE ++ * @flags_length: SGE flags and data transfer length ++ * @dma_addr: Physical address ++ * ++ * Return nothing. ++ */ ++static void ++_base_add_sg_single_32(void *paddr, u32 flags_length, dma_addr_t dma_addr) ++{ ++ Mpi2SGESimple32_t *sgel = paddr; ++ ++ flags_length |= (MPI2_SGE_FLAGS_32_BIT_ADDRESSING | ++ MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT; ++ sgel->FlagsLength = cpu_to_le32(flags_length); ++ sgel->Address = cpu_to_le32(dma_addr); ++} ++ ++ ++/** ++ * _base_add_sg_single_64 - Place a simple 64 bit SGE at address pAddr. ++ * @paddr: virtual address for SGE ++ * @flags_length: SGE flags and data transfer length ++ * @dma_addr: Physical address ++ * ++ * Return nothing. ++ */ ++static void ++_base_add_sg_single_64(void *paddr, u32 flags_length, dma_addr_t dma_addr) ++{ ++ Mpi2SGESimple64_t *sgel = paddr; ++ ++ flags_length |= (MPI2_SGE_FLAGS_64_BIT_ADDRESSING | ++ MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT; ++ sgel->FlagsLength = cpu_to_le32(flags_length); ++ sgel->Address = cpu_to_le64(dma_addr); ++} ++ ++/** ++ * _base_get_chain_buffer_tracker - obtain chain tracker ++ * @ioc: per adapter object ++ * @smid: smid associated to an IO request ++ * ++ * Returns chain tracker(from ioc->free_chain_list) ++ */ ++static struct chain_tracker * ++_base_get_chain_buffer_tracker(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ struct chain_tracker *chain_req; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ if (list_empty(&ioc->free_chain_list)) { ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "chain buffers not available\n", ioc->name)); ++ return NULL; ++ } ++ chain_req = list_entry(ioc->free_chain_list.next, ++ struct chain_tracker, tracker_list); ++ list_del_init(&chain_req->tracker_list); ++ list_add_tail(&chain_req->tracker_list, ++ &ioc->scsi_lookup[smid - 1].chain_list); ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return chain_req; ++} ++ ++ ++/** ++ * _base_build_sg - build generic sg ++ * @ioc: per adapter object ++ * @psge: virtual address for SGE ++ * @data_out_dma: physical address for WRITES ++ * @data_out_sz: data xfer size for WRITES ++ * @data_in_dma: physical address for READS ++ * @data_in_sz: data xfer size for READS ++ * ++ * Return nothing. ++ */ ++static void ++_base_build_sg(struct MPT3SAS_ADAPTER *ioc, void *psge, ++ dma_addr_t data_out_dma, size_t data_out_sz, dma_addr_t data_in_dma, ++ size_t data_in_sz) ++{ ++ u32 sgl_flags; ++ ++ if (!data_out_sz && !data_in_sz) { ++ _base_build_zero_len_sge(ioc, psge); ++ return; ++ } ++ ++ if (data_out_sz && data_in_sz) { ++ /* WRITE sgel first */ ++ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC); ++ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; ++ ioc->base_add_sg_single(psge, sgl_flags | ++ data_out_sz, data_out_dma); ++ ++ /* incr sgel */ ++ psge += ioc->sge_size; ++ ++ /* READ sgel last */ ++ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | ++ MPI2_SGE_FLAGS_END_OF_LIST); ++ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; ++ ioc->base_add_sg_single(psge, sgl_flags | ++ data_in_sz, data_in_dma); ++ } else if (data_out_sz) /* WRITE */ { ++ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | ++ MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC); ++ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; ++ ioc->base_add_sg_single(psge, sgl_flags | ++ data_out_sz, data_out_dma); ++ } else if (data_in_sz) /* READ */ { ++ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | ++ MPI2_SGE_FLAGS_END_OF_LIST); ++ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; ++ ioc->base_add_sg_single(psge, sgl_flags | ++ data_in_sz, data_in_dma); ++ } ++} ++ ++/* IEEE format sgls */ ++ ++/** ++ * _base_add_sg_single_ieee - add sg element for IEEE format ++ * @paddr: virtual address for SGE ++ * @flags: SGE flags ++ * @chain_offset: number of 128 byte elements from start of segment ++ * @length: data transfer length ++ * @dma_addr: Physical address ++ * ++ * Return nothing. ++ */ ++static void ++_base_add_sg_single_ieee(void *paddr, u8 flags, u8 chain_offset, u32 length, ++ dma_addr_t dma_addr) ++{ ++ Mpi25IeeeSgeChain64_t *sgel = paddr; ++ ++ sgel->Flags = flags; ++ sgel->NextChainOffset = chain_offset; ++ sgel->Length = cpu_to_le32(length); ++ sgel->Address = cpu_to_le64(dma_addr); ++} ++ ++/** ++ * _base_build_zero_len_sge_ieee - build zero length sg entry for IEEE format ++ * @ioc: per adapter object ++ * @paddr: virtual address for SGE ++ * ++ * Create a zero length scatter gather entry to insure the IOCs hardware has ++ * something to use if the target device goes brain dead and tries ++ * to send data even when none is asked for. ++ * ++ * Return nothing. ++ */ ++static void ++_base_build_zero_len_sge_ieee(struct MPT3SAS_ADAPTER *ioc, void *paddr) ++{ ++ u8 sgl_flags = (MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR | ++ MPI25_IEEE_SGE_FLAGS_END_OF_LIST); ++ ++ _base_add_sg_single_ieee(paddr, sgl_flags, 0, 0, -1); ++} ++ ++/** ++ * _base_build_sg_scmd - main sg creation routine ++ * @ioc: per adapter object ++ * @scmd: scsi command ++ * @smid: system request message index ++ * Context: none. ++ * ++ * The main routine that builds scatter gather table from a given ++ * scsi request sent via the .queuecommand main handler. ++ * ++ * Returns 0 success, anything else error ++ */ ++static int ++_base_build_sg_scmd(struct MPT3SAS_ADAPTER *ioc, ++ struct scsi_cmnd *scmd, u16 smid) ++{ ++ Mpi2SCSIIORequest_t *mpi_request; ++ dma_addr_t chain_dma; ++ struct scatterlist *sg_scmd; ++ void *sg_local, *chain; ++ u32 chain_offset; ++ u32 chain_length; ++ u32 chain_flags; ++ int sges_left; ++ u32 sges_in_segment; ++ u32 sgl_flags; ++ u32 sgl_flags_last_element; ++ u32 sgl_flags_end_buffer; ++ struct chain_tracker *chain_req; ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ++ /* init scatter gather flags */ ++ sgl_flags = MPI2_SGE_FLAGS_SIMPLE_ELEMENT; ++ if (scmd->sc_data_direction == DMA_TO_DEVICE) ++ sgl_flags |= MPI2_SGE_FLAGS_HOST_TO_IOC; ++ sgl_flags_last_element = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT) ++ << MPI2_SGE_FLAGS_SHIFT; ++ sgl_flags_end_buffer = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT | ++ MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST) ++ << MPI2_SGE_FLAGS_SHIFT; ++ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; ++ ++ sg_scmd = scsi_sglist(scmd); ++ sges_left = scsi_dma_map(scmd); ++ if (sges_left < 0) { ++ sdev_printk(KERN_ERR, scmd->device, ++ "pci_map_sg failed: request for %d bytes!\n", ++ scsi_bufflen(scmd)); ++ return -ENOMEM; ++ } ++ ++ sg_local = &mpi_request->SGL; ++ sges_in_segment = ioc->max_sges_in_main_message; ++ if (sges_left <= sges_in_segment) ++ goto fill_in_last_segment; ++ ++ mpi_request->ChainOffset = (offsetof(Mpi2SCSIIORequest_t, SGL) + ++ (sges_in_segment * ioc->sge_size))/4; ++ ++ /* fill in main message segment when there is a chain following */ ++ while (sges_in_segment) { ++ if (sges_in_segment == 1) ++ ioc->base_add_sg_single(sg_local, ++ sgl_flags_last_element | sg_dma_len(sg_scmd), ++ sg_dma_address(sg_scmd)); ++ else ++ ioc->base_add_sg_single(sg_local, sgl_flags | ++ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); ++ sg_scmd = sg_next(sg_scmd); ++ sg_local += ioc->sge_size; ++ sges_left--; ++ sges_in_segment--; ++ } ++ ++ /* initializing the chain flags and pointers */ ++ chain_flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT << MPI2_SGE_FLAGS_SHIFT; ++ chain_req = _base_get_chain_buffer_tracker(ioc, smid); ++ if (!chain_req) ++ return -1; ++ chain = chain_req->chain_buffer; ++ chain_dma = chain_req->chain_buffer_dma; ++ do { ++ sges_in_segment = (sges_left <= ++ ioc->max_sges_in_chain_message) ? sges_left : ++ ioc->max_sges_in_chain_message; ++ chain_offset = (sges_left == sges_in_segment) ? ++ 0 : (sges_in_segment * ioc->sge_size)/4; ++ chain_length = sges_in_segment * ioc->sge_size; ++ if (chain_offset) { ++ chain_offset = chain_offset << ++ MPI2_SGE_CHAIN_OFFSET_SHIFT; ++ chain_length += ioc->sge_size; ++ } ++ ioc->base_add_sg_single(sg_local, chain_flags | chain_offset | ++ chain_length, chain_dma); ++ sg_local = chain; ++ if (!chain_offset) ++ goto fill_in_last_segment; ++ ++ /* fill in chain segments */ ++ while (sges_in_segment) { ++ if (sges_in_segment == 1) ++ ioc->base_add_sg_single(sg_local, ++ sgl_flags_last_element | ++ sg_dma_len(sg_scmd), ++ sg_dma_address(sg_scmd)); ++ else ++ ioc->base_add_sg_single(sg_local, sgl_flags | ++ sg_dma_len(sg_scmd), ++ sg_dma_address(sg_scmd)); ++ sg_scmd = sg_next(sg_scmd); ++ sg_local += ioc->sge_size; ++ sges_left--; ++ sges_in_segment--; ++ } ++ ++ chain_req = _base_get_chain_buffer_tracker(ioc, smid); ++ if (!chain_req) ++ return -1; ++ chain = chain_req->chain_buffer; ++ chain_dma = chain_req->chain_buffer_dma; ++ } while (1); ++ ++ ++ fill_in_last_segment: ++ ++ /* fill the last segment */ ++ while (sges_left) { ++ if (sges_left == 1) ++ ioc->base_add_sg_single(sg_local, sgl_flags_end_buffer | ++ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); ++ else ++ ioc->base_add_sg_single(sg_local, sgl_flags | ++ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); ++ sg_scmd = sg_next(sg_scmd); ++ sg_local += ioc->sge_size; ++ sges_left--; ++ } ++ ++ return 0; ++} ++ ++/** ++ * _base_build_sg_scmd_ieee - main sg creation routine for IEEE format ++ * @ioc: per adapter object ++ * @scmd: scsi command ++ * @smid: system request message index ++ * Context: none. ++ * ++ * The main routine that builds scatter gather table from a given ++ * scsi request sent via the .queuecommand main handler. ++ * ++ * Returns 0 success, anything else error ++ */ ++static int ++_base_build_sg_scmd_ieee(struct MPT3SAS_ADAPTER *ioc, ++ struct scsi_cmnd *scmd, u16 smid) ++{ ++ Mpi2SCSIIORequest_t *mpi_request; ++ dma_addr_t chain_dma; ++ struct scatterlist *sg_scmd; ++ void *sg_local, *chain; ++ u32 chain_offset; ++ u32 chain_length; ++ int sges_left; ++ u32 sges_in_segment; ++ u8 simple_sgl_flags; ++ u8 simple_sgl_flags_last; ++ u8 chain_sgl_flags; ++ struct chain_tracker *chain_req; ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ++ /* init scatter gather flags */ ++ simple_sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; ++ simple_sgl_flags_last = simple_sgl_flags | ++ MPI25_IEEE_SGE_FLAGS_END_OF_LIST; ++ chain_sgl_flags = MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT | ++ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; ++ ++ sg_scmd = scsi_sglist(scmd); ++ sges_left = scsi_dma_map(scmd); ++ if (sges_left < 0) { ++ sdev_printk(KERN_ERR, scmd->device, ++ "pci_map_sg failed: request for %d bytes!\n", ++ scsi_bufflen(scmd)); ++ return -ENOMEM; ++ } ++ ++ sg_local = &mpi_request->SGL; ++ sges_in_segment = (ioc->request_sz - ++ offsetof(Mpi2SCSIIORequest_t, SGL))/ioc->sge_size_ieee; ++ if (sges_left <= sges_in_segment) ++ goto fill_in_last_segment; ++ ++ mpi_request->ChainOffset = (sges_in_segment - 1 /* chain element */) + ++ (offsetof(Mpi2SCSIIORequest_t, SGL)/ioc->sge_size_ieee); ++ ++ /* fill in main message segment when there is a chain following */ ++ while (sges_in_segment > 1) { ++ _base_add_sg_single_ieee(sg_local, simple_sgl_flags, 0, ++ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); ++ sg_scmd = sg_next(sg_scmd); ++ sg_local += ioc->sge_size_ieee; ++ sges_left--; ++ sges_in_segment--; ++ } ++ ++ /* initializing the pointers */ ++ chain_req = _base_get_chain_buffer_tracker(ioc, smid); ++ if (!chain_req) ++ return -1; ++ chain = chain_req->chain_buffer; ++ chain_dma = chain_req->chain_buffer_dma; ++ do { ++ sges_in_segment = (sges_left <= ++ ioc->max_sges_in_chain_message) ? sges_left : ++ ioc->max_sges_in_chain_message; ++ chain_offset = (sges_left == sges_in_segment) ? ++ 0 : sges_in_segment; ++ chain_length = sges_in_segment * ioc->sge_size_ieee; ++ if (chain_offset) ++ chain_length += ioc->sge_size_ieee; ++ _base_add_sg_single_ieee(sg_local, chain_sgl_flags, ++ chain_offset, chain_length, chain_dma); ++ ++ sg_local = chain; ++ if (!chain_offset) ++ goto fill_in_last_segment; ++ ++ /* fill in chain segments */ ++ while (sges_in_segment) { ++ _base_add_sg_single_ieee(sg_local, simple_sgl_flags, 0, ++ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); ++ sg_scmd = sg_next(sg_scmd); ++ sg_local += ioc->sge_size_ieee; ++ sges_left--; ++ sges_in_segment--; ++ } ++ ++ chain_req = _base_get_chain_buffer_tracker(ioc, smid); ++ if (!chain_req) ++ return -1; ++ chain = chain_req->chain_buffer; ++ chain_dma = chain_req->chain_buffer_dma; ++ } while (1); ++ ++ ++ fill_in_last_segment: ++ ++ /* fill the last segment */ ++ while (sges_left > 0) { ++ if (sges_left == 1) ++ _base_add_sg_single_ieee(sg_local, ++ simple_sgl_flags_last, 0, sg_dma_len(sg_scmd), ++ sg_dma_address(sg_scmd)); ++ else ++ _base_add_sg_single_ieee(sg_local, simple_sgl_flags, 0, ++ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); ++ sg_scmd = sg_next(sg_scmd); ++ sg_local += ioc->sge_size_ieee; ++ sges_left--; ++ } ++ ++ return 0; ++} ++ ++/** ++ * _base_build_sg_ieee - build generic sg for IEEE format ++ * @ioc: per adapter object ++ * @psge: virtual address for SGE ++ * @data_out_dma: physical address for WRITES ++ * @data_out_sz: data xfer size for WRITES ++ * @data_in_dma: physical address for READS ++ * @data_in_sz: data xfer size for READS ++ * ++ * Return nothing. ++ */ ++static void ++_base_build_sg_ieee(struct MPT3SAS_ADAPTER *ioc, void *psge, ++ dma_addr_t data_out_dma, size_t data_out_sz, dma_addr_t data_in_dma, ++ size_t data_in_sz) ++{ ++ u8 sgl_flags; ++ ++ if (!data_out_sz && !data_in_sz) { ++ _base_build_zero_len_sge_ieee(ioc, psge); ++ return; ++ } ++ ++ if (data_out_sz && data_in_sz) { ++ /* WRITE sgel first */ ++ sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; ++ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_out_sz, ++ data_out_dma); ++ ++ /* incr sgel */ ++ psge += ioc->sge_size_ieee; ++ ++ /* READ sgel last */ ++ sgl_flags |= MPI25_IEEE_SGE_FLAGS_END_OF_LIST; ++ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_in_sz, ++ data_in_dma); ++ } else if (data_out_sz) /* WRITE */ { ++ sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI25_IEEE_SGE_FLAGS_END_OF_LIST | ++ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; ++ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_out_sz, ++ data_out_dma); ++ } else if (data_in_sz) /* READ */ { ++ sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI25_IEEE_SGE_FLAGS_END_OF_LIST | ++ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; ++ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_in_sz, ++ data_in_dma); ++ } ++} ++ ++#define convert_to_kb(x) ((x) << (PAGE_SHIFT - 10)) ++ ++/** ++ * _base_config_dma_addressing - set dma addressing ++ * @ioc: per adapter object ++ * @pdev: PCI device struct ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_config_dma_addressing(struct MPT3SAS_ADAPTER *ioc, struct pci_dev *pdev) ++{ ++ struct sysinfo s; ++ u64 consistent_dma_mask; ++ ++ if (ioc->dma_mask) ++ consistent_dma_mask = DMA_BIT_MASK(64); ++ else ++ consistent_dma_mask = DMA_BIT_MASK(32); ++ ++ if (sizeof(dma_addr_t) > 4) { ++ const uint64_t required_mask = ++ dma_get_required_mask(&pdev->dev); ++ if ((required_mask > DMA_BIT_MASK(32)) && ++ !pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) && ++ !pci_set_consistent_dma_mask(pdev, consistent_dma_mask)) { ++ ioc->base_add_sg_single = &_base_add_sg_single_64; ++ ioc->sge_size = sizeof(Mpi2SGESimple64_t); ++ ioc->dma_mask = 64; ++ goto out; ++ } ++ } ++ ++ if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) ++ && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { ++ ioc->base_add_sg_single = &_base_add_sg_single_32; ++ ioc->sge_size = sizeof(Mpi2SGESimple32_t); ++ ioc->dma_mask = 32; ++ } else ++ return -ENODEV; ++ ++ out: ++ si_meminfo(&s); ++ pr_info(MPT3SAS_FMT ++ "%d BIT PCI BUS DMA ADDRESSING SUPPORTED, total mem (%ld kB)\n", ++ ioc->name, ioc->dma_mask, convert_to_kb(s.totalram)); ++ ++ return 0; ++} ++ ++static int ++_base_change_consistent_dma_mask(struct MPT3SAS_ADAPTER *ioc, ++ struct pci_dev *pdev) ++{ ++ if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) { ++ if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++/** ++ * _base_check_enable_msix - checks MSIX capabable. ++ * @ioc: per adapter object ++ * ++ * Check to see if card is capable of MSIX, and set number ++ * of available msix vectors ++ */ ++static int ++_base_check_enable_msix(struct MPT3SAS_ADAPTER *ioc) ++{ ++ int base; ++ u16 message_control; ++ ++ /* Check whether controller SAS2008 B0 controller, ++ * if it is SAS2008 B0 controller use IO-APIC instead of MSIX ++ */ ++ if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 && ++ ioc->pdev->revision == SAS2_PCI_DEVICE_B0_REVISION) { ++ return -EINVAL; ++ } ++ ++ base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX); ++ if (!base) { ++ dfailprintk(ioc, pr_info(MPT3SAS_FMT "msix not supported\n", ++ ioc->name)); ++ return -EINVAL; ++ } ++ ++ /* get msix vector count */ ++ /* NUMA_IO not supported for older controllers */ ++ if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2004 || ++ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 || ++ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_1 || ++ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_2 || ++ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_3 || ++ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2116_1 || ++ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2116_2) ++ ioc->msix_vector_count = 1; ++ else { ++ pci_read_config_word(ioc->pdev, base + 2, &message_control); ++ ioc->msix_vector_count = (message_control & 0x3FF) + 1; ++ } ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "msix is supported, vector_count(%d)\n", ++ ioc->name, ioc->msix_vector_count)); ++ return 0; ++} ++ ++/** ++ * _base_free_irq - free irq ++ * @ioc: per adapter object ++ * ++ * Freeing respective reply_queue from the list. ++ */ ++static void ++_base_free_irq(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct adapter_reply_queue *reply_q, *next; ++ ++ if (list_empty(&ioc->reply_queue_list)) ++ return; ++ ++ list_for_each_entry_safe(reply_q, next, &ioc->reply_queue_list, list) { ++ list_del(&reply_q->list); ++ if (smp_affinity_enable) { ++ irq_set_affinity_hint(reply_q->vector, NULL); ++ free_cpumask_var(reply_q->affinity_hint); ++ } ++ free_irq(reply_q->vector, reply_q); ++ kfree(reply_q); ++ } ++} ++ ++/** ++ * _base_request_irq - request irq ++ * @ioc: per adapter object ++ * @index: msix index into vector table ++ * @vector: irq vector ++ * ++ * Inserting respective reply_queue into the list. ++ */ ++static int ++_base_request_irq(struct MPT3SAS_ADAPTER *ioc, u8 index, u32 vector) ++{ ++ struct adapter_reply_queue *reply_q; ++ int r; ++ ++ reply_q = kzalloc(sizeof(struct adapter_reply_queue), GFP_KERNEL); ++ if (!reply_q) { ++ pr_err(MPT3SAS_FMT "unable to allocate memory %d!\n", ++ ioc->name, (int)sizeof(struct adapter_reply_queue)); ++ return -ENOMEM; ++ } ++ reply_q->ioc = ioc; ++ reply_q->msix_index = index; ++ reply_q->vector = vector; ++ ++ if (smp_affinity_enable) { ++ if (!zalloc_cpumask_var(&reply_q->affinity_hint, GFP_KERNEL)) { ++ kfree(reply_q); ++ return -ENOMEM; ++ } ++ } ++ ++ atomic_set(&reply_q->busy, 0); ++ if (ioc->msix_enable) ++ snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d-msix%d", ++ ioc->driver_name, ioc->id, index); ++ else ++ snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d", ++ ioc->driver_name, ioc->id); ++ r = request_irq(vector, _base_interrupt, IRQF_SHARED, reply_q->name, ++ reply_q); ++ if (r) { ++ pr_err(MPT3SAS_FMT "unable to allocate interrupt %d!\n", ++ reply_q->name, vector); ++ free_cpumask_var(reply_q->affinity_hint); ++ kfree(reply_q); ++ return -EBUSY; ++ } ++ ++ INIT_LIST_HEAD(&reply_q->list); ++ list_add_tail(&reply_q->list, &ioc->reply_queue_list); ++ return 0; ++} ++ ++/** ++ * _base_assign_reply_queues - assigning msix index for each cpu ++ * @ioc: per adapter object ++ * ++ * The enduser would need to set the affinity via /proc/irq/#/smp_affinity ++ * ++ * It would nice if we could call irq_set_affinity, however it is not ++ * an exported symbol ++ */ ++static void ++_base_assign_reply_queues(struct MPT3SAS_ADAPTER *ioc) ++{ ++ unsigned int cpu, nr_cpus, nr_msix, index = 0; ++ struct adapter_reply_queue *reply_q; ++ ++ if (!_base_is_controller_msix_enabled(ioc)) ++ return; ++ ++ memset(ioc->cpu_msix_table, 0, ioc->cpu_msix_table_sz); ++ ++ nr_cpus = num_online_cpus(); ++ nr_msix = ioc->reply_queue_count = min(ioc->reply_queue_count, ++ ioc->facts.MaxMSIxVectors); ++ if (!nr_msix) ++ return; ++ ++ cpu = cpumask_first(cpu_online_mask); ++ ++ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { ++ ++ unsigned int i, group = nr_cpus / nr_msix; ++ ++ if (cpu >= nr_cpus) ++ break; ++ ++ if (index < nr_cpus % nr_msix) ++ group++; ++ ++ for (i = 0 ; i < group ; i++) { ++ ioc->cpu_msix_table[cpu] = index; ++ if (smp_affinity_enable) ++ cpumask_or(reply_q->affinity_hint, ++ reply_q->affinity_hint, get_cpu_mask(cpu)); ++ cpu = cpumask_next(cpu, cpu_online_mask); ++ } ++ if (smp_affinity_enable) ++ if (irq_set_affinity_hint(reply_q->vector, ++ reply_q->affinity_hint)) ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "Err setting affinity hint to irq vector %d\n", ++ ioc->name, reply_q->vector)); ++ index++; ++ } ++} ++ ++/** ++ * _base_disable_msix - disables msix ++ * @ioc: per adapter object ++ * ++ */ ++static void ++_base_disable_msix(struct MPT3SAS_ADAPTER *ioc) ++{ ++ if (!ioc->msix_enable) ++ return; ++ pci_disable_msix(ioc->pdev); ++ ioc->msix_enable = 0; ++} ++ ++/** ++ * _base_enable_msix - enables msix, failback to io_apic ++ * @ioc: per adapter object ++ * ++ */ ++static int ++_base_enable_msix(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct msix_entry *entries, *a; ++ int r; ++ int i; ++ u8 try_msix = 0; ++ ++ if (msix_disable == -1 || msix_disable == 0) ++ try_msix = 1; ++ ++ if (!try_msix) ++ goto try_ioapic; ++ ++ if (_base_check_enable_msix(ioc) != 0) ++ goto try_ioapic; ++ ++ ioc->reply_queue_count = min_t(int, ioc->cpu_count, ++ ioc->msix_vector_count); ++ ++ printk(MPT3SAS_FMT "MSI-X vectors supported: %d, no of cores" ++ ": %d, max_msix_vectors: %d\n", ioc->name, ioc->msix_vector_count, ++ ioc->cpu_count, max_msix_vectors); ++ ++ if (!ioc->rdpq_array_enable && max_msix_vectors == -1) ++ max_msix_vectors = 8; ++ ++ if (max_msix_vectors > 0) { ++ ioc->reply_queue_count = min_t(int, max_msix_vectors, ++ ioc->reply_queue_count); ++ ioc->msix_vector_count = ioc->reply_queue_count; ++ } else if (max_msix_vectors == 0) ++ goto try_ioapic; ++ ++ if (ioc->msix_vector_count < ioc->cpu_count) ++ smp_affinity_enable = 0; ++ ++ entries = kcalloc(ioc->reply_queue_count, sizeof(struct msix_entry), ++ GFP_KERNEL); ++ if (!entries) { ++ dfailprintk(ioc, pr_info(MPT3SAS_FMT ++ "kcalloc failed @ at %s:%d/%s() !!!\n", ++ ioc->name, __FILE__, __LINE__, __func__)); ++ goto try_ioapic; ++ } ++ ++ for (i = 0, a = entries; i < ioc->reply_queue_count; i++, a++) ++ a->entry = i; ++ ++ r = pci_enable_msix_exact(ioc->pdev, entries, ioc->reply_queue_count); ++ if (r) { ++ dfailprintk(ioc, pr_info(MPT3SAS_FMT ++ "pci_enable_msix_exact failed (r=%d) !!!\n", ++ ioc->name, r)); ++ kfree(entries); ++ goto try_ioapic; ++ } ++ ++ ioc->msix_enable = 1; ++ for (i = 0, a = entries; i < ioc->reply_queue_count; i++, a++) { ++ r = _base_request_irq(ioc, i, a->vector); ++ if (r) { ++ _base_free_irq(ioc); ++ _base_disable_msix(ioc); ++ kfree(entries); ++ goto try_ioapic; ++ } ++ } ++ ++ kfree(entries); ++ return 0; ++ ++/* failback to io_apic interrupt routing */ ++ try_ioapic: ++ ++ ioc->reply_queue_count = 1; ++ r = _base_request_irq(ioc, 0, ioc->pdev->irq); ++ ++ return r; ++} ++ ++/** ++ * mpt2sas_base_unmap_resources - free controller resources ++ * @ioc: per adapter object ++ */ ++void ++mpt2sas_base_unmap_resources(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct pci_dev *pdev = ioc->pdev; ++ ++ dexitprintk(ioc, printk(MPT3SAS_FMT "%s\n", ++ ioc->name, __func__)); ++ ++ _base_free_irq(ioc); ++ _base_disable_msix(ioc); ++ ++ if (ioc->msix96_vector) { ++ kfree(ioc->replyPostRegisterIndex); ++ ioc->replyPostRegisterIndex = NULL; ++ } ++ ++ if (ioc->chip_phys) { ++ iounmap(ioc->chip); ++ ioc->chip_phys = 0; ++ } ++ ++ if (pci_is_enabled(pdev)) { ++ pci_release_selected_regions(ioc->pdev, ioc->bars); ++ pci_disable_pcie_error_reporting(pdev); ++ pci_disable_device(pdev); ++ } ++} ++ ++/** ++ * mpt2sas_base_map_resources - map in controller resources (io/irq/memap) ++ * @ioc: per adapter object ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct pci_dev *pdev = ioc->pdev; ++ u32 memap_sz; ++ u32 pio_sz; ++ int i, r = 0; ++ u64 pio_chip = 0; ++ u64 chip_phys = 0; ++ struct adapter_reply_queue *reply_q; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ++ ioc->name, __func__)); ++ ++ ioc->bars = pci_select_bars(pdev, IORESOURCE_MEM); ++ if (pci_enable_device_mem(pdev)) { ++ pr_warn(MPT3SAS_FMT "pci_enable_device_mem: failed\n", ++ ioc->name); ++ ioc->bars = 0; ++ return -ENODEV; ++ } ++ ++ ++ if (pci_request_selected_regions(pdev, ioc->bars, ++ ioc->driver_name)) { ++ pr_warn(MPT3SAS_FMT "pci_request_selected_regions: failed\n", ++ ioc->name); ++ ioc->bars = 0; ++ r = -ENODEV; ++ goto out_fail; ++ } ++ ++/* AER (Advanced Error Reporting) hooks */ ++ pci_enable_pcie_error_reporting(pdev); ++ ++ pci_set_master(pdev); ++ ++ ++ if (_base_config_dma_addressing(ioc, pdev) != 0) { ++ pr_warn(MPT3SAS_FMT "no suitable DMA mask for %s\n", ++ ioc->name, pci_name(pdev)); ++ r = -ENODEV; ++ goto out_fail; ++ } ++ ++ for (i = 0, memap_sz = 0, pio_sz = 0; (i < DEVICE_COUNT_RESOURCE) && ++ (!memap_sz || !pio_sz); i++) { ++ if (pci_resource_flags(pdev, i) & IORESOURCE_IO) { ++ if (pio_sz) ++ continue; ++ pio_chip = (u64)pci_resource_start(pdev, i); ++ pio_sz = pci_resource_len(pdev, i); ++ } else if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) { ++ if (memap_sz) ++ continue; ++ ioc->chip_phys = pci_resource_start(pdev, i); ++ chip_phys = (u64)ioc->chip_phys; ++ memap_sz = pci_resource_len(pdev, i); ++ ioc->chip = ioremap(ioc->chip_phys, memap_sz); ++ } ++ } ++ ++ if (ioc->chip == NULL) { ++ pr_err(MPT3SAS_FMT "unable to map adapter memory! " ++ " or resource not found\n", ioc->name); ++ r = -EINVAL; ++ goto out_fail; ++ } ++ ++ _base_mask_interrupts(ioc); ++ ++ r = _base_get_ioc_facts(ioc, CAN_SLEEP); ++ if (r) ++ goto out_fail; ++ ++ if (!ioc->rdpq_array_enable_assigned) { ++ ioc->rdpq_array_enable = ioc->rdpq_array_capable; ++ ioc->rdpq_array_enable_assigned = 1; ++ } ++ ++ r = _base_enable_msix(ioc); ++ if (r) ++ goto out_fail; ++ ++ /* Use the Combined reply queue feature only for SAS3 C0 & higher ++ * revision HBAs and also only when reply queue count is greater than 8 ++ */ ++ if (ioc->msix96_vector && ioc->reply_queue_count > 8) { ++ /* Determine the Supplemental Reply Post Host Index Registers ++ * Addresse. Supplemental Reply Post Host Index Registers ++ * starts at offset MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET and ++ * each register is at offset bytes of ++ * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET from previous one. ++ */ ++ ioc->replyPostRegisterIndex = kcalloc( ++ MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT, ++ sizeof(resource_size_t *), GFP_KERNEL); ++ if (!ioc->replyPostRegisterIndex) { ++ dfailprintk(ioc, printk(MPT3SAS_FMT ++ "allocation for reply Post Register Index failed!!!\n", ++ ioc->name)); ++ r = -ENOMEM; ++ goto out_fail; ++ } ++ ++ for (i = 0; i < MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT; i++) { ++ ioc->replyPostRegisterIndex[i] = (resource_size_t *) ++ ((u8 *)&ioc->chip->Doorbell + ++ MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET + ++ (i * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET)); ++ } ++ } else ++ ioc->msix96_vector = 0; ++ ++ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) ++ pr_info(MPT3SAS_FMT "%s: IRQ %d\n", ++ reply_q->name, ((ioc->msix_enable) ? "PCI-MSI-X enabled" : ++ "IO-APIC enabled"), reply_q->vector); ++ ++ pr_info(MPT3SAS_FMT "iomem(0x%016llx), mapped(0x%p), size(%d)\n", ++ ioc->name, (unsigned long long)chip_phys, ioc->chip, memap_sz); ++ pr_info(MPT3SAS_FMT "ioport(0x%016llx), size(%d)\n", ++ ioc->name, (unsigned long long)pio_chip, pio_sz); ++ ++ /* Save PCI configuration state for recovery from PCI AER/EEH errors */ ++ pci_save_state(pdev); ++ return 0; ++ ++ out_fail: ++ mpt2sas_base_unmap_resources(ioc); ++ return r; ++} ++ ++/** ++ * mpt2sas_base_get_msg_frame - obtain request mf pointer ++ * @ioc: per adapter object ++ * @smid: system request message index(smid zero is invalid) ++ * ++ * Returns virt pointer to message frame. ++ */ ++void * ++mpt2sas_base_get_msg_frame(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ return (void *)(ioc->request + (smid * ioc->request_sz)); ++} ++ ++/** ++ * mpt2sas_base_get_sense_buffer - obtain a sense buffer virt addr ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Returns virt pointer to sense buffer. ++ */ ++void * ++mpt2sas_base_get_sense_buffer(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ return (void *)(ioc->sense + ((smid - 1) * SCSI_SENSE_BUFFERSIZE)); ++} ++ ++/** ++ * mpt2sas_base_get_sense_buffer_dma - obtain a sense buffer dma addr ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Returns phys pointer to the low 32bit address of the sense buffer. ++ */ ++__le32 ++mpt2sas_base_get_sense_buffer_dma(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ return cpu_to_le32(ioc->sense_dma + ((smid - 1) * ++ SCSI_SENSE_BUFFERSIZE)); ++} ++ ++/** ++ * mpt2sas_base_get_reply_virt_addr - obtain reply frames virt address ++ * @ioc: per adapter object ++ * @phys_addr: lower 32 physical addr of the reply ++ * ++ * Converts 32bit lower physical addr into a virt address. ++ */ ++void * ++mpt2sas_base_get_reply_virt_addr(struct MPT3SAS_ADAPTER *ioc, u32 phys_addr) ++{ ++ if (!phys_addr) ++ return NULL; ++ return ioc->reply + (phys_addr - (u32)ioc->reply_dma); ++} ++ ++static inline u8 ++_base_get_msix_index(struct MPT3SAS_ADAPTER *ioc) ++{ ++ return ioc->cpu_msix_table[raw_smp_processor_id()]; ++} ++ ++/** ++ * mpt2sas_base_get_smid - obtain a free smid from internal queue ++ * @ioc: per adapter object ++ * @cb_idx: callback index ++ * ++ * Returns smid (zero is invalid) ++ */ ++u16 ++mpt2sas_base_get_smid(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx) ++{ ++ unsigned long flags; ++ struct request_tracker *request; ++ u16 smid; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ if (list_empty(&ioc->internal_free_list)) { ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ pr_err(MPT3SAS_FMT "%s: smid not available\n", ++ ioc->name, __func__); ++ return 0; ++ } ++ ++ request = list_entry(ioc->internal_free_list.next, ++ struct request_tracker, tracker_list); ++ request->cb_idx = cb_idx; ++ smid = request->smid; ++ list_del(&request->tracker_list); ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return smid; ++} ++ ++/** ++ * mpt2sas_base_get_smid_scsiio - obtain a free smid from scsiio queue ++ * @ioc: per adapter object ++ * @cb_idx: callback index ++ * @scmd: pointer to scsi command object ++ * ++ * Returns smid (zero is invalid) ++ */ ++u16 ++mpt2sas_base_get_smid_scsiio(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx, ++ struct scsi_cmnd *scmd) ++{ ++ unsigned long flags; ++ struct scsiio_tracker *request; ++ u16 smid; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ if (list_empty(&ioc->free_list)) { ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ pr_err(MPT3SAS_FMT "%s: smid not available\n", ++ ioc->name, __func__); ++ return 0; ++ } ++ ++ request = list_entry(ioc->free_list.next, ++ struct scsiio_tracker, tracker_list); ++ request->scmd = scmd; ++ request->cb_idx = cb_idx; ++ smid = request->smid; ++ request->msix_io = _base_get_msix_index(ioc); ++ list_del(&request->tracker_list); ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return smid; ++} ++ ++/** ++ * mpt2sas_base_get_smid_hpr - obtain a free smid from hi-priority queue ++ * @ioc: per adapter object ++ * @cb_idx: callback index ++ * ++ * Returns smid (zero is invalid) ++ */ ++u16 ++mpt2sas_base_get_smid_hpr(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx) ++{ ++ unsigned long flags; ++ struct request_tracker *request; ++ u16 smid; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ if (list_empty(&ioc->hpr_free_list)) { ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return 0; ++ } ++ ++ request = list_entry(ioc->hpr_free_list.next, ++ struct request_tracker, tracker_list); ++ request->cb_idx = cb_idx; ++ smid = request->smid; ++ list_del(&request->tracker_list); ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return smid; ++} ++ ++/** ++ * mpt2sas_base_free_smid - put smid back on free_list ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_free_smid(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ unsigned long flags; ++ int i; ++ struct chain_tracker *chain_req, *next; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ if (smid < ioc->hi_priority_smid) { ++ /* scsiio queue */ ++ i = smid - 1; ++ if (!list_empty(&ioc->scsi_lookup[i].chain_list)) { ++ list_for_each_entry_safe(chain_req, next, ++ &ioc->scsi_lookup[i].chain_list, tracker_list) { ++ list_del_init(&chain_req->tracker_list); ++ list_add(&chain_req->tracker_list, ++ &ioc->free_chain_list); ++ } ++ } ++ ioc->scsi_lookup[i].cb_idx = 0xFF; ++ ioc->scsi_lookup[i].scmd = NULL; ++ ioc->scsi_lookup[i].direct_io = 0; ++ list_add(&ioc->scsi_lookup[i].tracker_list, &ioc->free_list); ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ ++ /* ++ * See _wait_for_commands_to_complete() call with regards ++ * to this code. ++ */ ++ if (ioc->shost_recovery && ioc->pending_io_count) { ++ if (ioc->pending_io_count == 1) ++ wake_up(&ioc->reset_wq); ++ ioc->pending_io_count--; ++ } ++ return; ++ } else if (smid < ioc->internal_smid) { ++ /* hi-priority */ ++ i = smid - ioc->hi_priority_smid; ++ ioc->hpr_lookup[i].cb_idx = 0xFF; ++ list_add(&ioc->hpr_lookup[i].tracker_list, &ioc->hpr_free_list); ++ } else if (smid <= ioc->hba_queue_depth) { ++ /* internal queue */ ++ i = smid - ioc->internal_smid; ++ ioc->internal_lookup[i].cb_idx = 0xFF; ++ list_add(&ioc->internal_lookup[i].tracker_list, ++ &ioc->internal_free_list); ++ } ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++} ++ ++/** ++ * _base_writeq - 64 bit write to MMIO ++ * @ioc: per adapter object ++ * @b: data payload ++ * @addr: address in MMIO space ++ * @writeq_lock: spin lock ++ * ++ * Glue for handling an atomic 64 bit word to MMIO. This special handling takes ++ * care of 32 bit environment where its not quarenteed to send the entire word ++ * in one transfer. ++ */ ++#if defined(writeq) && defined(CONFIG_64BIT) ++static inline void ++_base_writeq(__u64 b, volatile void __iomem *addr, spinlock_t *writeq_lock) ++{ ++ writeq(cpu_to_le64(b), addr); ++} ++#else ++static inline void ++_base_writeq(__u64 b, volatile void __iomem *addr, spinlock_t *writeq_lock) ++{ ++ unsigned long flags; ++ __u64 data_out = cpu_to_le64(b); ++ ++ spin_lock_irqsave(writeq_lock, flags); ++ writel((u32)(data_out), addr); ++ writel((u32)(data_out >> 32), (addr + 4)); ++ spin_unlock_irqrestore(writeq_lock, flags); ++} ++#endif ++ ++/** ++ * mpt2sas_base_put_smid_scsi_io - send SCSI_IO request to firmware ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @handle: device handle ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_put_smid_scsi_io(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 handle) ++{ ++ Mpi2RequestDescriptorUnion_t descriptor; ++ u64 *request = (u64 *)&descriptor; ++ ++ ++ descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; ++ descriptor.SCSIIO.MSIxIndex = _base_get_msix_index(ioc); ++ descriptor.SCSIIO.SMID = cpu_to_le16(smid); ++ descriptor.SCSIIO.DevHandle = cpu_to_le16(handle); ++ descriptor.SCSIIO.LMID = 0; ++ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, ++ &ioc->scsi_lookup_lock); ++} ++ ++/** ++ * mpt2sas_base_put_smid_fast_path - send fast path request to firmware ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @handle: device handle ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ u16 handle) ++{ ++ Mpi2RequestDescriptorUnion_t descriptor; ++ u64 *request = (u64 *)&descriptor; ++ ++ descriptor.SCSIIO.RequestFlags = ++ MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO; ++ descriptor.SCSIIO.MSIxIndex = _base_get_msix_index(ioc); ++ descriptor.SCSIIO.SMID = cpu_to_le16(smid); ++ descriptor.SCSIIO.DevHandle = cpu_to_le16(handle); ++ descriptor.SCSIIO.LMID = 0; ++ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, ++ &ioc->scsi_lookup_lock); ++} ++ ++/** ++ * mpt2sas_base_put_smid_hi_priority - send Task Managment request to firmware ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_task: msix_task will be same as msix of IO incase of task abort else 0. ++ * Return nothing. ++ */ ++void ++mpt2sas_base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ u16 msix_task) ++{ ++ Mpi2RequestDescriptorUnion_t descriptor; ++ u64 *request = (u64 *)&descriptor; ++ ++ descriptor.HighPriority.RequestFlags = ++ MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY; ++ descriptor.HighPriority.MSIxIndex = msix_task; ++ descriptor.HighPriority.SMID = cpu_to_le16(smid); ++ descriptor.HighPriority.LMID = 0; ++ descriptor.HighPriority.Reserved1 = 0; ++ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, ++ &ioc->scsi_lookup_lock); ++} ++ ++/** ++ * mpt2sas_base_put_smid_default - Default, primarily used for config pages ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ Mpi2RequestDescriptorUnion_t descriptor; ++ u64 *request = (u64 *)&descriptor; ++ ++ descriptor.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; ++ descriptor.Default.MSIxIndex = _base_get_msix_index(ioc); ++ descriptor.Default.SMID = cpu_to_le16(smid); ++ descriptor.Default.LMID = 0; ++ descriptor.Default.DescriptorTypeDependent = 0; ++ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, ++ &ioc->scsi_lookup_lock); ++} ++ ++/** ++ * _base_display_OEMs_branding - Display branding string ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++static void ++_base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc) ++{ ++ if (ioc->pdev->subsystem_vendor != PCI_VENDOR_ID_INTEL) ++ return; ++ ++ switch (ioc->pdev->subsystem_vendor) { ++ case PCI_VENDOR_ID_INTEL: ++ switch (ioc->pdev->device) { ++ case MPI2_MFGPAGE_DEVID_SAS2008: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT2SAS_INTEL_RMS2LL080_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS2LL080_BRANDING); ++ break; ++ case MPT2SAS_INTEL_RMS2LL040_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS2LL040_BRANDING); ++ break; ++ case MPT2SAS_INTEL_SSD910_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_SSD910_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Intel(R) Controller: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ case MPI2_MFGPAGE_DEVID_SAS2308_2: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT2SAS_INTEL_RS25GB008_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RS25GB008_BRANDING); ++ break; ++ case MPT2SAS_INTEL_RMS25JB080_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS25JB080_BRANDING); ++ break; ++ case MPT2SAS_INTEL_RMS25JB040_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS25JB040_BRANDING); ++ break; ++ case MPT2SAS_INTEL_RMS25KB080_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS25KB080_BRANDING); ++ break; ++ case MPT2SAS_INTEL_RMS25KB040_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS25KB040_BRANDING); ++ break; ++ case MPT2SAS_INTEL_RMS25LB040_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS25LB040_BRANDING); ++ break; ++ case MPT2SAS_INTEL_RMS25LB080_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS25LB080_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Intel(R) Controller: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ case MPI25_MFGPAGE_DEVID_SAS3008: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT3SAS_INTEL_RMS3JC080_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_INTEL_RMS3JC080_BRANDING); ++ break; ++ ++ case MPT3SAS_INTEL_RS3GC008_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_INTEL_RS3GC008_BRANDING); ++ break; ++ case MPT3SAS_INTEL_RS3FC044_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_INTEL_RS3FC044_BRANDING); ++ break; ++ case MPT3SAS_INTEL_RS3UC080_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_INTEL_RS3UC080_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Intel(R) Controller: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Intel(R) Controller: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ case PCI_VENDOR_ID_DELL: ++ switch (ioc->pdev->device) { ++ case MPI2_MFGPAGE_DEVID_SAS2008: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT2SAS_DELL_6GBPS_SAS_HBA_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_DELL_6GBPS_SAS_HBA_BRANDING); ++ break; ++ case MPT2SAS_DELL_PERC_H200_ADAPTER_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_DELL_PERC_H200_ADAPTER_BRANDING); ++ break; ++ case MPT2SAS_DELL_PERC_H200_INTEGRATED_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_DELL_PERC_H200_INTEGRATED_BRANDING); ++ break; ++ case MPT2SAS_DELL_PERC_H200_MODULAR_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_DELL_PERC_H200_MODULAR_BRANDING); ++ break; ++ case MPT2SAS_DELL_PERC_H200_EMBEDDED_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_DELL_PERC_H200_EMBEDDED_BRANDING); ++ break; ++ case MPT2SAS_DELL_PERC_H200_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_DELL_PERC_H200_BRANDING); ++ break; ++ case MPT2SAS_DELL_6GBPS_SAS_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_DELL_6GBPS_SAS_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Dell 6Gbps HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ case MPI25_MFGPAGE_DEVID_SAS3008: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT3SAS_DELL_12G_HBA_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_DELL_12G_HBA_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Dell 12Gbps HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Dell HBA: Subsystem ID: 0x%X\n", ioc->name, ++ ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ case PCI_VENDOR_ID_CISCO: ++ switch (ioc->pdev->device) { ++ case MPI25_MFGPAGE_DEVID_SAS3008: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT3SAS_CISCO_12G_8E_HBA_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_CISCO_12G_8E_HBA_BRANDING); ++ break; ++ case MPT3SAS_CISCO_12G_8I_HBA_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_CISCO_12G_8I_HBA_BRANDING); ++ break; ++ case MPT3SAS_CISCO_12G_AVILA_HBA_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Cisco 12Gbps SAS HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ case MPI25_MFGPAGE_DEVID_SAS3108_1: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT3SAS_CISCO_12G_AVILA_HBA_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING); ++ break; ++ case MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_BRANDING ++ ); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Cisco 12Gbps SAS HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Cisco SAS HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ case MPT2SAS_HP_3PAR_SSVID: ++ switch (ioc->pdev->device) { ++ case MPI2_MFGPAGE_DEVID_SAS2004: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "HP 6Gbps SAS HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ case MPI2_MFGPAGE_DEVID_SAS2308_2: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT2SAS_HP_2_4_INTERNAL_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_HP_2_4_INTERNAL_BRANDING); ++ break; ++ case MPT2SAS_HP_2_4_EXTERNAL_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_HP_2_4_EXTERNAL_BRANDING); ++ break; ++ case MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING); ++ break; ++ case MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "HP 6Gbps SAS HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ default: ++ pr_info(MPT3SAS_FMT ++ "HP SAS HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ default: ++ break; ++ } ++} ++ ++/** ++ * _base_display_ioc_capabilities - Disply IOC's capabilities. ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++static void ++_base_display_ioc_capabilities(struct MPT3SAS_ADAPTER *ioc) ++{ ++ int i = 0; ++ char desc[16]; ++ u32 iounit_pg1_flags; ++ u32 bios_version; ++ ++ bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion); ++ strncpy(desc, ioc->manu_pg0.ChipName, 16); ++ pr_info(MPT3SAS_FMT "%s: FWVersion(%02d.%02d.%02d.%02d), "\ ++ "ChipRevision(0x%02x), BiosVersion(%02d.%02d.%02d.%02d)\n", ++ ioc->name, desc, ++ (ioc->facts.FWVersion.Word & 0xFF000000) >> 24, ++ (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16, ++ (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8, ++ ioc->facts.FWVersion.Word & 0x000000FF, ++ ioc->pdev->revision, ++ (bios_version & 0xFF000000) >> 24, ++ (bios_version & 0x00FF0000) >> 16, ++ (bios_version & 0x0000FF00) >> 8, ++ bios_version & 0x000000FF); ++ ++ _base_display_OEMs_branding(ioc); ++ ++ pr_info(MPT3SAS_FMT "Protocol=(", ioc->name); ++ ++ if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR) { ++ pr_info("Initiator"); ++ i++; ++ } ++ ++ if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET) { ++ pr_info("%sTarget", i ? "," : ""); ++ i++; ++ } ++ ++ i = 0; ++ pr_info("), "); ++ pr_info("Capabilities=("); ++ ++ if (!ioc->hide_ir_msg) { ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) { ++ pr_info("Raid"); ++ i++; ++ } ++ } ++ ++ if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR) { ++ pr_info("%sTLR", i ? "," : ""); ++ i++; ++ } ++ ++ if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_MULTICAST) { ++ pr_info("%sMulticast", i ? "," : ""); ++ i++; ++ } ++ ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET) { ++ pr_info("%sBIDI Target", i ? "," : ""); ++ i++; ++ } ++ ++ if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_EEDP) { ++ pr_info("%sEEDP", i ? "," : ""); ++ i++; ++ } ++ ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) { ++ pr_info("%sSnapshot Buffer", i ? "," : ""); ++ i++; ++ } ++ ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) { ++ pr_info("%sDiag Trace Buffer", i ? "," : ""); ++ i++; ++ } ++ ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) { ++ pr_info("%sDiag Extended Buffer", i ? "," : ""); ++ i++; ++ } ++ ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING) { ++ pr_info("%sTask Set Full", i ? "," : ""); ++ i++; ++ } ++ ++ iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags); ++ if (!(iounit_pg1_flags & MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE)) { ++ pr_info("%sNCQ", i ? "," : ""); ++ i++; ++ } ++ ++ pr_info(")\n"); ++} ++ ++/** ++ * mpt2sas_base_update_missing_delay - change the missing delay timers ++ * @ioc: per adapter object ++ * @device_missing_delay: amount of time till device is reported missing ++ * @io_missing_delay: interval IO is returned when there is a missing device ++ * ++ * Return nothing. ++ * ++ * Passed on the command line, this function will modify the device missing ++ * delay, as well as the io missing delay. This should be called at driver ++ * load time. ++ */ ++void ++mpt2sas_base_update_missing_delay(struct MPT3SAS_ADAPTER *ioc, ++ u16 device_missing_delay, u8 io_missing_delay) ++{ ++ u16 dmd, dmd_new, dmd_orignal; ++ u8 io_missing_delay_original; ++ u16 sz; ++ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; ++ Mpi2ConfigReply_t mpi_reply; ++ u8 num_phys = 0; ++ u16 ioc_status; ++ ++ mpt2sas_config_get_number_hba_phys(ioc, &num_phys); ++ if (!num_phys) ++ return; ++ ++ sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (num_phys * ++ sizeof(Mpi2SasIOUnit1PhyData_t)); ++ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); ++ if (!sas_iounit_pg1) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, ++ sas_iounit_pg1, sz))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ++ /* device missing delay */ ++ dmd = sas_iounit_pg1->ReportDeviceMissingDelay; ++ if (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16) ++ dmd = (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16; ++ else ++ dmd = dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK; ++ dmd_orignal = dmd; ++ if (device_missing_delay > 0x7F) { ++ dmd = (device_missing_delay > 0x7F0) ? 0x7F0 : ++ device_missing_delay; ++ dmd = dmd / 16; ++ dmd |= MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16; ++ } else ++ dmd = device_missing_delay; ++ sas_iounit_pg1->ReportDeviceMissingDelay = dmd; ++ ++ /* io missing delay */ ++ io_missing_delay_original = sas_iounit_pg1->IODeviceMissingDelay; ++ sas_iounit_pg1->IODeviceMissingDelay = io_missing_delay; ++ ++ if (!mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, ++ sz)) { ++ if (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16) ++ dmd_new = (dmd & ++ MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16; ++ else ++ dmd_new = ++ dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK; ++ pr_info(MPT3SAS_FMT "device_missing_delay: old(%d), new(%d)\n", ++ ioc->name, dmd_orignal, dmd_new); ++ pr_info(MPT3SAS_FMT "ioc_missing_delay: old(%d), new(%d)\n", ++ ioc->name, io_missing_delay_original, ++ io_missing_delay); ++ ioc->device_missing_delay = dmd_new; ++ ioc->io_missing_delay = io_missing_delay; ++ } ++ ++out: ++ kfree(sas_iounit_pg1); ++} ++/** ++ * _base_static_config_pages - static start of day config pages ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++static void ++_base_static_config_pages(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2ConfigReply_t mpi_reply; ++ u32 iounit_pg1_flags; ++ ++ mpt2sas_config_get_manufacturing_pg0(ioc, &mpi_reply, &ioc->manu_pg0); ++ if (ioc->ir_firmware) ++ mpt2sas_config_get_manufacturing_pg10(ioc, &mpi_reply, ++ &ioc->manu_pg10); ++ ++ /* ++ * Ensure correct T10 PI operation if vendor left EEDPTagMode ++ * flag unset in NVDATA. ++ */ ++ mpt2sas_config_get_manufacturing_pg11(ioc, &mpi_reply, &ioc->manu_pg11); ++ if (ioc->manu_pg11.EEDPTagMode == 0) { ++ pr_err("%s: overriding NVDATA EEDPTagMode setting\n", ++ ioc->name); ++ ioc->manu_pg11.EEDPTagMode &= ~0x3; ++ ioc->manu_pg11.EEDPTagMode |= 0x1; ++ mpt2sas_config_set_manufacturing_pg11(ioc, &mpi_reply, ++ &ioc->manu_pg11); ++ } ++ ++ mpt2sas_config_get_bios_pg2(ioc, &mpi_reply, &ioc->bios_pg2); ++ mpt2sas_config_get_bios_pg3(ioc, &mpi_reply, &ioc->bios_pg3); ++ mpt2sas_config_get_ioc_pg8(ioc, &mpi_reply, &ioc->ioc_pg8); ++ mpt2sas_config_get_iounit_pg0(ioc, &mpi_reply, &ioc->iounit_pg0); ++ mpt2sas_config_get_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1); ++ mpt2sas_config_get_iounit_pg8(ioc, &mpi_reply, &ioc->iounit_pg8); ++ _base_display_ioc_capabilities(ioc); ++ ++ /* ++ * Enable task_set_full handling in iounit_pg1 when the ++ * facts capabilities indicate that its supported. ++ */ ++ iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags); ++ if ((ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING)) ++ iounit_pg1_flags &= ++ ~MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING; ++ else ++ iounit_pg1_flags |= ++ MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING; ++ ioc->iounit_pg1.Flags = cpu_to_le32(iounit_pg1_flags); ++ mpt2sas_config_set_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1); ++ ++ if (ioc->iounit_pg8.NumSensors) ++ ioc->temp_sensors_count = ioc->iounit_pg8.NumSensors; ++} ++ ++/** ++ * _base_release_memory_pools - release memory ++ * @ioc: per adapter object ++ * ++ * Free memory allocated from _base_allocate_memory_pools. ++ * ++ * Return nothing. ++ */ ++static void ++_base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc) ++{ ++ int i = 0; ++ struct reply_post_struct *rps; ++ ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ if (ioc->request) { ++ pci_free_consistent(ioc->pdev, ioc->request_dma_sz, ++ ioc->request, ioc->request_dma); ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT ++ "request_pool(0x%p): free\n", ++ ioc->name, ioc->request)); ++ ioc->request = NULL; ++ } ++ ++ if (ioc->sense) { ++ pci_pool_free(ioc->sense_dma_pool, ioc->sense, ioc->sense_dma); ++ if (ioc->sense_dma_pool) ++ pci_pool_destroy(ioc->sense_dma_pool); ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT ++ "sense_pool(0x%p): free\n", ++ ioc->name, ioc->sense)); ++ ioc->sense = NULL; ++ } ++ ++ if (ioc->reply) { ++ pci_pool_free(ioc->reply_dma_pool, ioc->reply, ioc->reply_dma); ++ if (ioc->reply_dma_pool) ++ pci_pool_destroy(ioc->reply_dma_pool); ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT ++ "reply_pool(0x%p): free\n", ++ ioc->name, ioc->reply)); ++ ioc->reply = NULL; ++ } ++ ++ if (ioc->reply_free) { ++ pci_pool_free(ioc->reply_free_dma_pool, ioc->reply_free, ++ ioc->reply_free_dma); ++ if (ioc->reply_free_dma_pool) ++ pci_pool_destroy(ioc->reply_free_dma_pool); ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT ++ "reply_free_pool(0x%p): free\n", ++ ioc->name, ioc->reply_free)); ++ ioc->reply_free = NULL; ++ } ++ ++ if (ioc->reply_post) { ++ do { ++ rps = &ioc->reply_post[i]; ++ if (rps->reply_post_free) { ++ pci_pool_free( ++ ioc->reply_post_free_dma_pool, ++ rps->reply_post_free, ++ rps->reply_post_free_dma); ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT ++ "reply_post_free_pool(0x%p): free\n", ++ ioc->name, rps->reply_post_free)); ++ rps->reply_post_free = NULL; ++ } ++ } while (ioc->rdpq_array_enable && ++ (++i < ioc->reply_queue_count)); ++ ++ if (ioc->reply_post_free_dma_pool) ++ pci_pool_destroy(ioc->reply_post_free_dma_pool); ++ kfree(ioc->reply_post); ++ } ++ ++ if (ioc->config_page) { ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT ++ "config_page(0x%p): free\n", ioc->name, ++ ioc->config_page)); ++ pci_free_consistent(ioc->pdev, ioc->config_page_sz, ++ ioc->config_page, ioc->config_page_dma); ++ } ++ ++ if (ioc->scsi_lookup) { ++ free_pages((ulong)ioc->scsi_lookup, ioc->scsi_lookup_pages); ++ ioc->scsi_lookup = NULL; ++ } ++ kfree(ioc->hpr_lookup); ++ kfree(ioc->internal_lookup); ++ if (ioc->chain_lookup) { ++ for (i = 0; i < ioc->chain_depth; i++) { ++ if (ioc->chain_lookup[i].chain_buffer) ++ pci_pool_free(ioc->chain_dma_pool, ++ ioc->chain_lookup[i].chain_buffer, ++ ioc->chain_lookup[i].chain_buffer_dma); ++ } ++ if (ioc->chain_dma_pool) ++ pci_pool_destroy(ioc->chain_dma_pool); ++ free_pages((ulong)ioc->chain_lookup, ioc->chain_pages); ++ ioc->chain_lookup = NULL; ++ } ++} ++ ++/** ++ * _base_allocate_memory_pools - allocate start of day memory pools ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 success, anything else error ++ */ ++static int ++_base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ struct mpt2sas_facts *facts; ++ u16 max_sge_elements; ++ u16 chains_needed_per_io; ++ u32 sz, total_sz, reply_post_free_sz; ++ u32 retry_sz; ++ u16 max_request_credit; ++ unsigned short sg_tablesize; ++ u16 sge_size; ++ int i; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ ++ retry_sz = 0; ++ facts = &ioc->facts; ++ ++ /* command line tunables for max sgl entries */ ++ if (max_sgl_entries != -1) ++ sg_tablesize = max_sgl_entries; ++ else { ++ if (ioc->hba_mpi_version_belonged == MPI2_VERSION) ++ sg_tablesize = MPT2SAS_SG_DEPTH; ++ else ++ sg_tablesize = MPT3SAS_SG_DEPTH; ++ } ++ ++ if (sg_tablesize < MPT_MIN_PHYS_SEGMENTS) ++ sg_tablesize = MPT_MIN_PHYS_SEGMENTS; ++ else if (sg_tablesize > MPT_MAX_PHYS_SEGMENTS) { ++ sg_tablesize = min_t(unsigned short, sg_tablesize, ++ SCSI_MAX_SG_CHAIN_SEGMENTS); ++ pr_warn(MPT3SAS_FMT ++ "sg_tablesize(%u) is bigger than kernel" ++ " defined SCSI_MAX_SG_SEGMENTS(%u)\n", ioc->name, ++ sg_tablesize, MPT_MAX_PHYS_SEGMENTS); ++ } ++ ioc->shost->sg_tablesize = sg_tablesize; ++ ++ ioc->internal_depth = min_t(int, (facts->HighPriorityCredit + (5)), ++ (facts->RequestCredit / 4)); ++ if (ioc->internal_depth < INTERNAL_CMDS_COUNT) { ++ if (facts->RequestCredit <= (INTERNAL_CMDS_COUNT + ++ INTERNAL_SCSIIO_CMDS_COUNT)) { ++ pr_err(MPT3SAS_FMT "IOC doesn't have enough Request \ ++ Credits, it has just %d number of credits\n", ++ ioc->name, facts->RequestCredit); ++ return -ENOMEM; ++ } ++ ioc->internal_depth = 10; ++ } ++ ++ ioc->hi_priority_depth = ioc->internal_depth - (5); ++ /* command line tunables for max controller queue depth */ ++ if (max_queue_depth != -1 && max_queue_depth != 0) { ++ max_request_credit = min_t(u16, max_queue_depth + ++ ioc->internal_depth, facts->RequestCredit); ++ if (max_request_credit > MAX_HBA_QUEUE_DEPTH) ++ max_request_credit = MAX_HBA_QUEUE_DEPTH; ++ } else ++ max_request_credit = min_t(u16, facts->RequestCredit, ++ MAX_HBA_QUEUE_DEPTH); ++ ++ /* Firmware maintains additional facts->HighPriorityCredit number of ++ * credits for HiPriprity Request messages, so hba queue depth will be ++ * sum of max_request_credit and high priority queue depth. ++ */ ++ ioc->hba_queue_depth = max_request_credit + ioc->hi_priority_depth; ++ ++ /* request frame size */ ++ ioc->request_sz = facts->IOCRequestFrameSize * 4; ++ ++ /* reply frame size */ ++ ioc->reply_sz = facts->ReplyFrameSize * 4; ++ ++ /* chain segment size */ ++ if (ioc->hba_mpi_version_belonged != MPI2_VERSION) { ++ if (facts->IOCMaxChainSegmentSize) ++ ioc->chain_segment_sz = ++ facts->IOCMaxChainSegmentSize * ++ MAX_CHAIN_ELEMT_SZ; ++ else ++ /* set to 128 bytes size if IOCMaxChainSegmentSize is zero */ ++ ioc->chain_segment_sz = DEFAULT_NUM_FWCHAIN_ELEMTS * ++ MAX_CHAIN_ELEMT_SZ; ++ } else ++ ioc->chain_segment_sz = ioc->request_sz; ++ ++ /* calculate the max scatter element size */ ++ sge_size = max_t(u16, ioc->sge_size, ioc->sge_size_ieee); ++ ++ retry_allocation: ++ total_sz = 0; ++ /* calculate number of sg elements left over in the 1st frame */ ++ max_sge_elements = ioc->request_sz - ((sizeof(Mpi2SCSIIORequest_t) - ++ sizeof(Mpi2SGEIOUnion_t)) + sge_size); ++ ioc->max_sges_in_main_message = max_sge_elements/sge_size; ++ ++ /* now do the same for a chain buffer */ ++ max_sge_elements = ioc->chain_segment_sz - sge_size; ++ ioc->max_sges_in_chain_message = max_sge_elements/sge_size; ++ ++ /* ++ * MPT3SAS_SG_DEPTH = CONFIG_FUSION_MAX_SGE ++ */ ++ chains_needed_per_io = ((ioc->shost->sg_tablesize - ++ ioc->max_sges_in_main_message)/ioc->max_sges_in_chain_message) ++ + 1; ++ if (chains_needed_per_io > facts->MaxChainDepth) { ++ chains_needed_per_io = facts->MaxChainDepth; ++ ioc->shost->sg_tablesize = min_t(u16, ++ ioc->max_sges_in_main_message + (ioc->max_sges_in_chain_message ++ * chains_needed_per_io), ioc->shost->sg_tablesize); ++ } ++ ioc->chains_needed_per_io = chains_needed_per_io; ++ ++ /* reply free queue sizing - taking into account for 64 FW events */ ++ ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64; ++ ++ /* calculate reply descriptor post queue depth */ ++ ioc->reply_post_queue_depth = ioc->hba_queue_depth + ++ ioc->reply_free_queue_depth + 1 ; ++ /* align the reply post queue on the next 16 count boundary */ ++ if (ioc->reply_post_queue_depth % 16) ++ ioc->reply_post_queue_depth += 16 - ++ (ioc->reply_post_queue_depth % 16); ++ ++ if (ioc->reply_post_queue_depth > ++ facts->MaxReplyDescriptorPostQueueDepth) { ++ ioc->reply_post_queue_depth = ++ facts->MaxReplyDescriptorPostQueueDepth - ++ (facts->MaxReplyDescriptorPostQueueDepth % 16); ++ ioc->hba_queue_depth = ++ ((ioc->reply_post_queue_depth - 64) / 2) - 1; ++ ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64; ++ } ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "scatter gather: " \ ++ "sge_in_main_msg(%d), sge_per_chain(%d), sge_per_io(%d), " ++ "chains_per_io(%d)\n", ioc->name, ioc->max_sges_in_main_message, ++ ioc->max_sges_in_chain_message, ioc->shost->sg_tablesize, ++ ioc->chains_needed_per_io)); ++ ++ /* reply post queue, 16 byte align */ ++ reply_post_free_sz = ioc->reply_post_queue_depth * ++ sizeof(Mpi2DefaultReplyDescriptor_t); ++ ++ sz = reply_post_free_sz; ++ if (_base_is_controller_msix_enabled(ioc) && !ioc->rdpq_array_enable) ++ sz *= ioc->reply_queue_count; ++ ++ ioc->reply_post = kcalloc((ioc->rdpq_array_enable) ? ++ (ioc->reply_queue_count):1, ++ sizeof(struct reply_post_struct), GFP_KERNEL); ++ ++ if (!ioc->reply_post) { ++ pr_err(MPT3SAS_FMT "reply_post_free pool: kcalloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->reply_post_free_dma_pool = pci_pool_create("reply_post_free pool", ++ ioc->pdev, sz, 16, 0); ++ if (!ioc->reply_post_free_dma_pool) { ++ pr_err(MPT3SAS_FMT ++ "reply_post_free pool: pci_pool_create failed\n", ++ ioc->name); ++ goto out; ++ } ++ i = 0; ++ do { ++ ioc->reply_post[i].reply_post_free = ++ pci_pool_alloc(ioc->reply_post_free_dma_pool, ++ GFP_KERNEL, ++ &ioc->reply_post[i].reply_post_free_dma); ++ if (!ioc->reply_post[i].reply_post_free) { ++ pr_err(MPT3SAS_FMT ++ "reply_post_free pool: pci_pool_alloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ memset(ioc->reply_post[i].reply_post_free, 0, sz); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "reply post free pool (0x%p): depth(%d)," ++ "element_size(%d), pool_size(%d kB)\n", ioc->name, ++ ioc->reply_post[i].reply_post_free, ++ ioc->reply_post_queue_depth, 8, sz/1024)); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "reply_post_free_dma = (0x%llx)\n", ioc->name, ++ (unsigned long long) ++ ioc->reply_post[i].reply_post_free_dma)); ++ total_sz += sz; ++ } while (ioc->rdpq_array_enable && (++i < ioc->reply_queue_count)); ++ ++ if (ioc->dma_mask == 64) { ++ if (_base_change_consistent_dma_mask(ioc, ioc->pdev) != 0) { ++ pr_warn(MPT3SAS_FMT ++ "no suitable consistent DMA mask for %s\n", ++ ioc->name, pci_name(ioc->pdev)); ++ goto out; ++ } ++ } ++ ++ ioc->scsiio_depth = ioc->hba_queue_depth - ++ ioc->hi_priority_depth - ioc->internal_depth; ++ ++ /* set the scsi host can_queue depth ++ * with some internal commands that could be outstanding ++ */ ++ ioc->shost->can_queue = ioc->scsiio_depth - INTERNAL_SCSIIO_CMDS_COUNT; ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "scsi host: can_queue depth (%d)\n", ++ ioc->name, ioc->shost->can_queue)); ++ ++ ++ /* contiguous pool for request and chains, 16 byte align, one extra " ++ * "frame for smid=0 ++ */ ++ ioc->chain_depth = ioc->chains_needed_per_io * ioc->scsiio_depth; ++ sz = ((ioc->scsiio_depth + 1) * ioc->request_sz); ++ ++ /* hi-priority queue */ ++ sz += (ioc->hi_priority_depth * ioc->request_sz); ++ ++ /* internal queue */ ++ sz += (ioc->internal_depth * ioc->request_sz); ++ ++ ioc->request_dma_sz = sz; ++ ioc->request = pci_alloc_consistent(ioc->pdev, sz, &ioc->request_dma); ++ if (!ioc->request) { ++ pr_err(MPT3SAS_FMT "request pool: pci_alloc_consistent " \ ++ "failed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), " ++ "total(%d kB)\n", ioc->name, ioc->hba_queue_depth, ++ ioc->chains_needed_per_io, ioc->request_sz, sz/1024); ++ if (ioc->scsiio_depth < MPT3SAS_SAS_QUEUE_DEPTH) ++ goto out; ++ retry_sz = 64; ++ ioc->hba_queue_depth -= retry_sz; ++ _base_release_memory_pools(ioc); ++ goto retry_allocation; ++ } ++ ++ if (retry_sz) ++ pr_err(MPT3SAS_FMT "request pool: pci_alloc_consistent " \ ++ "succeed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), " ++ "total(%d kb)\n", ioc->name, ioc->hba_queue_depth, ++ ioc->chains_needed_per_io, ioc->request_sz, sz/1024); ++ ++ /* hi-priority queue */ ++ ioc->hi_priority = ioc->request + ((ioc->scsiio_depth + 1) * ++ ioc->request_sz); ++ ioc->hi_priority_dma = ioc->request_dma + ((ioc->scsiio_depth + 1) * ++ ioc->request_sz); ++ ++ /* internal queue */ ++ ioc->internal = ioc->hi_priority + (ioc->hi_priority_depth * ++ ioc->request_sz); ++ ioc->internal_dma = ioc->hi_priority_dma + (ioc->hi_priority_depth * ++ ioc->request_sz); ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "request pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB)\n", ++ ioc->name, ioc->request, ioc->hba_queue_depth, ioc->request_sz, ++ (ioc->hba_queue_depth * ioc->request_sz)/1024)); ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "request pool: dma(0x%llx)\n", ++ ioc->name, (unsigned long long) ioc->request_dma)); ++ total_sz += sz; ++ ++ sz = ioc->scsiio_depth * sizeof(struct scsiio_tracker); ++ ioc->scsi_lookup_pages = get_order(sz); ++ ioc->scsi_lookup = (struct scsiio_tracker *)__get_free_pages( ++ GFP_KERNEL, ioc->scsi_lookup_pages); ++ if (!ioc->scsi_lookup) { ++ pr_err(MPT3SAS_FMT "scsi_lookup: get_free_pages failed, sz(%d)\n", ++ ioc->name, (int)sz); ++ goto out; ++ } ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "scsiio(0x%p): depth(%d)\n", ++ ioc->name, ioc->request, ioc->scsiio_depth)); ++ ++ ioc->chain_depth = min_t(u32, ioc->chain_depth, MAX_CHAIN_DEPTH); ++ sz = ioc->chain_depth * sizeof(struct chain_tracker); ++ ioc->chain_pages = get_order(sz); ++ ioc->chain_lookup = (struct chain_tracker *)__get_free_pages( ++ GFP_KERNEL, ioc->chain_pages); ++ if (!ioc->chain_lookup) { ++ pr_err(MPT3SAS_FMT "chain_lookup: __get_free_pages failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->chain_dma_pool = pci_pool_create("chain pool", ioc->pdev, ++ ioc->chain_segment_sz, 16, 0); ++ if (!ioc->chain_dma_pool) { ++ pr_err(MPT3SAS_FMT "chain_dma_pool: pci_pool_create failed\n", ++ ioc->name); ++ goto out; ++ } ++ for (i = 0; i < ioc->chain_depth; i++) { ++ ioc->chain_lookup[i].chain_buffer = pci_pool_alloc( ++ ioc->chain_dma_pool , GFP_KERNEL, ++ &ioc->chain_lookup[i].chain_buffer_dma); ++ if (!ioc->chain_lookup[i].chain_buffer) { ++ ioc->chain_depth = i; ++ goto chain_done; ++ } ++ total_sz += ioc->chain_segment_sz; ++ } ++ chain_done: ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "chain pool depth(%d), frame_size(%d), pool_size(%d kB)\n", ++ ioc->name, ioc->chain_depth, ioc->chain_segment_sz, ++ ((ioc->chain_depth * ioc->chain_segment_sz))/1024)); ++ ++ /* initialize hi-priority queue smid's */ ++ ioc->hpr_lookup = kcalloc(ioc->hi_priority_depth, ++ sizeof(struct request_tracker), GFP_KERNEL); ++ if (!ioc->hpr_lookup) { ++ pr_err(MPT3SAS_FMT "hpr_lookup: kcalloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->hi_priority_smid = ioc->scsiio_depth + 1; ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "hi_priority(0x%p): depth(%d), start smid(%d)\n", ++ ioc->name, ioc->hi_priority, ++ ioc->hi_priority_depth, ioc->hi_priority_smid)); ++ ++ /* initialize internal queue smid's */ ++ ioc->internal_lookup = kcalloc(ioc->internal_depth, ++ sizeof(struct request_tracker), GFP_KERNEL); ++ if (!ioc->internal_lookup) { ++ pr_err(MPT3SAS_FMT "internal_lookup: kcalloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->internal_smid = ioc->hi_priority_smid + ioc->hi_priority_depth; ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "internal(0x%p): depth(%d), start smid(%d)\n", ++ ioc->name, ioc->internal, ++ ioc->internal_depth, ioc->internal_smid)); ++ ++ /* sense buffers, 4 byte align */ ++ sz = ioc->scsiio_depth * SCSI_SENSE_BUFFERSIZE; ++ ioc->sense_dma_pool = pci_pool_create("sense pool", ioc->pdev, sz, 4, ++ 0); ++ if (!ioc->sense_dma_pool) { ++ pr_err(MPT3SAS_FMT "sense pool: pci_pool_create failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->sense = pci_pool_alloc(ioc->sense_dma_pool , GFP_KERNEL, ++ &ioc->sense_dma); ++ if (!ioc->sense) { ++ pr_err(MPT3SAS_FMT "sense pool: pci_pool_alloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "sense pool(0x%p): depth(%d), element_size(%d), pool_size" ++ "(%d kB)\n", ioc->name, ioc->sense, ioc->scsiio_depth, ++ SCSI_SENSE_BUFFERSIZE, sz/1024)); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "sense_dma(0x%llx)\n", ++ ioc->name, (unsigned long long)ioc->sense_dma)); ++ total_sz += sz; ++ ++ /* reply pool, 4 byte align */ ++ sz = ioc->reply_free_queue_depth * ioc->reply_sz; ++ ioc->reply_dma_pool = pci_pool_create("reply pool", ioc->pdev, sz, 4, ++ 0); ++ if (!ioc->reply_dma_pool) { ++ pr_err(MPT3SAS_FMT "reply pool: pci_pool_create failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->reply = pci_pool_alloc(ioc->reply_dma_pool , GFP_KERNEL, ++ &ioc->reply_dma); ++ if (!ioc->reply) { ++ pr_err(MPT3SAS_FMT "reply pool: pci_pool_alloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->reply_dma_min_address = (u32)(ioc->reply_dma); ++ ioc->reply_dma_max_address = (u32)(ioc->reply_dma) + sz; ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "reply pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB)\n", ++ ioc->name, ioc->reply, ++ ioc->reply_free_queue_depth, ioc->reply_sz, sz/1024)); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "reply_dma(0x%llx)\n", ++ ioc->name, (unsigned long long)ioc->reply_dma)); ++ total_sz += sz; ++ ++ /* reply free queue, 16 byte align */ ++ sz = ioc->reply_free_queue_depth * 4; ++ ioc->reply_free_dma_pool = pci_pool_create("reply_free pool", ++ ioc->pdev, sz, 16, 0); ++ if (!ioc->reply_free_dma_pool) { ++ pr_err(MPT3SAS_FMT "reply_free pool: pci_pool_create failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->reply_free = pci_pool_alloc(ioc->reply_free_dma_pool , GFP_KERNEL, ++ &ioc->reply_free_dma); ++ if (!ioc->reply_free) { ++ pr_err(MPT3SAS_FMT "reply_free pool: pci_pool_alloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ memset(ioc->reply_free, 0, sz); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "reply_free pool(0x%p): " \ ++ "depth(%d), element_size(%d), pool_size(%d kB)\n", ioc->name, ++ ioc->reply_free, ioc->reply_free_queue_depth, 4, sz/1024)); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "reply_free_dma (0x%llx)\n", ++ ioc->name, (unsigned long long)ioc->reply_free_dma)); ++ total_sz += sz; ++ ++ ioc->config_page_sz = 512; ++ ioc->config_page = pci_alloc_consistent(ioc->pdev, ++ ioc->config_page_sz, &ioc->config_page_dma); ++ if (!ioc->config_page) { ++ pr_err(MPT3SAS_FMT ++ "config page: pci_pool_alloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "config page(0x%p): size(%d)\n", ++ ioc->name, ioc->config_page, ioc->config_page_sz)); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "config_page_dma(0x%llx)\n", ++ ioc->name, (unsigned long long)ioc->config_page_dma)); ++ total_sz += ioc->config_page_sz; ++ ++ pr_info(MPT3SAS_FMT "Allocated physical memory: size(%d kB)\n", ++ ioc->name, total_sz/1024); ++ pr_info(MPT3SAS_FMT ++ "Current Controller Queue Depth(%d),Max Controller Queue Depth(%d)\n", ++ ioc->name, ioc->shost->can_queue, facts->RequestCredit); ++ pr_info(MPT3SAS_FMT "Scatter Gather Elements per IO(%d)\n", ++ ioc->name, ioc->shost->sg_tablesize); ++ return 0; ++ ++ out: ++ return -ENOMEM; ++} ++ ++/** ++ * mpt2sas_base_get_iocstate - Get the current state of a MPT adapter. ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @cooked: Request raw or cooked IOC state ++ * ++ * Returns all IOC Doorbell register bits if cooked==0, else just the ++ * Doorbell bits in MPI_IOC_STATE_MASK. ++ */ ++u32 ++mpt2sas_base_get_iocstate(struct MPT3SAS_ADAPTER *ioc, int cooked) ++{ ++ u32 s, sc; ++ ++ s = readl(&ioc->chip->Doorbell); ++ sc = s & MPI2_IOC_STATE_MASK; ++ return cooked ? sc : s; ++} ++ ++/** ++ * _base_wait_on_iocstate - waiting on a particular ioc state ++ * @ioc_state: controller state { READY, OPERATIONAL, or RESET } ++ * @timeout: timeout in second ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout, ++ int sleep_flag) ++{ ++ u32 count, cntdn; ++ u32 current_state; ++ ++ count = 0; ++ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; ++ do { ++ current_state = mpt2sas_base_get_iocstate(ioc, 1); ++ if (current_state == ioc_state) ++ return 0; ++ if (count && current_state == MPI2_IOC_STATE_FAULT) ++ break; ++ if (sleep_flag == CAN_SLEEP) ++ usleep_range(1000, 1500); ++ else ++ udelay(500); ++ count++; ++ } while (--cntdn); ++ ++ return current_state; ++} ++ ++/** ++ * _base_wait_for_doorbell_int - waiting for controller interrupt(generated by ++ * a write to the doorbell) ++ * @ioc: per adapter object ++ * @timeout: timeout in second ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ * ++ * Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell. ++ */ ++static int ++_base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag); ++ ++static int ++_base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout, ++ int sleep_flag) ++{ ++ u32 cntdn, count; ++ u32 int_status; ++ ++ count = 0; ++ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; ++ do { ++ int_status = readl(&ioc->chip->HostInterruptStatus); ++ if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { ++ dhsprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: successful count(%d), timeout(%d)\n", ++ ioc->name, __func__, count, timeout)); ++ return 0; ++ } ++ if (sleep_flag == CAN_SLEEP) ++ usleep_range(1000, 1500); ++ else ++ udelay(500); ++ count++; ++ } while (--cntdn); ++ ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to timeout count(%d), int_status(%x)!\n", ++ ioc->name, __func__, count, int_status); ++ return -EFAULT; ++} ++ ++/** ++ * _base_wait_for_doorbell_ack - waiting for controller to read the doorbell. ++ * @ioc: per adapter object ++ * @timeout: timeout in second ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ * ++ * Notes: MPI2_HIS_SYS2IOC_DB_STATUS - set to one when host writes to ++ * doorbell. ++ */ ++static int ++_base_wait_for_doorbell_ack(struct MPT3SAS_ADAPTER *ioc, int timeout, ++ int sleep_flag) ++{ ++ u32 cntdn, count; ++ u32 int_status; ++ u32 doorbell; ++ ++ count = 0; ++ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; ++ do { ++ int_status = readl(&ioc->chip->HostInterruptStatus); ++ if (!(int_status & MPI2_HIS_SYS2IOC_DB_STATUS)) { ++ dhsprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: successful count(%d), timeout(%d)\n", ++ ioc->name, __func__, count, timeout)); ++ return 0; ++ } else if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { ++ doorbell = readl(&ioc->chip->Doorbell); ++ if ((doorbell & MPI2_IOC_STATE_MASK) == ++ MPI2_IOC_STATE_FAULT) { ++ mpt2sas_base_fault_info(ioc , doorbell); ++ return -EFAULT; ++ } ++ } else if (int_status == 0xFFFFFFFF) ++ goto out; ++ ++ if (sleep_flag == CAN_SLEEP) ++ usleep_range(1000, 1500); ++ else ++ udelay(500); ++ count++; ++ } while (--cntdn); ++ ++ out: ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to timeout count(%d), int_status(%x)!\n", ++ ioc->name, __func__, count, int_status); ++ return -EFAULT; ++} ++ ++/** ++ * _base_wait_for_doorbell_not_used - waiting for doorbell to not be in use ++ * @ioc: per adapter object ++ * @timeout: timeout in second ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ * ++ */ ++static int ++_base_wait_for_doorbell_not_used(struct MPT3SAS_ADAPTER *ioc, int timeout, ++ int sleep_flag) ++{ ++ u32 cntdn, count; ++ u32 doorbell_reg; ++ ++ count = 0; ++ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; ++ do { ++ doorbell_reg = readl(&ioc->chip->Doorbell); ++ if (!(doorbell_reg & MPI2_DOORBELL_USED)) { ++ dhsprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: successful count(%d), timeout(%d)\n", ++ ioc->name, __func__, count, timeout)); ++ return 0; ++ } ++ if (sleep_flag == CAN_SLEEP) ++ usleep_range(1000, 1500); ++ else ++ udelay(500); ++ count++; ++ } while (--cntdn); ++ ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to timeout count(%d), doorbell_reg(%x)!\n", ++ ioc->name, __func__, count, doorbell_reg); ++ return -EFAULT; ++} ++ ++/** ++ * _base_send_ioc_reset - send doorbell reset ++ * @ioc: per adapter object ++ * @reset_type: currently only supports: MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET ++ * @timeout: timeout in second ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout, ++ int sleep_flag) ++{ ++ u32 ioc_state; ++ int r = 0; ++ ++ if (reset_type != MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET) { ++ pr_err(MPT3SAS_FMT "%s: unknown reset_type\n", ++ ioc->name, __func__); ++ return -EFAULT; ++ } ++ ++ if (!(ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY)) ++ return -EFAULT; ++ ++ pr_info(MPT3SAS_FMT "sending message unit reset !!\n", ioc->name); ++ ++ writel(reset_type << MPI2_DOORBELL_FUNCTION_SHIFT, ++ &ioc->chip->Doorbell); ++ if ((_base_wait_for_doorbell_ack(ioc, 15, sleep_flag))) { ++ r = -EFAULT; ++ goto out; ++ } ++ ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, ++ timeout, sleep_flag); ++ if (ioc_state) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed going to ready state (ioc_state=0x%x)\n", ++ ioc->name, __func__, ioc_state); ++ r = -EFAULT; ++ goto out; ++ } ++ out: ++ pr_info(MPT3SAS_FMT "message unit reset: %s\n", ++ ioc->name, ((r == 0) ? "SUCCESS" : "FAILED")); ++ return r; ++} ++ ++/** ++ * _base_handshake_req_reply_wait - send request thru doorbell interface ++ * @ioc: per adapter object ++ * @request_bytes: request length ++ * @request: pointer having request payload ++ * @reply_bytes: reply length ++ * @reply: pointer to reply payload ++ * @timeout: timeout in second ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, ++ u32 *request, int reply_bytes, u16 *reply, int timeout, int sleep_flag) ++{ ++ MPI2DefaultReply_t *default_reply = (MPI2DefaultReply_t *)reply; ++ int i; ++ u8 failed; ++ u16 dummy; ++ __le32 *mfp; ++ ++ /* make sure doorbell is not in use */ ++ if ((readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_USED)) { ++ pr_err(MPT3SAS_FMT ++ "doorbell is in use (line=%d)\n", ++ ioc->name, __LINE__); ++ return -EFAULT; ++ } ++ ++ /* clear pending doorbell interrupts from previous state changes */ ++ if (readl(&ioc->chip->HostInterruptStatus) & ++ MPI2_HIS_IOC2SYS_DB_STATUS) ++ writel(0, &ioc->chip->HostInterruptStatus); ++ ++ /* send message to ioc */ ++ writel(((MPI2_FUNCTION_HANDSHAKE<chip->Doorbell); ++ ++ if ((_base_wait_for_doorbell_int(ioc, 5, NO_SLEEP))) { ++ pr_err(MPT3SAS_FMT ++ "doorbell handshake int failed (line=%d)\n", ++ ioc->name, __LINE__); ++ return -EFAULT; ++ } ++ writel(0, &ioc->chip->HostInterruptStatus); ++ ++ if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag))) { ++ pr_err(MPT3SAS_FMT ++ "doorbell handshake ack failed (line=%d)\n", ++ ioc->name, __LINE__); ++ return -EFAULT; ++ } ++ ++ /* send message 32-bits at a time */ ++ for (i = 0, failed = 0; i < request_bytes/4 && !failed; i++) { ++ writel(cpu_to_le32(request[i]), &ioc->chip->Doorbell); ++ if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag))) ++ failed = 1; ++ } ++ ++ if (failed) { ++ pr_err(MPT3SAS_FMT ++ "doorbell handshake sending request failed (line=%d)\n", ++ ioc->name, __LINE__); ++ return -EFAULT; ++ } ++ ++ /* now wait for the reply */ ++ if ((_base_wait_for_doorbell_int(ioc, timeout, sleep_flag))) { ++ pr_err(MPT3SAS_FMT ++ "doorbell handshake int failed (line=%d)\n", ++ ioc->name, __LINE__); ++ return -EFAULT; ++ } ++ ++ /* read the first two 16-bits, it gives the total length of the reply */ ++ reply[0] = le16_to_cpu(readl(&ioc->chip->Doorbell) ++ & MPI2_DOORBELL_DATA_MASK); ++ writel(0, &ioc->chip->HostInterruptStatus); ++ if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) { ++ pr_err(MPT3SAS_FMT ++ "doorbell handshake int failed (line=%d)\n", ++ ioc->name, __LINE__); ++ return -EFAULT; ++ } ++ reply[1] = le16_to_cpu(readl(&ioc->chip->Doorbell) ++ & MPI2_DOORBELL_DATA_MASK); ++ writel(0, &ioc->chip->HostInterruptStatus); ++ ++ for (i = 2; i < default_reply->MsgLength * 2; i++) { ++ if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) { ++ pr_err(MPT3SAS_FMT ++ "doorbell handshake int failed (line=%d)\n", ++ ioc->name, __LINE__); ++ return -EFAULT; ++ } ++ if (i >= reply_bytes/2) /* overflow case */ ++ dummy = readl(&ioc->chip->Doorbell); ++ else ++ reply[i] = le16_to_cpu(readl(&ioc->chip->Doorbell) ++ & MPI2_DOORBELL_DATA_MASK); ++ writel(0, &ioc->chip->HostInterruptStatus); ++ } ++ ++ _base_wait_for_doorbell_int(ioc, 5, sleep_flag); ++ if (_base_wait_for_doorbell_not_used(ioc, 5, sleep_flag) != 0) { ++ dhsprintk(ioc, pr_info(MPT3SAS_FMT ++ "doorbell is in use (line=%d)\n", ioc->name, __LINE__)); ++ } ++ writel(0, &ioc->chip->HostInterruptStatus); ++ ++ if (ioc->logging_level & MPT_DEBUG_INIT) { ++ mfp = (__le32 *)reply; ++ pr_info("\toffset:data\n"); ++ for (i = 0; i < reply_bytes/4; i++) ++ pr_info("\t[0x%02x]:%08x\n", i*4, ++ le32_to_cpu(mfp[i])); ++ } ++ return 0; ++} ++ ++/** ++ * mpt2sas_base_sas_iounit_control - send sas iounit control to FW ++ * @ioc: per adapter object ++ * @mpi_reply: the reply payload from FW ++ * @mpi_request: the request payload sent to FW ++ * ++ * The SAS IO Unit Control Request message allows the host to perform low-level ++ * operations, such as resets on the PHYs of the IO Unit, also allows the host ++ * to obtain the IOC assigned device handles for a device if it has other ++ * identifying information about the device, in addition allows the host to ++ * remove IOC resources associated with the device. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2SasIoUnitControlReply_t *mpi_reply, ++ Mpi2SasIoUnitControlRequest_t *mpi_request) ++{ ++ u16 smid; ++ u32 ioc_state; ++ unsigned long timeleft; ++ bool issue_reset = false; ++ int rc; ++ void *request; ++ u16 wait_state_count; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ mutex_lock(&ioc->base_cmds.mutex); ++ ++ if (ioc->base_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: base_cmd in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, __func__, wait_state_count); ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ rc = 0; ++ ioc->base_cmds.status = MPT3_CMD_PENDING; ++ request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->base_cmds.smid = smid; ++ memcpy(request, mpi_request, sizeof(Mpi2SasIoUnitControlRequest_t)); ++ if (mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || ++ mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) ++ ioc->ioc_link_reset_in_progress = 1; ++ init_completion(&ioc->base_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, ++ msecs_to_jiffies(10000)); ++ if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || ++ mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) && ++ ioc->ioc_link_reset_in_progress) ++ ioc->ioc_link_reset_in_progress = 0; ++ if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SasIoUnitControlRequest_t)/4); ++ if (!(ioc->base_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = true; ++ goto issue_host_reset; ++ } ++ if (ioc->base_cmds.status & MPT3_CMD_REPLY_VALID) ++ memcpy(mpi_reply, ioc->base_cmds.reply, ++ sizeof(Mpi2SasIoUnitControlReply_t)); ++ else ++ memset(mpi_reply, 0, sizeof(Mpi2SasIoUnitControlReply_t)); ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ goto out; ++ ++ issue_host_reset: ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ rc = -EFAULT; ++ out: ++ mutex_unlock(&ioc->base_cmds.mutex); ++ return rc; ++} ++ ++/** ++ * mpt2sas_base_scsi_enclosure_processor - sending request to sep device ++ * @ioc: per adapter object ++ * @mpi_reply: the reply payload from FW ++ * @mpi_request: the request payload sent to FW ++ * ++ * The SCSI Enclosure Processor request message causes the IOC to ++ * communicate with SES devices to control LED status signals. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request) ++{ ++ u16 smid; ++ u32 ioc_state; ++ unsigned long timeleft; ++ bool issue_reset = false; ++ int rc; ++ void *request; ++ u16 wait_state_count; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ mutex_lock(&ioc->base_cmds.mutex); ++ ++ if (ioc->base_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: base_cmd in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, ++ __func__, wait_state_count); ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ rc = 0; ++ ioc->base_cmds.status = MPT3_CMD_PENDING; ++ request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->base_cmds.smid = smid; ++ memcpy(request, mpi_request, sizeof(Mpi2SepReply_t)); ++ init_completion(&ioc->base_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, ++ msecs_to_jiffies(10000)); ++ if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SepRequest_t)/4); ++ if (!(ioc->base_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = false; ++ goto issue_host_reset; ++ } ++ if (ioc->base_cmds.status & MPT3_CMD_REPLY_VALID) ++ memcpy(mpi_reply, ioc->base_cmds.reply, ++ sizeof(Mpi2SepReply_t)); ++ else ++ memset(mpi_reply, 0, sizeof(Mpi2SepReply_t)); ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ goto out; ++ ++ issue_host_reset: ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ rc = -EFAULT; ++ out: ++ mutex_unlock(&ioc->base_cmds.mutex); ++ return rc; ++} ++ ++/** ++ * _base_get_port_facts - obtain port facts reply and save in ioc ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port, int sleep_flag) ++{ ++ Mpi2PortFactsRequest_t mpi_request; ++ Mpi2PortFactsReply_t mpi_reply; ++ struct mpt2sas_port_facts *pfacts; ++ int mpi_reply_sz, mpi_request_sz, r; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ mpi_reply_sz = sizeof(Mpi2PortFactsReply_t); ++ mpi_request_sz = sizeof(Mpi2PortFactsRequest_t); ++ memset(&mpi_request, 0, mpi_request_sz); ++ mpi_request.Function = MPI2_FUNCTION_PORT_FACTS; ++ mpi_request.PortNumber = port; ++ r = _base_handshake_req_reply_wait(ioc, mpi_request_sz, ++ (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP); ++ ++ if (r != 0) { ++ pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n", ++ ioc->name, __func__, r); ++ return r; ++ } ++ ++ pfacts = &ioc->pfacts[port]; ++ memset(pfacts, 0, sizeof(struct mpt2sas_port_facts)); ++ pfacts->PortNumber = mpi_reply.PortNumber; ++ pfacts->VP_ID = mpi_reply.VP_ID; ++ pfacts->VF_ID = mpi_reply.VF_ID; ++ pfacts->MaxPostedCmdBuffers = ++ le16_to_cpu(mpi_reply.MaxPostedCmdBuffers); ++ ++ return 0; ++} ++ ++/** ++ * _base_wait_for_iocstate - Wait until the card is in READY or OPERATIONAL ++ * @ioc: per adapter object ++ * @timeout: ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout, ++ int sleep_flag) ++{ ++ u32 ioc_state; ++ int rc; ++ ++ dinitprintk(ioc, printk(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ if (ioc->pci_error_recovery) { ++ dfailprintk(ioc, printk(MPT3SAS_FMT ++ "%s: host in pci error recovery\n", ioc->name, __func__)); ++ return -EFAULT; ++ } ++ ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 0); ++ dhsprintk(ioc, printk(MPT3SAS_FMT "%s: ioc_state(0x%08x)\n", ++ ioc->name, __func__, ioc_state)); ++ ++ if (((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY) || ++ (ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL) ++ return 0; ++ ++ if (ioc_state & MPI2_DOORBELL_USED) { ++ dhsprintk(ioc, printk(MPT3SAS_FMT ++ "unexpected doorbell active!\n", ioc->name)); ++ goto issue_diag_reset; ++ } ++ ++ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { ++ mpt2sas_base_fault_info(ioc, ioc_state & ++ MPI2_DOORBELL_DATA_MASK); ++ goto issue_diag_reset; ++ } ++ ++ ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, ++ timeout, sleep_flag); ++ if (ioc_state) { ++ dfailprintk(ioc, printk(MPT3SAS_FMT ++ "%s: failed going to ready state (ioc_state=0x%x)\n", ++ ioc->name, __func__, ioc_state)); ++ return -EFAULT; ++ } ++ ++ issue_diag_reset: ++ rc = _base_diag_reset(ioc, sleep_flag); ++ return rc; ++} ++ ++/** ++ * _base_get_ioc_facts - obtain ioc facts reply and save in ioc ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ Mpi2IOCFactsRequest_t mpi_request; ++ Mpi2IOCFactsReply_t mpi_reply; ++ struct mpt2sas_facts *facts; ++ int mpi_reply_sz, mpi_request_sz, r; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ r = _base_wait_for_iocstate(ioc, 10, sleep_flag); ++ if (r) { ++ dfailprintk(ioc, printk(MPT3SAS_FMT ++ "%s: failed getting to correct state\n", ++ ioc->name, __func__)); ++ return r; ++ } ++ mpi_reply_sz = sizeof(Mpi2IOCFactsReply_t); ++ mpi_request_sz = sizeof(Mpi2IOCFactsRequest_t); ++ memset(&mpi_request, 0, mpi_request_sz); ++ mpi_request.Function = MPI2_FUNCTION_IOC_FACTS; ++ r = _base_handshake_req_reply_wait(ioc, mpi_request_sz, ++ (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP); ++ ++ if (r != 0) { ++ pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n", ++ ioc->name, __func__, r); ++ return r; ++ } ++ ++ facts = &ioc->facts; ++ memset(facts, 0, sizeof(struct mpt2sas_facts)); ++ facts->MsgVersion = le16_to_cpu(mpi_reply.MsgVersion); ++ facts->HeaderVersion = le16_to_cpu(mpi_reply.HeaderVersion); ++ facts->VP_ID = mpi_reply.VP_ID; ++ facts->VF_ID = mpi_reply.VF_ID; ++ facts->IOCExceptions = le16_to_cpu(mpi_reply.IOCExceptions); ++ facts->MaxChainDepth = mpi_reply.MaxChainDepth; ++ facts->WhoInit = mpi_reply.WhoInit; ++ facts->NumberOfPorts = mpi_reply.NumberOfPorts; ++ facts->MaxMSIxVectors = mpi_reply.MaxMSIxVectors; ++ facts->RequestCredit = le16_to_cpu(mpi_reply.RequestCredit); ++ facts->MaxReplyDescriptorPostQueueDepth = ++ le16_to_cpu(mpi_reply.MaxReplyDescriptorPostQueueDepth); ++ facts->ProductID = le16_to_cpu(mpi_reply.ProductID); ++ facts->IOCCapabilities = le32_to_cpu(mpi_reply.IOCCapabilities); ++ if ((facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID)) ++ ioc->ir_firmware = 1; ++ if ((facts->IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE)) ++ ioc->rdpq_array_capable = 1; ++ facts->FWVersion.Word = le32_to_cpu(mpi_reply.FWVersion.Word); ++ facts->IOCRequestFrameSize = ++ le16_to_cpu(mpi_reply.IOCRequestFrameSize); ++ if (ioc->hba_mpi_version_belonged != MPI2_VERSION) { ++ facts->IOCMaxChainSegmentSize = ++ le16_to_cpu(mpi_reply.IOCMaxChainSegmentSize); ++ } ++ facts->MaxInitiators = le16_to_cpu(mpi_reply.MaxInitiators); ++ facts->MaxTargets = le16_to_cpu(mpi_reply.MaxTargets); ++ ioc->shost->max_id = -1; ++ facts->MaxSasExpanders = le16_to_cpu(mpi_reply.MaxSasExpanders); ++ facts->MaxEnclosures = le16_to_cpu(mpi_reply.MaxEnclosures); ++ facts->ProtocolFlags = le16_to_cpu(mpi_reply.ProtocolFlags); ++ facts->HighPriorityCredit = ++ le16_to_cpu(mpi_reply.HighPriorityCredit); ++ facts->ReplyFrameSize = mpi_reply.ReplyFrameSize; ++ facts->MaxDevHandle = le16_to_cpu(mpi_reply.MaxDevHandle); ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "hba queue depth(%d), max chains per io(%d)\n", ++ ioc->name, facts->RequestCredit, ++ facts->MaxChainDepth)); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "request frame size(%d), reply frame size(%d)\n", ioc->name, ++ facts->IOCRequestFrameSize * 4, facts->ReplyFrameSize * 4)); ++ return 0; ++} ++ ++/** ++ * _base_send_ioc_init - send ioc_init to firmware ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ Mpi2IOCInitRequest_t mpi_request; ++ Mpi2IOCInitReply_t mpi_reply; ++ int i, r = 0; ++ ktime_t current_time; ++ u16 ioc_status; ++ u32 reply_post_free_array_sz = 0; ++ Mpi2IOCInitRDPQArrayEntry *reply_post_free_array = NULL; ++ dma_addr_t reply_post_free_array_dma; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ memset(&mpi_request, 0, sizeof(Mpi2IOCInitRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_IOC_INIT; ++ mpi_request.WhoInit = MPI2_WHOINIT_HOST_DRIVER; ++ mpi_request.VF_ID = 0; /* TODO */ ++ mpi_request.VP_ID = 0; ++ mpi_request.MsgVersion = cpu_to_le16(ioc->hba_mpi_version_belonged); ++ mpi_request.HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION); ++ ++ if (_base_is_controller_msix_enabled(ioc)) ++ mpi_request.HostMSIxVectors = ioc->reply_queue_count; ++ mpi_request.SystemRequestFrameSize = cpu_to_le16(ioc->request_sz/4); ++ mpi_request.ReplyDescriptorPostQueueDepth = ++ cpu_to_le16(ioc->reply_post_queue_depth); ++ mpi_request.ReplyFreeQueueDepth = ++ cpu_to_le16(ioc->reply_free_queue_depth); ++ ++ mpi_request.SenseBufferAddressHigh = ++ cpu_to_le32((u64)ioc->sense_dma >> 32); ++ mpi_request.SystemReplyAddressHigh = ++ cpu_to_le32((u64)ioc->reply_dma >> 32); ++ mpi_request.SystemRequestFrameBaseAddress = ++ cpu_to_le64((u64)ioc->request_dma); ++ mpi_request.ReplyFreeQueueAddress = ++ cpu_to_le64((u64)ioc->reply_free_dma); ++ ++ if (ioc->rdpq_array_enable) { ++ reply_post_free_array_sz = ioc->reply_queue_count * ++ sizeof(Mpi2IOCInitRDPQArrayEntry); ++ reply_post_free_array = pci_alloc_consistent(ioc->pdev, ++ reply_post_free_array_sz, &reply_post_free_array_dma); ++ if (!reply_post_free_array) { ++ pr_err(MPT3SAS_FMT ++ "reply_post_free_array: pci_alloc_consistent failed\n", ++ ioc->name); ++ r = -ENOMEM; ++ goto out; ++ } ++ memset(reply_post_free_array, 0, reply_post_free_array_sz); ++ for (i = 0; i < ioc->reply_queue_count; i++) ++ reply_post_free_array[i].RDPQBaseAddress = ++ cpu_to_le64( ++ (u64)ioc->reply_post[i].reply_post_free_dma); ++ mpi_request.MsgFlags = MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE; ++ mpi_request.ReplyDescriptorPostQueueAddress = ++ cpu_to_le64((u64)reply_post_free_array_dma); ++ } else { ++ mpi_request.ReplyDescriptorPostQueueAddress = ++ cpu_to_le64((u64)ioc->reply_post[0].reply_post_free_dma); ++ } ++ ++ /* This time stamp specifies number of milliseconds ++ * since epoch ~ midnight January 1, 1970. ++ */ ++ current_time = ktime_get_real(); ++ mpi_request.TimeStamp = cpu_to_le64(ktime_to_ms(current_time)); ++ ++ if (ioc->logging_level & MPT_DEBUG_INIT) { ++ __le32 *mfp; ++ int i; ++ ++ mfp = (__le32 *)&mpi_request; ++ pr_info("\toffset:data\n"); ++ for (i = 0; i < sizeof(Mpi2IOCInitRequest_t)/4; i++) ++ pr_info("\t[0x%02x]:%08x\n", i*4, ++ le32_to_cpu(mfp[i])); ++ } ++ ++ r = _base_handshake_req_reply_wait(ioc, ++ sizeof(Mpi2IOCInitRequest_t), (u32 *)&mpi_request, ++ sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 10, ++ sleep_flag); ++ ++ if (r != 0) { ++ pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n", ++ ioc->name, __func__, r); ++ goto out; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS || ++ mpi_reply.IOCLogInfo) { ++ pr_err(MPT3SAS_FMT "%s: failed\n", ioc->name, __func__); ++ r = -EIO; ++ } ++ ++out: ++ if (reply_post_free_array) ++ pci_free_consistent(ioc->pdev, reply_post_free_array_sz, ++ reply_post_free_array, ++ reply_post_free_array_dma); ++ return r; ++} ++ ++/** ++ * mpt2sas_port_enable_done - command completion routine for port enable ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_port_enable_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ u16 ioc_status; ++ ++ if (ioc->port_enable_cmds.status == MPT3_CMD_NOT_USED) ++ return 1; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (!mpi_reply) ++ return 1; ++ ++ if (mpi_reply->Function != MPI2_FUNCTION_PORT_ENABLE) ++ return 1; ++ ++ ioc->port_enable_cmds.status &= ~MPT3_CMD_PENDING; ++ ioc->port_enable_cmds.status |= MPT3_CMD_COMPLETE; ++ ioc->port_enable_cmds.status |= MPT3_CMD_REPLY_VALID; ++ memcpy(ioc->port_enable_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ ioc->port_enable_failed = 1; ++ ++ if (ioc->is_driver_loading) { ++ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ++ mpt2sas_port_enable_complete(ioc); ++ return 1; ++ } else { ++ ioc->start_scan_failed = ioc_status; ++ ioc->start_scan = 0; ++ return 1; ++ } ++ } ++ complete(&ioc->port_enable_cmds.done); ++ return 1; ++} ++ ++/** ++ * _base_send_port_enable - send port_enable(discovery stuff) to firmware ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_send_port_enable(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ Mpi2PortEnableRequest_t *mpi_request; ++ Mpi2PortEnableReply_t *mpi_reply; ++ unsigned long timeleft; ++ int r = 0; ++ u16 smid; ++ u16 ioc_status; ++ ++ pr_info(MPT3SAS_FMT "sending port enable !!\n", ioc->name); ++ ++ if (ioc->port_enable_cmds.status & MPT3_CMD_PENDING) { ++ pr_err(MPT3SAS_FMT "%s: internal command already in use\n", ++ ioc->name, __func__); ++ return -EAGAIN; ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->port_enable_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ return -EAGAIN; ++ } ++ ++ ioc->port_enable_cmds.status = MPT3_CMD_PENDING; ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->port_enable_cmds.smid = smid; ++ memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE; ++ ++ init_completion(&ioc->port_enable_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->port_enable_cmds.done, ++ 300*HZ); ++ if (!(ioc->port_enable_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2PortEnableRequest_t)/4); ++ if (ioc->port_enable_cmds.status & MPT3_CMD_RESET) ++ r = -EFAULT; ++ else ++ r = -ETIME; ++ goto out; ++ } ++ ++ mpi_reply = ioc->port_enable_cmds.reply; ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "%s: failed with (ioc_status=0x%08x)\n", ++ ioc->name, __func__, ioc_status); ++ r = -EFAULT; ++ goto out; ++ } ++ ++ out: ++ ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED; ++ pr_info(MPT3SAS_FMT "port enable: %s\n", ioc->name, ((r == 0) ? ++ "SUCCESS" : "FAILED")); ++ return r; ++} ++ ++/** ++ * mpt2sas_port_enable - initiate firmware discovery (don't wait for reply) ++ * @ioc: per adapter object ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_port_enable(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2PortEnableRequest_t *mpi_request; ++ u16 smid; ++ ++ pr_info(MPT3SAS_FMT "sending port enable !!\n", ioc->name); ++ ++ if (ioc->port_enable_cmds.status & MPT3_CMD_PENDING) { ++ pr_err(MPT3SAS_FMT "%s: internal command already in use\n", ++ ioc->name, __func__); ++ return -EAGAIN; ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->port_enable_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ return -EAGAIN; ++ } ++ ++ ioc->port_enable_cmds.status = MPT3_CMD_PENDING; ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->port_enable_cmds.smid = smid; ++ memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE; ++ ++ mpt2sas_base_put_smid_default(ioc, smid); ++ return 0; ++} ++ ++/** ++ * _base_determine_wait_on_discovery - desposition ++ * @ioc: per adapter object ++ * ++ * Decide whether to wait on discovery to complete. Used to either ++ * locate boot device, or report volumes ahead of physical devices. ++ * ++ * Returns 1 for wait, 0 for don't wait ++ */ ++static int ++_base_determine_wait_on_discovery(struct MPT3SAS_ADAPTER *ioc) ++{ ++ /* We wait for discovery to complete if IR firmware is loaded. ++ * The sas topology events arrive before PD events, so we need time to ++ * turn on the bit in ioc->pd_handles to indicate PD ++ * Also, it maybe required to report Volumes ahead of physical ++ * devices when MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING is set. ++ */ ++ if (ioc->ir_firmware) ++ return 1; ++ ++ /* if no Bios, then we don't need to wait */ ++ if (!ioc->bios_pg3.BiosVersion) ++ return 0; ++ ++ /* Bios is present, then we drop down here. ++ * ++ * If there any entries in the Bios Page 2, then we wait ++ * for discovery to complete. ++ */ ++ ++ /* Current Boot Device */ ++ if ((ioc->bios_pg2.CurrentBootDeviceForm & ++ MPI2_BIOSPAGE2_FORM_MASK) == ++ MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED && ++ /* Request Boot Device */ ++ (ioc->bios_pg2.ReqBootDeviceForm & ++ MPI2_BIOSPAGE2_FORM_MASK) == ++ MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED && ++ /* Alternate Request Boot Device */ ++ (ioc->bios_pg2.ReqAltBootDeviceForm & ++ MPI2_BIOSPAGE2_FORM_MASK) == ++ MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED) ++ return 0; ++ ++ return 1; ++} ++ ++/** ++ * _base_unmask_events - turn on notification for this event ++ * @ioc: per adapter object ++ * @event: firmware event ++ * ++ * The mask is stored in ioc->event_masks. ++ */ ++static void ++_base_unmask_events(struct MPT3SAS_ADAPTER *ioc, u16 event) ++{ ++ u32 desired_event; ++ ++ if (event >= 128) ++ return; ++ ++ desired_event = (1 << (event % 32)); ++ ++ if (event < 32) ++ ioc->event_masks[0] &= ~desired_event; ++ else if (event < 64) ++ ioc->event_masks[1] &= ~desired_event; ++ else if (event < 96) ++ ioc->event_masks[2] &= ~desired_event; ++ else if (event < 128) ++ ioc->event_masks[3] &= ~desired_event; ++} ++ ++/** ++ * _base_event_notification - send event notification ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_event_notification(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ Mpi2EventNotificationRequest_t *mpi_request; ++ unsigned long timeleft; ++ u16 smid; ++ int r = 0; ++ int i; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ if (ioc->base_cmds.status & MPT3_CMD_PENDING) { ++ pr_err(MPT3SAS_FMT "%s: internal command already in use\n", ++ ioc->name, __func__); ++ return -EAGAIN; ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ return -EAGAIN; ++ } ++ ioc->base_cmds.status = MPT3_CMD_PENDING; ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->base_cmds.smid = smid; ++ memset(mpi_request, 0, sizeof(Mpi2EventNotificationRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_EVENT_NOTIFICATION; ++ mpi_request->VF_ID = 0; /* TODO */ ++ mpi_request->VP_ID = 0; ++ for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) ++ mpi_request->EventMasks[i] = ++ cpu_to_le32(ioc->event_masks[i]); ++ init_completion(&ioc->base_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ); ++ if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2EventNotificationRequest_t)/4); ++ if (ioc->base_cmds.status & MPT3_CMD_RESET) ++ r = -EFAULT; ++ else ++ r = -ETIME; ++ } else ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s: complete\n", ++ ioc->name, __func__)); ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ return r; ++} ++ ++/** ++ * mpt2sas_base_validate_event_type - validating event types ++ * @ioc: per adapter object ++ * @event: firmware event ++ * ++ * This will turn on firmware event notification when application ++ * ask for that event. We don't mask events that are already enabled. ++ */ ++void ++mpt2sas_base_validate_event_type(struct MPT3SAS_ADAPTER *ioc, u32 *event_type) ++{ ++ int i, j; ++ u32 event_mask, desired_event; ++ u8 send_update_to_fw; ++ ++ for (i = 0, send_update_to_fw = 0; i < ++ MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) { ++ event_mask = ~event_type[i]; ++ desired_event = 1; ++ for (j = 0; j < 32; j++) { ++ if (!(event_mask & desired_event) && ++ (ioc->event_masks[i] & desired_event)) { ++ ioc->event_masks[i] &= ~desired_event; ++ send_update_to_fw = 1; ++ } ++ desired_event = (desired_event << 1); ++ } ++ } ++ ++ if (!send_update_to_fw) ++ return; ++ ++ mutex_lock(&ioc->base_cmds.mutex); ++ _base_event_notification(ioc, CAN_SLEEP); ++ mutex_unlock(&ioc->base_cmds.mutex); ++} ++ ++/** ++ * _base_diag_reset - the "big hammer" start of day reset ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ u32 host_diagnostic; ++ u32 ioc_state; ++ u32 count; ++ u32 hcb_size; ++ ++ pr_info(MPT3SAS_FMT "sending diag reset !!\n", ioc->name); ++ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT "clear interrupts\n", ++ ioc->name)); ++ ++ count = 0; ++ do { ++ /* Write magic sequence to WriteSequence register ++ * Loop until in diagnostic mode ++ */ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT ++ "write magic sequence\n", ioc->name)); ++ writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence); ++ writel(MPI2_WRSEQ_1ST_KEY_VALUE, &ioc->chip->WriteSequence); ++ writel(MPI2_WRSEQ_2ND_KEY_VALUE, &ioc->chip->WriteSequence); ++ writel(MPI2_WRSEQ_3RD_KEY_VALUE, &ioc->chip->WriteSequence); ++ writel(MPI2_WRSEQ_4TH_KEY_VALUE, &ioc->chip->WriteSequence); ++ writel(MPI2_WRSEQ_5TH_KEY_VALUE, &ioc->chip->WriteSequence); ++ writel(MPI2_WRSEQ_6TH_KEY_VALUE, &ioc->chip->WriteSequence); ++ ++ /* wait 100 msec */ ++ if (sleep_flag == CAN_SLEEP) ++ msleep(100); ++ else ++ mdelay(100); ++ ++ if (count++ > 20) ++ goto out; ++ ++ host_diagnostic = readl(&ioc->chip->HostDiagnostic); ++ drsprintk(ioc, pr_info(MPT3SAS_FMT ++ "wrote magic sequence: count(%d), host_diagnostic(0x%08x)\n", ++ ioc->name, count, host_diagnostic)); ++ ++ } while ((host_diagnostic & MPI2_DIAG_DIAG_WRITE_ENABLE) == 0); ++ ++ hcb_size = readl(&ioc->chip->HCBSize); ++ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT "diag reset: issued\n", ++ ioc->name)); ++ writel(host_diagnostic | MPI2_DIAG_RESET_ADAPTER, ++ &ioc->chip->HostDiagnostic); ++ ++ /*This delay allows the chip PCIe hardware time to finish reset tasks*/ ++ if (sleep_flag == CAN_SLEEP) ++ msleep(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000); ++ else ++ mdelay(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000); ++ ++ /* Approximately 300 second max wait */ ++ for (count = 0; count < (300000000 / ++ MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC); count++) { ++ ++ host_diagnostic = readl(&ioc->chip->HostDiagnostic); ++ ++ if (host_diagnostic == 0xFFFFFFFF) ++ goto out; ++ if (!(host_diagnostic & MPI2_DIAG_RESET_ADAPTER)) ++ break; ++ ++ /* Wait to pass the second read delay window */ ++ if (sleep_flag == CAN_SLEEP) ++ msleep(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC ++ / 1000); ++ else ++ mdelay(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC ++ / 1000); ++ } ++ ++ if (host_diagnostic & MPI2_DIAG_HCB_MODE) { ++ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT ++ "restart the adapter assuming the HCB Address points to good F/W\n", ++ ioc->name)); ++ host_diagnostic &= ~MPI2_DIAG_BOOT_DEVICE_SELECT_MASK; ++ host_diagnostic |= MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW; ++ writel(host_diagnostic, &ioc->chip->HostDiagnostic); ++ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT ++ "re-enable the HCDW\n", ioc->name)); ++ writel(hcb_size | MPI2_HCB_SIZE_HCB_ENABLE, ++ &ioc->chip->HCBSize); ++ } ++ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT "restart the adapter\n", ++ ioc->name)); ++ writel(host_diagnostic & ~MPI2_DIAG_HOLD_IOC_RESET, ++ &ioc->chip->HostDiagnostic); ++ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT ++ "disable writes to the diagnostic register\n", ioc->name)); ++ writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence); ++ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT ++ "Wait for FW to go to the READY state\n", ioc->name)); ++ ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20, ++ sleep_flag); ++ if (ioc_state) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed going to ready state (ioc_state=0x%x)\n", ++ ioc->name, __func__, ioc_state); ++ goto out; ++ } ++ ++ pr_info(MPT3SAS_FMT "diag reset: SUCCESS\n", ioc->name); ++ return 0; ++ ++ out: ++ pr_err(MPT3SAS_FMT "diag reset: FAILED\n", ioc->name); ++ return -EFAULT; ++} ++ ++/** ++ * _base_make_ioc_ready - put controller in READY state ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * @type: FORCE_BIG_HAMMER or SOFT_RESET ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, ++ enum reset_type type) ++{ ++ u32 ioc_state; ++ int rc; ++ int count; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ if (ioc->pci_error_recovery) ++ return 0; ++ ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 0); ++ dhsprintk(ioc, pr_info(MPT3SAS_FMT "%s: ioc_state(0x%08x)\n", ++ ioc->name, __func__, ioc_state)); ++ ++ /* if in RESET state, it should move to READY state shortly */ ++ count = 0; ++ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_RESET) { ++ while ((ioc_state & MPI2_IOC_STATE_MASK) != ++ MPI2_IOC_STATE_READY) { ++ if (count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed going to ready state (ioc_state=0x%x)\n", ++ ioc->name, __func__, ioc_state); ++ return -EFAULT; ++ } ++ if (sleep_flag == CAN_SLEEP) ++ ssleep(1); ++ else ++ mdelay(1000); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 0); ++ } ++ } ++ ++ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY) ++ return 0; ++ ++ if (ioc_state & MPI2_DOORBELL_USED) { ++ dhsprintk(ioc, pr_info(MPT3SAS_FMT ++ "unexpected doorbell active!\n", ++ ioc->name)); ++ goto issue_diag_reset; ++ } ++ ++ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { ++ mpt2sas_base_fault_info(ioc, ioc_state & ++ MPI2_DOORBELL_DATA_MASK); ++ goto issue_diag_reset; ++ } ++ ++ if (type == FORCE_BIG_HAMMER) ++ goto issue_diag_reset; ++ ++ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL) ++ if (!(_base_send_ioc_reset(ioc, ++ MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET, 15, CAN_SLEEP))) { ++ return 0; ++ } ++ ++ issue_diag_reset: ++ rc = _base_diag_reset(ioc, CAN_SLEEP); ++ return rc; ++} ++ ++/** ++ * _base_make_ioc_operational - put controller in OPERATIONAL state ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ int r, i, index; ++ unsigned long flags; ++ u32 reply_address; ++ u16 smid; ++ struct _tr_list *delayed_tr, *delayed_tr_next; ++ struct _sc_list *delayed_sc, *delayed_sc_next; ++ struct _event_ack_list *delayed_event_ack, *delayed_event_ack_next; ++ u8 hide_flag; ++ struct adapter_reply_queue *reply_q; ++ Mpi2ReplyDescriptorsUnion_t *reply_post_free_contig; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ /* clean the delayed target reset list */ ++ list_for_each_entry_safe(delayed_tr, delayed_tr_next, ++ &ioc->delayed_tr_list, list) { ++ list_del(&delayed_tr->list); ++ kfree(delayed_tr); ++ } ++ ++ ++ list_for_each_entry_safe(delayed_tr, delayed_tr_next, ++ &ioc->delayed_tr_volume_list, list) { ++ list_del(&delayed_tr->list); ++ kfree(delayed_tr); ++ } ++ ++ list_for_each_entry_safe(delayed_sc, delayed_sc_next, ++ &ioc->delayed_sc_list, list) { ++ list_del(&delayed_sc->list); ++ kfree(delayed_sc); ++ } ++ ++ list_for_each_entry_safe(delayed_event_ack, delayed_event_ack_next, ++ &ioc->delayed_event_ack_list, list) { ++ list_del(&delayed_event_ack->list); ++ kfree(delayed_event_ack); ++ } ++ ++ /* initialize the scsi lookup free list */ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ INIT_LIST_HEAD(&ioc->free_list); ++ smid = 1; ++ for (i = 0; i < ioc->scsiio_depth; i++, smid++) { ++ INIT_LIST_HEAD(&ioc->scsi_lookup[i].chain_list); ++ ioc->scsi_lookup[i].cb_idx = 0xFF; ++ ioc->scsi_lookup[i].smid = smid; ++ ioc->scsi_lookup[i].scmd = NULL; ++ ioc->scsi_lookup[i].direct_io = 0; ++ list_add_tail(&ioc->scsi_lookup[i].tracker_list, ++ &ioc->free_list); ++ } ++ ++ /* hi-priority queue */ ++ INIT_LIST_HEAD(&ioc->hpr_free_list); ++ smid = ioc->hi_priority_smid; ++ for (i = 0; i < ioc->hi_priority_depth; i++, smid++) { ++ ioc->hpr_lookup[i].cb_idx = 0xFF; ++ ioc->hpr_lookup[i].smid = smid; ++ list_add_tail(&ioc->hpr_lookup[i].tracker_list, ++ &ioc->hpr_free_list); ++ } ++ ++ /* internal queue */ ++ INIT_LIST_HEAD(&ioc->internal_free_list); ++ smid = ioc->internal_smid; ++ for (i = 0; i < ioc->internal_depth; i++, smid++) { ++ ioc->internal_lookup[i].cb_idx = 0xFF; ++ ioc->internal_lookup[i].smid = smid; ++ list_add_tail(&ioc->internal_lookup[i].tracker_list, ++ &ioc->internal_free_list); ++ } ++ ++ /* chain pool */ ++ INIT_LIST_HEAD(&ioc->free_chain_list); ++ for (i = 0; i < ioc->chain_depth; i++) ++ list_add_tail(&ioc->chain_lookup[i].tracker_list, ++ &ioc->free_chain_list); ++ ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ ++ /* initialize Reply Free Queue */ ++ for (i = 0, reply_address = (u32)ioc->reply_dma ; ++ i < ioc->reply_free_queue_depth ; i++, reply_address += ++ ioc->reply_sz) ++ ioc->reply_free[i] = cpu_to_le32(reply_address); ++ ++ /* initialize reply queues */ ++ if (ioc->is_driver_loading) ++ _base_assign_reply_queues(ioc); ++ ++ /* initialize Reply Post Free Queue */ ++ index = 0; ++ reply_post_free_contig = ioc->reply_post[0].reply_post_free; ++ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { ++ /* ++ * If RDPQ is enabled, switch to the next allocation. ++ * Otherwise advance within the contiguous region. ++ */ ++ if (ioc->rdpq_array_enable) { ++ reply_q->reply_post_free = ++ ioc->reply_post[index++].reply_post_free; ++ } else { ++ reply_q->reply_post_free = reply_post_free_contig; ++ reply_post_free_contig += ioc->reply_post_queue_depth; ++ } ++ ++ reply_q->reply_post_host_index = 0; ++ for (i = 0; i < ioc->reply_post_queue_depth; i++) ++ reply_q->reply_post_free[i].Words = ++ cpu_to_le64(ULLONG_MAX); ++ if (!_base_is_controller_msix_enabled(ioc)) ++ goto skip_init_reply_post_free_queue; ++ } ++ skip_init_reply_post_free_queue: ++ ++ r = _base_send_ioc_init(ioc, sleep_flag); ++ if (r) ++ return r; ++ ++ /* initialize reply free host index */ ++ ioc->reply_free_host_index = ioc->reply_free_queue_depth - 1; ++ writel(ioc->reply_free_host_index, &ioc->chip->ReplyFreeHostIndex); ++ ++ /* initialize reply post host index */ ++ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { ++ if (ioc->msix96_vector) ++ writel((reply_q->msix_index & 7)<< ++ MPI2_RPHI_MSIX_INDEX_SHIFT, ++ ioc->replyPostRegisterIndex[reply_q->msix_index/8]); ++ else ++ writel(reply_q->msix_index << ++ MPI2_RPHI_MSIX_INDEX_SHIFT, ++ &ioc->chip->ReplyPostHostIndex); ++ ++ if (!_base_is_controller_msix_enabled(ioc)) ++ goto skip_init_reply_post_host_index; ++ } ++ ++ skip_init_reply_post_host_index: ++ ++ _base_unmask_interrupts(ioc); ++ r = _base_event_notification(ioc, sleep_flag); ++ if (r) ++ return r; ++ ++ if (sleep_flag == CAN_SLEEP) ++ _base_static_config_pages(ioc); ++ ++ ++ if (ioc->is_driver_loading) { ++ ++ if (ioc->is_warpdrive && ioc->manu_pg10.OEMIdentifier ++ == 0x80) { ++ hide_flag = (u8) ( ++ le32_to_cpu(ioc->manu_pg10.OEMSpecificFlags0) & ++ MFG_PAGE10_HIDE_SSDS_MASK); ++ if (hide_flag != MFG_PAGE10_HIDE_SSDS_MASK) ++ ioc->mfg_pg10_hide_flag = hide_flag; ++ } ++ ++ ioc->wait_for_discovery_to_complete = ++ _base_determine_wait_on_discovery(ioc); ++ ++ return r; /* scan_start and scan_finished support */ ++ } ++ ++ r = _base_send_port_enable(ioc, sleep_flag); ++ if (r) ++ return r; ++ ++ return r; ++} ++ ++/** ++ * mpt2sas_base_free_resources - free resources controller resources ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc) ++{ ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ /* synchronizing freeing resource with pci_access_mutex lock */ ++ mutex_lock(&ioc->pci_access_mutex); ++ if (ioc->chip_phys && ioc->chip) { ++ _base_mask_interrupts(ioc); ++ ioc->shost_recovery = 1; ++ _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); ++ ioc->shost_recovery = 0; ++ } ++ ++ mpt2sas_base_unmap_resources(ioc); ++ mutex_unlock(&ioc->pci_access_mutex); ++ return; ++} ++ ++/** ++ * mpt2sas_base_attach - attach controller instance ++ * @ioc: per adapter object ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_base_attach(struct MPT3SAS_ADAPTER *ioc) ++{ ++ int r, i; ++ int cpu_id, last_cpu_id = 0; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ /* setup cpu_msix_table */ ++ ioc->cpu_count = num_online_cpus(); ++ for_each_online_cpu(cpu_id) ++ last_cpu_id = cpu_id; ++ ioc->cpu_msix_table_sz = last_cpu_id + 1; ++ ioc->cpu_msix_table = kzalloc(ioc->cpu_msix_table_sz, GFP_KERNEL); ++ ioc->reply_queue_count = 1; ++ if (!ioc->cpu_msix_table) { ++ dfailprintk(ioc, pr_info(MPT3SAS_FMT ++ "allocation for cpu_msix_table failed!!!\n", ++ ioc->name)); ++ r = -ENOMEM; ++ goto out_free_resources; ++ } ++ ++ if (ioc->is_warpdrive) { ++ ioc->reply_post_host_index = kcalloc(ioc->cpu_msix_table_sz, ++ sizeof(resource_size_t *), GFP_KERNEL); ++ if (!ioc->reply_post_host_index) { ++ dfailprintk(ioc, pr_info(MPT3SAS_FMT "allocation " ++ "for cpu_msix_table failed!!!\n", ioc->name)); ++ r = -ENOMEM; ++ goto out_free_resources; ++ } ++ } ++ ++ ioc->rdpq_array_enable_assigned = 0; ++ ioc->dma_mask = 0; ++ r = mpt2sas_base_map_resources(ioc); ++ if (r) ++ goto out_free_resources; ++ ++ if (ioc->is_warpdrive) { ++ ioc->reply_post_host_index[0] = (resource_size_t __iomem *) ++ &ioc->chip->ReplyPostHostIndex; ++ ++ for (i = 1; i < ioc->cpu_msix_table_sz; i++) ++ ioc->reply_post_host_index[i] = ++ (resource_size_t __iomem *) ++ ((u8 __iomem *)&ioc->chip->Doorbell + (0x4000 + ((i - 1) ++ * 4))); ++ } ++ ++ pci_set_drvdata(ioc->pdev, ioc->shost); ++ r = _base_get_ioc_facts(ioc, CAN_SLEEP); ++ if (r) ++ goto out_free_resources; ++ ++ switch (ioc->hba_mpi_version_belonged) { ++ case MPI2_VERSION: ++ ioc->build_sg_scmd = &_base_build_sg_scmd; ++ ioc->build_sg = &_base_build_sg; ++ ioc->build_zero_len_sge = &_base_build_zero_len_sge; ++ break; ++ case MPI25_VERSION: ++ case MPI26_VERSION: ++ /* ++ * In SAS3.0, ++ * SCSI_IO, SMP_PASSTHRU, SATA_PASSTHRU, Target Assist, and ++ * Target Status - all require the IEEE formated scatter gather ++ * elements. ++ */ ++ ioc->build_sg_scmd = &_base_build_sg_scmd_ieee; ++ ioc->build_sg = &_base_build_sg_ieee; ++ ioc->build_zero_len_sge = &_base_build_zero_len_sge_ieee; ++ ioc->sge_size_ieee = sizeof(Mpi2IeeeSgeSimple64_t); ++ break; ++ } ++ ++ /* ++ * These function pointers for other requests that don't ++ * the require IEEE scatter gather elements. ++ * ++ * For example Configuration Pages and SAS IOUNIT Control don't. ++ */ ++ ioc->build_sg_mpi = &_base_build_sg; ++ ioc->build_zero_len_sge_mpi = &_base_build_zero_len_sge; ++ ++ r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); ++ if (r) ++ goto out_free_resources; ++ ++ ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts, ++ sizeof(struct mpt2sas_port_facts), GFP_KERNEL); ++ if (!ioc->pfacts) { ++ r = -ENOMEM; ++ goto out_free_resources; ++ } ++ ++ for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) { ++ r = _base_get_port_facts(ioc, i, CAN_SLEEP); ++ if (r) ++ goto out_free_resources; ++ } ++ ++ r = _base_allocate_memory_pools(ioc, CAN_SLEEP); ++ if (r) ++ goto out_free_resources; ++ ++ init_waitqueue_head(&ioc->reset_wq); ++ ++ /* allocate memory pd handle bitmask list */ ++ ioc->pd_handles_sz = (ioc->facts.MaxDevHandle / 8); ++ if (ioc->facts.MaxDevHandle % 8) ++ ioc->pd_handles_sz++; ++ ioc->pd_handles = kzalloc(ioc->pd_handles_sz, ++ GFP_KERNEL); ++ if (!ioc->pd_handles) { ++ r = -ENOMEM; ++ goto out_free_resources; ++ } ++ ioc->blocking_handles = kzalloc(ioc->pd_handles_sz, ++ GFP_KERNEL); ++ if (!ioc->blocking_handles) { ++ r = -ENOMEM; ++ goto out_free_resources; ++ } ++ ++ ioc->fwfault_debug = mpt2sas_fwfault_debug; ++ ++ /* base internal command bits */ ++ mutex_init(&ioc->base_cmds.mutex); ++ ioc->base_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ ++ /* port_enable command bits */ ++ ioc->port_enable_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ++ ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED; ++ ++ /* transport internal command bits */ ++ ioc->transport_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ++ ioc->transport_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_init(&ioc->transport_cmds.mutex); ++ ++ /* scsih internal command bits */ ++ ioc->scsih_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ++ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_init(&ioc->scsih_cmds.mutex); ++ ++ /* task management internal command bits */ ++ ioc->tm_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ++ ioc->tm_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_init(&ioc->tm_cmds.mutex); ++ ++ /* config page internal command bits */ ++ ioc->config_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ++ ioc->config_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_init(&ioc->config_cmds.mutex); ++ ++ /* ctl module internal command bits */ ++ ioc->ctl_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ++ ioc->ctl_cmds.sense = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL); ++ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_init(&ioc->ctl_cmds.mutex); ++ ++ if (!ioc->base_cmds.reply || !ioc->transport_cmds.reply || ++ !ioc->scsih_cmds.reply || !ioc->tm_cmds.reply || ++ !ioc->config_cmds.reply || !ioc->ctl_cmds.reply || ++ !ioc->ctl_cmds.sense) { ++ r = -ENOMEM; ++ goto out_free_resources; ++ } ++ ++ for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) ++ ioc->event_masks[i] = -1; ++ ++ /* here we enable the events we care about */ ++ _base_unmask_events(ioc, MPI2_EVENT_SAS_DISCOVERY); ++ _base_unmask_events(ioc, MPI2_EVENT_SAS_BROADCAST_PRIMITIVE); ++ _base_unmask_events(ioc, MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST); ++ _base_unmask_events(ioc, MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE); ++ _base_unmask_events(ioc, MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE); ++ _base_unmask_events(ioc, MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST); ++ _base_unmask_events(ioc, MPI2_EVENT_IR_VOLUME); ++ _base_unmask_events(ioc, MPI2_EVENT_IR_PHYSICAL_DISK); ++ _base_unmask_events(ioc, MPI2_EVENT_IR_OPERATION_STATUS); ++ _base_unmask_events(ioc, MPI2_EVENT_LOG_ENTRY_ADDED); ++ _base_unmask_events(ioc, MPI2_EVENT_TEMP_THRESHOLD); ++ if (ioc->hba_mpi_version_belonged == MPI26_VERSION) ++ _base_unmask_events(ioc, MPI2_EVENT_ACTIVE_CABLE_EXCEPTION); ++ ++ r = _base_make_ioc_operational(ioc, CAN_SLEEP); ++ if (r) ++ goto out_free_resources; ++ ++ ioc->non_operational_loop = 0; ++ return 0; ++ ++ out_free_resources: ++ ++ ioc->remove_host = 1; ++ ++ mpt2sas_base_free_resources(ioc); ++ _base_release_memory_pools(ioc); ++ pci_set_drvdata(ioc->pdev, NULL); ++ kfree(ioc->cpu_msix_table); ++ if (ioc->is_warpdrive) ++ kfree(ioc->reply_post_host_index); ++ kfree(ioc->pd_handles); ++ kfree(ioc->blocking_handles); ++ kfree(ioc->tm_cmds.reply); ++ kfree(ioc->transport_cmds.reply); ++ kfree(ioc->scsih_cmds.reply); ++ kfree(ioc->config_cmds.reply); ++ kfree(ioc->base_cmds.reply); ++ kfree(ioc->port_enable_cmds.reply); ++ kfree(ioc->ctl_cmds.reply); ++ kfree(ioc->ctl_cmds.sense); ++ kfree(ioc->pfacts); ++ ioc->ctl_cmds.reply = NULL; ++ ioc->base_cmds.reply = NULL; ++ ioc->tm_cmds.reply = NULL; ++ ioc->scsih_cmds.reply = NULL; ++ ioc->transport_cmds.reply = NULL; ++ ioc->config_cmds.reply = NULL; ++ ioc->pfacts = NULL; ++ return r; ++} ++ ++ ++/** ++ * mpt2sas_base_detach - remove controller instance ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_detach(struct MPT3SAS_ADAPTER *ioc) ++{ ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ mpt2sas_base_stop_watchdog(ioc); ++ mpt2sas_base_free_resources(ioc); ++ _base_release_memory_pools(ioc); ++ pci_set_drvdata(ioc->pdev, NULL); ++ kfree(ioc->cpu_msix_table); ++ if (ioc->is_warpdrive) ++ kfree(ioc->reply_post_host_index); ++ kfree(ioc->pd_handles); ++ kfree(ioc->blocking_handles); ++ kfree(ioc->pfacts); ++ kfree(ioc->ctl_cmds.reply); ++ kfree(ioc->ctl_cmds.sense); ++ kfree(ioc->base_cmds.reply); ++ kfree(ioc->port_enable_cmds.reply); ++ kfree(ioc->tm_cmds.reply); ++ kfree(ioc->transport_cmds.reply); ++ kfree(ioc->scsih_cmds.reply); ++ kfree(ioc->config_cmds.reply); ++} ++ ++/** ++ * _base_reset_handler - reset callback handler (for base) ++ * @ioc: per adapter object ++ * @reset_phase: phase ++ * ++ * The handler for doing any required cleanup or initialization. ++ * ++ * The reset phase can be MPT3_IOC_PRE_RESET, MPT3_IOC_AFTER_RESET, ++ * MPT3_IOC_DONE_RESET ++ * ++ * Return nothing. ++ */ ++static void ++_base_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase) ++{ ++ mpt2sas_scsih_reset_handler(ioc, reset_phase); ++ mpt2sas_ctl_reset_handler(ioc, reset_phase); ++ switch (reset_phase) { ++ case MPT3_IOC_PRE_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_PRE_RESET\n", ioc->name, __func__)); ++ break; ++ case MPT3_IOC_AFTER_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_AFTER_RESET\n", ioc->name, __func__)); ++ if (ioc->transport_cmds.status & MPT3_CMD_PENDING) { ++ ioc->transport_cmds.status |= MPT3_CMD_RESET; ++ mpt2sas_base_free_smid(ioc, ioc->transport_cmds.smid); ++ complete(&ioc->transport_cmds.done); ++ } ++ if (ioc->base_cmds.status & MPT3_CMD_PENDING) { ++ ioc->base_cmds.status |= MPT3_CMD_RESET; ++ mpt2sas_base_free_smid(ioc, ioc->base_cmds.smid); ++ complete(&ioc->base_cmds.done); ++ } ++ if (ioc->port_enable_cmds.status & MPT3_CMD_PENDING) { ++ ioc->port_enable_failed = 1; ++ ioc->port_enable_cmds.status |= MPT3_CMD_RESET; ++ mpt2sas_base_free_smid(ioc, ioc->port_enable_cmds.smid); ++ if (ioc->is_driver_loading) { ++ ioc->start_scan_failed = ++ MPI2_IOCSTATUS_INTERNAL_ERROR; ++ ioc->start_scan = 0; ++ ioc->port_enable_cmds.status = ++ MPT3_CMD_NOT_USED; ++ } else ++ complete(&ioc->port_enable_cmds.done); ++ } ++ if (ioc->config_cmds.status & MPT3_CMD_PENDING) { ++ ioc->config_cmds.status |= MPT3_CMD_RESET; ++ mpt2sas_base_free_smid(ioc, ioc->config_cmds.smid); ++ ioc->config_cmds.smid = USHRT_MAX; ++ complete(&ioc->config_cmds.done); ++ } ++ break; ++ case MPT3_IOC_DONE_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_DONE_RESET\n", ioc->name, __func__)); ++ break; ++ } ++} ++ ++/** ++ * _wait_for_commands_to_complete - reset controller ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * This function waiting(3s) for all pending commands to complete ++ * prior to putting controller in reset. ++ */ ++static void ++_wait_for_commands_to_complete(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ u32 ioc_state; ++ unsigned long flags; ++ u16 i; ++ ++ ioc->pending_io_count = 0; ++ if (sleep_flag != CAN_SLEEP) ++ return; ++ ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 0); ++ if ((ioc_state & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL) ++ return; ++ ++ /* pending command count */ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ for (i = 0; i < ioc->scsiio_depth; i++) ++ if (ioc->scsi_lookup[i].cb_idx != 0xFF) ++ ioc->pending_io_count++; ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ ++ if (!ioc->pending_io_count) ++ return; ++ ++ /* wait for pending commands to complete */ ++ wait_event_timeout(ioc->reset_wq, ioc->pending_io_count == 0, 10 * HZ); ++} ++ ++/** ++ * mpt2sas_base_hard_reset_handler - reset controller ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * @type: FORCE_BIG_HAMMER or SOFT_RESET ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, ++ enum reset_type type) ++{ ++ int r; ++ unsigned long flags; ++ u32 ioc_state; ++ u8 is_fault = 0, is_trigger = 0; ++ ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name, ++ __func__)); ++ ++ if (ioc->pci_error_recovery) { ++ pr_err(MPT3SAS_FMT "%s: pci error recovery reset\n", ++ ioc->name, __func__); ++ r = 0; ++ goto out_unlocked; ++ } ++ ++ if (mpt2sas_fwfault_debug) ++ mpt2sas_halt_firmware(ioc); ++ ++ /* TODO - What we really should be doing is pulling ++ * out all the code associated with NO_SLEEP; its never used. ++ * That is legacy code from mpt fusion driver, ported over. ++ * I will leave this BUG_ON here for now till its been resolved. ++ */ ++ BUG_ON(sleep_flag == NO_SLEEP); ++ ++ /* wait for an active reset in progress to complete */ ++ if (!mutex_trylock(&ioc->reset_in_progress_mutex)) { ++ do { ++ ssleep(1); ++ } while (ioc->shost_recovery == 1); ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++ return ioc->ioc_reset_in_progress_status; ++ } ++ ++ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ++ ioc->shost_recovery = 1; ++ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); ++ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) && ++ (!(ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED))) { ++ is_trigger = 1; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 0); ++ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) ++ is_fault = 1; ++ } ++ _base_reset_handler(ioc, MPT3_IOC_PRE_RESET); ++ _wait_for_commands_to_complete(ioc, sleep_flag); ++ _base_mask_interrupts(ioc); ++ r = _base_make_ioc_ready(ioc, sleep_flag, type); ++ if (r) ++ goto out; ++ _base_reset_handler(ioc, MPT3_IOC_AFTER_RESET); ++ ++ /* If this hard reset is called while port enable is active, then ++ * there is no reason to call make_ioc_operational ++ */ ++ if (ioc->is_driver_loading && ioc->port_enable_failed) { ++ ioc->remove_host = 1; ++ r = -EFAULT; ++ goto out; ++ } ++ r = _base_get_ioc_facts(ioc, CAN_SLEEP); ++ if (r) ++ goto out; ++ ++ if (ioc->rdpq_array_enable && !ioc->rdpq_array_capable) ++ panic("%s: Issue occurred with flashing controller firmware." ++ "Please reboot the system and ensure that the correct" ++ " firmware version is running\n", ioc->name); ++ ++ r = _base_make_ioc_operational(ioc, sleep_flag); ++ if (!r) ++ _base_reset_handler(ioc, MPT3_IOC_DONE_RESET); ++ ++ out: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT "%s: %s\n", ++ ioc->name, __func__, ((r == 0) ? "SUCCESS" : "FAILED"))); ++ ++ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ++ ioc->ioc_reset_in_progress_status = r; ++ ioc->shost_recovery = 0; ++ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); ++ ioc->ioc_reset_count++; ++ mutex_unlock(&ioc->reset_in_progress_mutex); ++ ++ out_unlocked: ++ if ((r == 0) && is_trigger) { ++ if (is_fault) ++ mpt2sas_trigger_master(ioc, MASTER_TRIGGER_FW_FAULT); ++ else ++ mpt2sas_trigger_master(ioc, ++ MASTER_TRIGGER_ADAPTER_RESET); ++ } ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++ return r; ++} +diff --git a/drivers/scsi/mpt2sas/mpt3sas_base.h b/drivers/scsi/mpt2sas/mpt3sas_base.h +new file mode 100644 +index 0000000..a580770 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_base.h +@@ -0,0 +1,1462 @@ ++/* ++ * This is the Fusion MPT base driver providing common API layer interface ++ * for access to MPT (Message Passing Technology) firmware. ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.h ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#ifndef MPT3SAS_BASE_H_INCLUDED ++#define MPT3SAS_BASE_H_INCLUDED ++ ++#include "mpi/mpi2_type.h" ++#include "mpi/mpi2.h" ++#include "mpi/mpi2_ioc.h" ++#include "mpi/mpi2_cnfg.h" ++#include "mpi/mpi2_init.h" ++#include "mpi/mpi2_raid.h" ++#include "mpi/mpi2_tool.h" ++#include "mpi/mpi2_sas.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mpt3sas_debug.h" ++#include "mpt3sas_trigger_diag.h" ++ ++/* driver versioning info */ ++#define MPT3SAS_DRIVER_NAME "mpt3sas" ++#define MPT3SAS_AUTHOR "Avago Technologies " ++#define MPT3SAS_DESCRIPTION "LSI MPT Fusion SAS 3.0 Device Driver" ++#define MPT3SAS_DRIVER_VERSION "13.100.00.00" ++#define MPT3SAS_MAJOR_VERSION 13 ++#define MPT3SAS_MINOR_VERSION 100 ++#define MPT3SAS_BUILD_VERSION 0 ++#define MPT3SAS_RELEASE_VERSION 00 ++ ++#define MPT2SAS_DRIVER_NAME "mpt2sas" ++#define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver" ++#define MPT2SAS_DRIVER_VERSION "20.102.00.00" ++#define MPT2SAS_MAJOR_VERSION 20 ++#define MPT2SAS_MINOR_VERSION 102 ++#define MPT2SAS_BUILD_VERSION 0 ++#define MPT2SAS_RELEASE_VERSION 00 ++ ++/* ++ * Set MPT3SAS_SG_DEPTH value based on user input. ++ */ ++#define MPT_MAX_PHYS_SEGMENTS SCSI_MAX_SG_SEGMENTS ++#define MPT_MIN_PHYS_SEGMENTS 16 ++ ++#ifdef CONFIG_SCSI_MPT3SAS_MAX_SGE ++#define MPT3SAS_SG_DEPTH CONFIG_SCSI_MPT3SAS_MAX_SGE ++#else ++#define MPT3SAS_SG_DEPTH MPT_MAX_PHYS_SEGMENTS ++#endif ++ ++#ifdef CONFIG_SCSI_MPT2SAS_MAX_SGE ++#define MPT2SAS_SG_DEPTH CONFIG_SCSI_MPT2SAS_MAX_SGE ++#else ++#define MPT2SAS_SG_DEPTH MPT_MAX_PHYS_SEGMENTS ++#endif ++ ++/* ++ * Generic Defines ++ */ ++#define MPT3SAS_SATA_QUEUE_DEPTH 32 ++#define MPT3SAS_SAS_QUEUE_DEPTH 254 ++#define MPT3SAS_RAID_QUEUE_DEPTH 128 ++ ++#define MPT3SAS_RAID_MAX_SECTORS 8192 ++ ++#define MPT_NAME_LENGTH 32 /* generic length of strings */ ++#define MPT_STRING_LENGTH 64 ++ ++#define MPT_MAX_CALLBACKS 32 ++ ++ ++#define CAN_SLEEP 1 ++#define NO_SLEEP 0 ++ ++#define INTERNAL_CMDS_COUNT 10 /* reserved cmds */ ++/* reserved for issuing internally framed scsi io cmds */ ++#define INTERNAL_SCSIIO_CMDS_COUNT 3 ++ ++#define MPI3_HIM_MASK 0xFFFFFFFF /* mask every bit*/ ++ ++#define MPT3SAS_INVALID_DEVICE_HANDLE 0xFFFF ++ ++#define MAX_CHAIN_ELEMT_SZ 16 ++#define DEFAULT_NUM_FWCHAIN_ELEMTS 8 ++ ++/* ++ * reset phases ++ */ ++#define MPT3_IOC_PRE_RESET 1 /* prior to host reset */ ++#define MPT3_IOC_AFTER_RESET 2 /* just after host reset */ ++#define MPT3_IOC_DONE_RESET 3 /* links re-initialized */ ++ ++/* ++ * logging format ++ */ ++#define MPT3SAS_FMT "%s: " ++ ++/* ++ * WarpDrive Specific Log codes ++ */ ++ ++#define MPT2_WARPDRIVE_LOGENTRY (0x8002) ++#define MPT2_WARPDRIVE_LC_SSDT (0x41) ++#define MPT2_WARPDRIVE_LC_SSDLW (0x43) ++#define MPT2_WARPDRIVE_LC_SSDLF (0x44) ++#define MPT2_WARPDRIVE_LC_BRMF (0x4D) ++ ++/* ++ * per target private data ++ */ ++#define MPT_TARGET_FLAGS_RAID_COMPONENT 0x01 ++#define MPT_TARGET_FLAGS_VOLUME 0x02 ++#define MPT_TARGET_FLAGS_DELETED 0x04 ++#define MPT_TARGET_FASTPATH_IO 0x08 ++ ++#define SAS2_PCI_DEVICE_B0_REVISION (0x01) ++#define SAS3_PCI_DEVICE_C0_REVISION (0x02) ++ ++/* ++ * Intel HBA branding ++ */ ++#define MPT2SAS_INTEL_RMS25JB080_BRANDING \ ++ "Intel(R) Integrated RAID Module RMS25JB080" ++#define MPT2SAS_INTEL_RMS25JB040_BRANDING \ ++ "Intel(R) Integrated RAID Module RMS25JB040" ++#define MPT2SAS_INTEL_RMS25KB080_BRANDING \ ++ "Intel(R) Integrated RAID Module RMS25KB080" ++#define MPT2SAS_INTEL_RMS25KB040_BRANDING \ ++ "Intel(R) Integrated RAID Module RMS25KB040" ++#define MPT2SAS_INTEL_RMS25LB040_BRANDING \ ++ "Intel(R) Integrated RAID Module RMS25LB040" ++#define MPT2SAS_INTEL_RMS25LB080_BRANDING \ ++ "Intel(R) Integrated RAID Module RMS25LB080" ++#define MPT2SAS_INTEL_RMS2LL080_BRANDING \ ++ "Intel Integrated RAID Module RMS2LL080" ++#define MPT2SAS_INTEL_RMS2LL040_BRANDING \ ++ "Intel Integrated RAID Module RMS2LL040" ++#define MPT2SAS_INTEL_RS25GB008_BRANDING \ ++ "Intel(R) RAID Controller RS25GB008" ++#define MPT2SAS_INTEL_SSD910_BRANDING \ ++ "Intel(R) SSD 910 Series" ++ ++#define MPT3SAS_INTEL_RMS3JC080_BRANDING \ ++ "Intel(R) Integrated RAID Module RMS3JC080" ++#define MPT3SAS_INTEL_RS3GC008_BRANDING \ ++ "Intel(R) RAID Controller RS3GC008" ++#define MPT3SAS_INTEL_RS3FC044_BRANDING \ ++ "Intel(R) RAID Controller RS3FC044" ++#define MPT3SAS_INTEL_RS3UC080_BRANDING \ ++ "Intel(R) RAID Controller RS3UC080" ++ ++/* ++ * Intel HBA SSDIDs ++ */ ++#define MPT2SAS_INTEL_RMS25JB080_SSDID 0x3516 ++#define MPT2SAS_INTEL_RMS25JB040_SSDID 0x3517 ++#define MPT2SAS_INTEL_RMS25KB080_SSDID 0x3518 ++#define MPT2SAS_INTEL_RMS25KB040_SSDID 0x3519 ++#define MPT2SAS_INTEL_RMS25LB040_SSDID 0x351A ++#define MPT2SAS_INTEL_RMS25LB080_SSDID 0x351B ++#define MPT2SAS_INTEL_RMS2LL080_SSDID 0x350E ++#define MPT2SAS_INTEL_RMS2LL040_SSDID 0x350F ++#define MPT2SAS_INTEL_RS25GB008_SSDID 0x3000 ++#define MPT2SAS_INTEL_SSD910_SSDID 0x3700 ++ ++#define MPT3SAS_INTEL_RMS3JC080_SSDID 0x3521 ++#define MPT3SAS_INTEL_RS3GC008_SSDID 0x3522 ++#define MPT3SAS_INTEL_RS3FC044_SSDID 0x3523 ++#define MPT3SAS_INTEL_RS3UC080_SSDID 0x3524 ++ ++/* ++ * Dell HBA branding ++ */ ++#define MPT2SAS_DELL_BRANDING_SIZE 32 ++ ++#define MPT2SAS_DELL_6GBPS_SAS_HBA_BRANDING "Dell 6Gbps SAS HBA" ++#define MPT2SAS_DELL_PERC_H200_ADAPTER_BRANDING "Dell PERC H200 Adapter" ++#define MPT2SAS_DELL_PERC_H200_INTEGRATED_BRANDING "Dell PERC H200 Integrated" ++#define MPT2SAS_DELL_PERC_H200_MODULAR_BRANDING "Dell PERC H200 Modular" ++#define MPT2SAS_DELL_PERC_H200_EMBEDDED_BRANDING "Dell PERC H200 Embedded" ++#define MPT2SAS_DELL_PERC_H200_BRANDING "Dell PERC H200" ++#define MPT2SAS_DELL_6GBPS_SAS_BRANDING "Dell 6Gbps SAS" ++ ++#define MPT3SAS_DELL_12G_HBA_BRANDING \ ++ "Dell 12Gbps HBA" ++ ++/* ++ * Dell HBA SSDIDs ++ */ ++#define MPT2SAS_DELL_6GBPS_SAS_HBA_SSDID 0x1F1C ++#define MPT2SAS_DELL_PERC_H200_ADAPTER_SSDID 0x1F1D ++#define MPT2SAS_DELL_PERC_H200_INTEGRATED_SSDID 0x1F1E ++#define MPT2SAS_DELL_PERC_H200_MODULAR_SSDID 0x1F1F ++#define MPT2SAS_DELL_PERC_H200_EMBEDDED_SSDID 0x1F20 ++#define MPT2SAS_DELL_PERC_H200_SSDID 0x1F21 ++#define MPT2SAS_DELL_6GBPS_SAS_SSDID 0x1F22 ++ ++#define MPT3SAS_DELL_12G_HBA_SSDID 0x1F46 ++ ++/* ++ * Cisco HBA branding ++ */ ++#define MPT3SAS_CISCO_12G_8E_HBA_BRANDING \ ++ "Cisco 9300-8E 12G SAS HBA" ++#define MPT3SAS_CISCO_12G_8I_HBA_BRANDING \ ++ "Cisco 9300-8i 12G SAS HBA" ++#define MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING \ ++ "Cisco 12G Modular SAS Pass through Controller" ++#define MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_BRANDING \ ++ "UCS C3X60 12G SAS Pass through Controller" ++/* ++ * Cisco HBA SSSDIDs ++ */ ++#define MPT3SAS_CISCO_12G_8E_HBA_SSDID 0x14C ++#define MPT3SAS_CISCO_12G_8I_HBA_SSDID 0x154 ++#define MPT3SAS_CISCO_12G_AVILA_HBA_SSDID 0x155 ++#define MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_SSDID 0x156 ++ ++/* ++ * status bits for ioc->diag_buffer_status ++ */ ++#define MPT3_DIAG_BUFFER_IS_REGISTERED (0x01) ++#define MPT3_DIAG_BUFFER_IS_RELEASED (0x02) ++#define MPT3_DIAG_BUFFER_IS_DIAG_RESET (0x04) ++ ++/* ++ * HP HBA branding ++ */ ++#define MPT2SAS_HP_3PAR_SSVID 0x1590 ++ ++#define MPT2SAS_HP_2_4_INTERNAL_BRANDING \ ++ "HP H220 Host Bus Adapter" ++#define MPT2SAS_HP_2_4_EXTERNAL_BRANDING \ ++ "HP H221 Host Bus Adapter" ++#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING \ ++ "HP H222 Host Bus Adapter" ++#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING \ ++ "HP H220i Host Bus Adapter" ++#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING \ ++ "HP H210i Host Bus Adapter" ++ ++/* ++ * HO HBA SSDIDs ++ */ ++#define MPT2SAS_HP_2_4_INTERNAL_SSDID 0x0041 ++#define MPT2SAS_HP_2_4_EXTERNAL_SSDID 0x0042 ++#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID 0x0043 ++#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID 0x0044 ++#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID 0x0046 ++ ++/* ++ * Combined Reply Queue constants, ++ * There are twelve Supplemental Reply Post Host Index Registers ++ * and each register is at offset 0x10 bytes from the previous one. ++ */ ++#define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT 12 ++#define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET (0x10) ++ ++/* OEM Identifiers */ ++#define MFG10_OEM_ID_INVALID (0x00000000) ++#define MFG10_OEM_ID_DELL (0x00000001) ++#define MFG10_OEM_ID_FSC (0x00000002) ++#define MFG10_OEM_ID_SUN (0x00000003) ++#define MFG10_OEM_ID_IBM (0x00000004) ++ ++/* GENERIC Flags 0*/ ++#define MFG10_GF0_OCE_DISABLED (0x00000001) ++#define MFG10_GF0_R1E_DRIVE_COUNT (0x00000002) ++#define MFG10_GF0_R10_DISPLAY (0x00000004) ++#define MFG10_GF0_SSD_DATA_SCRUB_DISABLE (0x00000008) ++#define MFG10_GF0_SINGLE_DRIVE_R0 (0x00000010) ++ ++#define VIRTUAL_IO_FAILED_RETRY (0x32010081) ++ ++/* OEM Specific Flags will come from OEM specific header files */ ++struct Mpi2ManufacturingPage10_t { ++ MPI2_CONFIG_PAGE_HEADER Header; /* 00h */ ++ U8 OEMIdentifier; /* 04h */ ++ U8 Reserved1; /* 05h */ ++ U16 Reserved2; /* 08h */ ++ U32 Reserved3; /* 0Ch */ ++ U32 GenericFlags0; /* 10h */ ++ U32 GenericFlags1; /* 14h */ ++ U32 Reserved4; /* 18h */ ++ U32 OEMSpecificFlags0; /* 1Ch */ ++ U32 OEMSpecificFlags1; /* 20h */ ++ U32 Reserved5[18]; /* 24h - 60h*/ ++}; ++ ++ ++/* Miscellaneous options */ ++struct Mpi2ManufacturingPage11_t { ++ MPI2_CONFIG_PAGE_HEADER Header; /* 00h */ ++ __le32 Reserved1; /* 04h */ ++ u8 Reserved2; /* 08h */ ++ u8 EEDPTagMode; /* 09h */ ++ u8 Reserved3; /* 0Ah */ ++ u8 Reserved4; /* 0Bh */ ++ __le32 Reserved5[23]; /* 0Ch-60h*/ ++}; ++ ++/** ++ * struct MPT3SAS_TARGET - starget private hostdata ++ * @starget: starget object ++ * @sas_address: target sas address ++ * @raid_device: raid_device pointer to access volume data ++ * @handle: device handle ++ * @num_luns: number luns ++ * @flags: MPT_TARGET_FLAGS_XXX flags ++ * @deleted: target flaged for deletion ++ * @tm_busy: target is busy with TM request. ++ * @sdev: The sas_device associated with this target ++ */ ++struct MPT3SAS_TARGET { ++ struct scsi_target *starget; ++ u64 sas_address; ++ struct _raid_device *raid_device; ++ u16 handle; ++ int num_luns; ++ u32 flags; ++ u8 deleted; ++ u8 tm_busy; ++ struct _sas_device *sdev; ++}; ++ ++ ++/* ++ * per device private data ++ */ ++#define MPT_DEVICE_FLAGS_INIT 0x01 ++#define MPT_DEVICE_TLR_ON 0x02 ++ ++#define MFG_PAGE10_HIDE_SSDS_MASK (0x00000003) ++#define MFG_PAGE10_HIDE_ALL_DISKS (0x00) ++#define MFG_PAGE10_EXPOSE_ALL_DISKS (0x01) ++#define MFG_PAGE10_HIDE_IF_VOL_PRESENT (0x02) ++ ++/** ++ * struct MPT3SAS_DEVICE - sdev private hostdata ++ * @sas_target: starget private hostdata ++ * @lun: lun number ++ * @flags: MPT_DEVICE_XXX flags ++ * @configured_lun: lun is configured ++ * @block: device is in SDEV_BLOCK state ++ * @tlr_snoop_check: flag used in determining whether to disable TLR ++ * @eedp_enable: eedp support enable bit ++ * @eedp_type: 0(type_1), 1(type_2), 2(type_3) ++ * @eedp_block_length: block size ++ */ ++struct MPT3SAS_DEVICE { ++ struct MPT3SAS_TARGET *sas_target; ++ unsigned int lun; ++ u32 flags; ++ u8 configured_lun; ++ u8 block; ++ u8 tlr_snoop_check; ++ u8 ignore_delay_remove; ++}; ++ ++#define MPT3_CMD_NOT_USED 0x8000 /* free */ ++#define MPT3_CMD_COMPLETE 0x0001 /* completed */ ++#define MPT3_CMD_PENDING 0x0002 /* pending */ ++#define MPT3_CMD_REPLY_VALID 0x0004 /* reply is valid */ ++#define MPT3_CMD_RESET 0x0008 /* host reset dropped the command */ ++ ++/** ++ * struct _internal_cmd - internal commands struct ++ * @mutex: mutex ++ * @done: completion ++ * @reply: reply message pointer ++ * @sense: sense data ++ * @status: MPT3_CMD_XXX status ++ * @smid: system message id ++ */ ++struct _internal_cmd { ++ struct mutex mutex; ++ struct completion done; ++ void *reply; ++ void *sense; ++ u16 status; ++ u16 smid; ++}; ++ ++ ++ ++/** ++ * struct _sas_device - attached device information ++ * @list: sas device list ++ * @starget: starget object ++ * @sas_address: device sas address ++ * @device_name: retrieved from the SAS IDENTIFY frame. ++ * @handle: device handle ++ * @sas_address_parent: sas address of parent expander or sas host ++ * @enclosure_handle: enclosure handle ++ * @enclosure_logical_id: enclosure logical identifier ++ * @volume_handle: volume handle (valid when hidden raid member) ++ * @volume_wwid: volume unique identifier ++ * @device_info: bitfield provides detailed info about the device ++ * @id: target id ++ * @channel: target channel ++ * @slot: number number ++ * @phy: phy identifier provided in sas device page 0 ++ * @responding: used in _scsih_sas_device_mark_responding ++ * @fast_path: fast path feature enable bit ++ * @pfa_led_on: flag for PFA LED status ++ * @pend_sas_rphy_add: flag to check if device is in sas_rphy_add() ++ * addition routine. ++ */ ++struct _sas_device { ++ struct list_head list; ++ struct scsi_target *starget; ++ u64 sas_address; ++ u64 device_name; ++ u16 handle; ++ u64 sas_address_parent; ++ u16 enclosure_handle; ++ u64 enclosure_logical_id; ++ u16 volume_handle; ++ u64 volume_wwid; ++ u32 device_info; ++ int id; ++ int channel; ++ u16 slot; ++ u8 phy; ++ u8 responding; ++ u8 fast_path; ++ u8 pfa_led_on; ++ u8 pend_sas_rphy_add; ++ u8 enclosure_level; ++ u8 connector_name[4]; ++ struct kref refcount; ++}; ++ ++static inline void sas_device_get(struct _sas_device *s) ++{ ++ kref_get(&s->refcount); ++} ++ ++static inline void sas_device_free(struct kref *r) ++{ ++ kfree(container_of(r, struct _sas_device, refcount)); ++} ++ ++static inline void sas_device_put(struct _sas_device *s) ++{ ++ kref_put(&s->refcount, sas_device_free); ++} ++ ++/** ++ * struct _raid_device - raid volume link list ++ * @list: sas device list ++ * @starget: starget object ++ * @sdev: scsi device struct (volumes are single lun) ++ * @wwid: unique identifier for the volume ++ * @handle: device handle ++ * @block_size: Block size of the volume ++ * @id: target id ++ * @channel: target channel ++ * @volume_type: the raid level ++ * @device_info: bitfield provides detailed info about the hidden components ++ * @num_pds: number of hidden raid components ++ * @responding: used in _scsih_raid_device_mark_responding ++ * @percent_complete: resync percent complete ++ * @direct_io_enabled: Whether direct io to PDs are allowed or not ++ * @stripe_exponent: X where 2powX is the stripe sz in blocks ++ * @block_exponent: X where 2powX is the block sz in bytes ++ * @max_lba: Maximum number of LBA in the volume ++ * @stripe_sz: Stripe Size of the volume ++ * @device_info: Device info of the volume member disk ++ * @pd_handle: Array of handles of the physical drives for direct I/O in le16 ++ */ ++#define MPT_MAX_WARPDRIVE_PDS 8 ++struct _raid_device { ++ struct list_head list; ++ struct scsi_target *starget; ++ struct scsi_device *sdev; ++ u64 wwid; ++ u16 handle; ++ u16 block_sz; ++ int id; ++ int channel; ++ u8 volume_type; ++ u8 num_pds; ++ u8 responding; ++ u8 percent_complete; ++ u8 direct_io_enabled; ++ u8 stripe_exponent; ++ u8 block_exponent; ++ u64 max_lba; ++ u32 stripe_sz; ++ u32 device_info; ++ u16 pd_handle[MPT_MAX_WARPDRIVE_PDS]; ++}; ++ ++/** ++ * struct _boot_device - boot device info ++ * @is_raid: flag to indicate whether this is volume ++ * @device: holds pointer for either struct _sas_device or ++ * struct _raid_device ++ */ ++struct _boot_device { ++ u8 is_raid; ++ void *device; ++}; ++ ++/** ++ * struct _sas_port - wide/narrow sas port information ++ * @port_list: list of ports belonging to expander ++ * @num_phys: number of phys belonging to this port ++ * @remote_identify: attached device identification ++ * @rphy: sas transport rphy object ++ * @port: sas transport wide/narrow port object ++ * @phy_list: _sas_phy list objects belonging to this port ++ */ ++struct _sas_port { ++ struct list_head port_list; ++ u8 num_phys; ++ struct sas_identify remote_identify; ++ struct sas_rphy *rphy; ++ struct sas_port *port; ++ struct list_head phy_list; ++}; ++ ++/** ++ * struct _sas_phy - phy information ++ * @port_siblings: list of phys belonging to a port ++ * @identify: phy identification ++ * @remote_identify: attached device identification ++ * @phy: sas transport phy object ++ * @phy_id: unique phy id ++ * @handle: device handle for this phy ++ * @attached_handle: device handle for attached device ++ * @phy_belongs_to_port: port has been created for this phy ++ */ ++struct _sas_phy { ++ struct list_head port_siblings; ++ struct sas_identify identify; ++ struct sas_identify remote_identify; ++ struct sas_phy *phy; ++ u8 phy_id; ++ u16 handle; ++ u16 attached_handle; ++ u8 phy_belongs_to_port; ++}; ++ ++/** ++ * struct _sas_node - sas_host/expander information ++ * @list: list of expanders ++ * @parent_dev: parent device class ++ * @num_phys: number phys belonging to this sas_host/expander ++ * @sas_address: sas address of this sas_host/expander ++ * @handle: handle for this sas_host/expander ++ * @sas_address_parent: sas address of parent expander or sas host ++ * @enclosure_handle: handle for this a member of an enclosure ++ * @device_info: bitwise defining capabilities of this sas_host/expander ++ * @responding: used in _scsih_expander_device_mark_responding ++ * @phy: a list of phys that make up this sas_host/expander ++ * @sas_port_list: list of ports attached to this sas_host/expander ++ */ ++struct _sas_node { ++ struct list_head list; ++ struct device *parent_dev; ++ u8 num_phys; ++ u64 sas_address; ++ u16 handle; ++ u64 sas_address_parent; ++ u16 enclosure_handle; ++ u64 enclosure_logical_id; ++ u8 responding; ++ struct _sas_phy *phy; ++ struct list_head sas_port_list; ++}; ++ ++/** ++ * enum reset_type - reset state ++ * @FORCE_BIG_HAMMER: issue diagnostic reset ++ * @SOFT_RESET: issue message_unit_reset, if fails to to big hammer ++ */ ++enum reset_type { ++ FORCE_BIG_HAMMER, ++ SOFT_RESET, ++}; ++ ++/** ++ * struct chain_tracker - firmware chain tracker ++ * @chain_buffer: chain buffer ++ * @chain_buffer_dma: physical address ++ * @tracker_list: list of free request (ioc->free_chain_list) ++ */ ++struct chain_tracker { ++ void *chain_buffer; ++ dma_addr_t chain_buffer_dma; ++ struct list_head tracker_list; ++}; ++ ++/** ++ * struct scsiio_tracker - scsi mf request tracker ++ * @smid: system message id ++ * @scmd: scsi request pointer ++ * @cb_idx: callback index ++ * @direct_io: To indicate whether I/O is direct (WARPDRIVE) ++ * @tracker_list: list of free request (ioc->free_list) ++ * @msix_io: IO's msix ++ */ ++struct scsiio_tracker { ++ u16 smid; ++ struct scsi_cmnd *scmd; ++ u8 cb_idx; ++ u8 direct_io; ++ struct list_head chain_list; ++ struct list_head tracker_list; ++ u16 msix_io; ++}; ++ ++/** ++ * struct request_tracker - firmware request tracker ++ * @smid: system message id ++ * @cb_idx: callback index ++ * @tracker_list: list of free request (ioc->free_list) ++ */ ++struct request_tracker { ++ u16 smid; ++ u8 cb_idx; ++ struct list_head tracker_list; ++}; ++ ++/** ++ * struct _tr_list - target reset list ++ * @handle: device handle ++ * @state: state machine ++ */ ++struct _tr_list { ++ struct list_head list; ++ u16 handle; ++ u16 state; ++}; ++ ++/** ++ * struct _sc_list - delayed SAS_IO_UNIT_CONTROL message list ++ * @handle: device handle ++ */ ++struct _sc_list { ++ struct list_head list; ++ u16 handle; ++}; ++ ++/** ++ * struct _event_ack_list - delayed event acknowledgment list ++ * @Event: Event ID ++ * @EventContext: used to track the event uniquely ++ */ ++struct _event_ack_list { ++ struct list_head list; ++ u16 Event; ++ u32 EventContext; ++}; ++ ++/** ++ * struct adapter_reply_queue - the reply queue struct ++ * @ioc: per adapter object ++ * @msix_index: msix index into vector table ++ * @vector: irq vector ++ * @reply_post_host_index: head index in the pool where FW completes IO ++ * @reply_post_free: reply post base virt address ++ * @name: the name registered to request_irq() ++ * @busy: isr is actively processing replies on another cpu ++ * @list: this list ++*/ ++struct adapter_reply_queue { ++ struct MPT3SAS_ADAPTER *ioc; ++ u8 msix_index; ++ unsigned int vector; ++ u32 reply_post_host_index; ++ Mpi2ReplyDescriptorsUnion_t *reply_post_free; ++ char name[MPT_NAME_LENGTH]; ++ atomic_t busy; ++ cpumask_var_t affinity_hint; ++ struct list_head list; ++}; ++ ++typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); ++ ++/* SAS3.0 support */ ++typedef int (*MPT_BUILD_SG_SCMD)(struct MPT3SAS_ADAPTER *ioc, ++ struct scsi_cmnd *scmd, u16 smid); ++typedef void (*MPT_BUILD_SG)(struct MPT3SAS_ADAPTER *ioc, void *psge, ++ dma_addr_t data_out_dma, size_t data_out_sz, ++ dma_addr_t data_in_dma, size_t data_in_sz); ++typedef void (*MPT_BUILD_ZERO_LEN_SGE)(struct MPT3SAS_ADAPTER *ioc, ++ void *paddr); ++ ++ ++ ++/* IOC Facts and Port Facts converted from little endian to cpu */ ++union mpi3_version_union { ++ MPI2_VERSION_STRUCT Struct; ++ u32 Word; ++}; ++ ++struct mpt2sas_facts { ++ u16 MsgVersion; ++ u16 HeaderVersion; ++ u8 IOCNumber; ++ u8 VP_ID; ++ u8 VF_ID; ++ u16 IOCExceptions; ++ u16 IOCStatus; ++ u32 IOCLogInfo; ++ u8 MaxChainDepth; ++ u8 WhoInit; ++ u8 NumberOfPorts; ++ u8 MaxMSIxVectors; ++ u16 RequestCredit; ++ u16 ProductID; ++ u32 IOCCapabilities; ++ union mpi3_version_union FWVersion; ++ u16 IOCRequestFrameSize; ++ u16 IOCMaxChainSegmentSize; ++ u16 MaxInitiators; ++ u16 MaxTargets; ++ u16 MaxSasExpanders; ++ u16 MaxEnclosures; ++ u16 ProtocolFlags; ++ u16 HighPriorityCredit; ++ u16 MaxReplyDescriptorPostQueueDepth; ++ u8 ReplyFrameSize; ++ u8 MaxVolumes; ++ u16 MaxDevHandle; ++ u16 MaxPersistentEntries; ++ u16 MinDevHandle; ++}; ++ ++struct mpt2sas_port_facts { ++ u8 PortNumber; ++ u8 VP_ID; ++ u8 VF_ID; ++ u8 PortType; ++ u16 MaxPostedCmdBuffers; ++}; ++ ++struct reply_post_struct { ++ Mpi2ReplyDescriptorsUnion_t *reply_post_free; ++ dma_addr_t reply_post_free_dma; ++}; ++ ++/** ++ * enum mutex_type - task management mutex type ++ * @TM_MUTEX_OFF: mutex is not required becuase calling function is acquiring it ++ * @TM_MUTEX_ON: mutex is required ++ */ ++enum mutex_type { ++ TM_MUTEX_OFF = 0, ++ TM_MUTEX_ON = 1, ++}; ++ ++typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc); ++/** ++ * struct MPT3SAS_ADAPTER - per adapter struct ++ * @list: ioc_list ++ * @shost: shost object ++ * @id: unique adapter id ++ * @cpu_count: number online cpus ++ * @name: generic ioc string ++ * @tmp_string: tmp string used for logging ++ * @pdev: pci pdev object ++ * @pio_chip: physical io register space ++ * @chip: memory mapped register space ++ * @chip_phys: physical addrss prior to mapping ++ * @logging_level: see mpt3sas_debug.h ++ * @fwfault_debug: debuging FW timeouts ++ * @ir_firmware: IR firmware present ++ * @bars: bitmask of BAR's that must be configured ++ * @mask_interrupts: ignore interrupt ++ * @dma_mask: used to set the consistent dma mask ++ * @fault_reset_work_q_name: fw fault work queue ++ * @fault_reset_work_q: "" ++ * @fault_reset_work: "" ++ * @firmware_event_name: fw event work queue ++ * @firmware_event_thread: "" ++ * @fw_event_lock: ++ * @fw_event_list: list of fw events ++ * @aen_event_read_flag: event log was read ++ * @broadcast_aen_busy: broadcast aen waiting to be serviced ++ * @shost_recovery: host reset in progress ++ * @ioc_reset_in_progress_lock: ++ * @ioc_link_reset_in_progress: phy/hard reset in progress ++ * @ignore_loginfos: ignore loginfos during task management ++ * @remove_host: flag for when driver unloads, to avoid sending dev resets ++ * @pci_error_recovery: flag to prevent ioc access until slot reset completes ++ * @wait_for_discovery_to_complete: flag set at driver load time when ++ * waiting on reporting devices ++ * @is_driver_loading: flag set at driver load time ++ * @port_enable_failed: flag set when port enable has failed ++ * @start_scan: flag set from scan_start callback, cleared from _mpt2sas_fw_work ++ * @start_scan_failed: means port enable failed, return's the ioc_status ++ * @msix_enable: flag indicating msix is enabled ++ * @msix_vector_count: number msix vectors ++ * @cpu_msix_table: table for mapping cpus to msix index ++ * @cpu_msix_table_sz: table size ++ * @schedule_dead_ioc_flush_running_cmds: callback to flush pending commands ++ * @scsi_io_cb_idx: shost generated commands ++ * @tm_cb_idx: task management commands ++ * @scsih_cb_idx: scsih internal commands ++ * @transport_cb_idx: transport internal commands ++ * @ctl_cb_idx: clt internal commands ++ * @base_cb_idx: base internal commands ++ * @config_cb_idx: base internal commands ++ * @tm_tr_cb_idx : device removal target reset handshake ++ * @tm_tr_volume_cb_idx : volume removal target reset ++ * @base_cmds: ++ * @transport_cmds: ++ * @scsih_cmds: ++ * @tm_cmds: ++ * @ctl_cmds: ++ * @config_cmds: ++ * @base_add_sg_single: handler for either 32/64 bit sgl's ++ * @event_type: bits indicating which events to log ++ * @event_context: unique id for each logged event ++ * @event_log: event log pointer ++ * @event_masks: events that are masked ++ * @facts: static facts data ++ * @pfacts: static port facts data ++ * @manu_pg0: static manufacturing page 0 ++ * @manu_pg10: static manufacturing page 10 ++ * @manu_pg11: static manufacturing page 11 ++ * @bios_pg2: static bios page 2 ++ * @bios_pg3: static bios page 3 ++ * @ioc_pg8: static ioc page 8 ++ * @iounit_pg0: static iounit page 0 ++ * @iounit_pg1: static iounit page 1 ++ * @iounit_pg8: static iounit page 8 ++ * @sas_hba: sas host object ++ * @sas_expander_list: expander object list ++ * @sas_node_lock: ++ * @sas_device_list: sas device object list ++ * @sas_device_init_list: sas device object list (used only at init time) ++ * @sas_device_lock: ++ * @io_missing_delay: time for IO completed by fw when PDR enabled ++ * @device_missing_delay: time for device missing by fw when PDR enabled ++ * @sas_id : used for setting volume target IDs ++ * @blocking_handles: bitmask used to identify which devices need blocking ++ * @pd_handles : bitmask for PD handles ++ * @pd_handles_sz : size of pd_handle bitmask ++ * @config_page_sz: config page size ++ * @config_page: reserve memory for config page payload ++ * @config_page_dma: ++ * @hba_queue_depth: hba request queue depth ++ * @sge_size: sg element size for either 32/64 bit ++ * @scsiio_depth: SCSI_IO queue depth ++ * @request_sz: per request frame size ++ * @request: pool of request frames ++ * @request_dma: ++ * @request_dma_sz: ++ * @scsi_lookup: firmware request tracker list ++ * @scsi_lookup_lock: ++ * @free_list: free list of request ++ * @pending_io_count: ++ * @reset_wq: ++ * @chain: pool of chains ++ * @chain_dma: ++ * @max_sges_in_main_message: number sg elements in main message ++ * @max_sges_in_chain_message: number sg elements per chain ++ * @chains_needed_per_io: max chains per io ++ * @chain_depth: total chains allocated ++ * @chain_segment_sz: gives the max number of ++ * SGEs accommodate on single chain buffer ++ * @hi_priority_smid: ++ * @hi_priority: ++ * @hi_priority_dma: ++ * @hi_priority_depth: ++ * @hpr_lookup: ++ * @hpr_free_list: ++ * @internal_smid: ++ * @internal: ++ * @internal_dma: ++ * @internal_depth: ++ * @internal_lookup: ++ * @internal_free_list: ++ * @sense: pool of sense ++ * @sense_dma: ++ * @sense_dma_pool: ++ * @reply_depth: hba reply queue depth: ++ * @reply_sz: per reply frame size: ++ * @reply: pool of replys: ++ * @reply_dma: ++ * @reply_dma_pool: ++ * @reply_free_queue_depth: reply free depth ++ * @reply_free: pool for reply free queue (32 bit addr) ++ * @reply_free_dma: ++ * @reply_free_dma_pool: ++ * @reply_free_host_index: tail index in pool to insert free replys ++ * @reply_post_queue_depth: reply post queue depth ++ * @reply_post_struct: struct for reply_post_free physical & virt address ++ * @rdpq_array_capable: FW supports multiple reply queue addresses in ioc_init ++ * @rdpq_array_enable: rdpq_array support is enabled in the driver ++ * @rdpq_array_enable_assigned: this ensures that rdpq_array_enable flag ++ * is assigned only ones ++ * @reply_queue_count: number of reply queue's ++ * @reply_queue_list: link list contaning the reply queue info ++ * @msix96_vector: 96 MSI-X vector support ++ * @replyPostRegisterIndex: index of next position in Reply Desc Post Queue ++ * @delayed_tr_list: target reset link list ++ * @delayed_tr_volume_list: volume target reset link list ++ * @delayed_sc_list: ++ * @delayed_event_ack_list: ++ * @temp_sensors_count: flag to carry the number of temperature sensors ++ * @pci_access_mutex: Mutex to synchronize ioctl,sysfs show path and ++ * pci resource handling. PCI resource freeing will lead to free ++ * vital hardware/memory resource, which might be in use by cli/sysfs ++ * path functions resulting in Null pointer reference followed by kernel ++ * crash. To avoid the above race condition we use mutex syncrhonization ++ * which ensures the syncrhonization between cli/sysfs_show path. ++ */ ++struct MPT3SAS_ADAPTER { ++ struct list_head list; ++ struct Scsi_Host *shost; ++ u8 id; ++ int cpu_count; ++ char name[MPT_NAME_LENGTH]; ++ char driver_name[MPT_NAME_LENGTH]; ++ char tmp_string[MPT_STRING_LENGTH]; ++ struct pci_dev *pdev; ++ Mpi2SystemInterfaceRegs_t __iomem *chip; ++ resource_size_t chip_phys; ++ int logging_level; ++ int fwfault_debug; ++ u8 ir_firmware; ++ int bars; ++ u8 mask_interrupts; ++ int dma_mask; ++ ++ /* fw fault handler */ ++ char fault_reset_work_q_name[20]; ++ struct workqueue_struct *fault_reset_work_q; ++ struct delayed_work fault_reset_work; ++ ++ /* fw event handler */ ++ char firmware_event_name[20]; ++ struct workqueue_struct *firmware_event_thread; ++ spinlock_t fw_event_lock; ++ struct list_head fw_event_list; ++ ++ /* misc flags */ ++ int aen_event_read_flag; ++ u8 broadcast_aen_busy; ++ u16 broadcast_aen_pending; ++ u8 shost_recovery; ++ ++ struct mutex reset_in_progress_mutex; ++ spinlock_t ioc_reset_in_progress_lock; ++ u8 ioc_link_reset_in_progress; ++ u8 ioc_reset_in_progress_status; ++ ++ u8 ignore_loginfos; ++ u8 remove_host; ++ u8 pci_error_recovery; ++ u8 wait_for_discovery_to_complete; ++ u8 is_driver_loading; ++ u8 port_enable_failed; ++ u8 start_scan; ++ u16 start_scan_failed; ++ ++ u8 msix_enable; ++ u16 msix_vector_count; ++ u8 *cpu_msix_table; ++ u16 cpu_msix_table_sz; ++ resource_size_t __iomem **reply_post_host_index; ++ u32 ioc_reset_count; ++ MPT3SAS_FLUSH_RUNNING_CMDS schedule_dead_ioc_flush_running_cmds; ++ u32 non_operational_loop; ++ ++ /* internal commands, callback index */ ++ u8 scsi_io_cb_idx; ++ u8 tm_cb_idx; ++ u8 transport_cb_idx; ++ u8 scsih_cb_idx; ++ u8 ctl_cb_idx; ++ u8 base_cb_idx; ++ u8 port_enable_cb_idx; ++ u8 config_cb_idx; ++ u8 tm_tr_cb_idx; ++ u8 tm_tr_volume_cb_idx; ++ u8 tm_sas_control_cb_idx; ++ struct _internal_cmd base_cmds; ++ struct _internal_cmd port_enable_cmds; ++ struct _internal_cmd transport_cmds; ++ struct _internal_cmd scsih_cmds; ++ struct _internal_cmd tm_cmds; ++ struct _internal_cmd ctl_cmds; ++ struct _internal_cmd config_cmds; ++ ++ MPT_ADD_SGE base_add_sg_single; ++ ++ /* function ptr for either IEEE or MPI sg elements */ ++ MPT_BUILD_SG_SCMD build_sg_scmd; ++ MPT_BUILD_SG build_sg; ++ MPT_BUILD_ZERO_LEN_SGE build_zero_len_sge; ++ u16 sge_size_ieee; ++ u16 hba_mpi_version_belonged; ++ ++ /* function ptr for MPI sg elements only */ ++ MPT_BUILD_SG build_sg_mpi; ++ MPT_BUILD_ZERO_LEN_SGE build_zero_len_sge_mpi; ++ ++ /* event log */ ++ u32 event_type[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; ++ u32 event_context; ++ void *event_log; ++ u32 event_masks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; ++ ++ /* static config pages */ ++ struct mpt2sas_facts facts; ++ struct mpt2sas_port_facts *pfacts; ++ Mpi2ManufacturingPage0_t manu_pg0; ++ struct Mpi2ManufacturingPage10_t manu_pg10; ++ struct Mpi2ManufacturingPage11_t manu_pg11; ++ Mpi2BiosPage2_t bios_pg2; ++ Mpi2BiosPage3_t bios_pg3; ++ Mpi2IOCPage8_t ioc_pg8; ++ Mpi2IOUnitPage0_t iounit_pg0; ++ Mpi2IOUnitPage1_t iounit_pg1; ++ Mpi2IOUnitPage8_t iounit_pg8; ++ ++ struct _boot_device req_boot_device; ++ struct _boot_device req_alt_boot_device; ++ struct _boot_device current_boot_device; ++ ++ /* sas hba, expander, and device list */ ++ struct _sas_node sas_hba; ++ struct list_head sas_expander_list; ++ spinlock_t sas_node_lock; ++ struct list_head sas_device_list; ++ struct list_head sas_device_init_list; ++ spinlock_t sas_device_lock; ++ struct list_head raid_device_list; ++ spinlock_t raid_device_lock; ++ u8 io_missing_delay; ++ u16 device_missing_delay; ++ int sas_id; ++ ++ void *blocking_handles; ++ void *pd_handles; ++ u16 pd_handles_sz; ++ ++ /* config page */ ++ u16 config_page_sz; ++ void *config_page; ++ dma_addr_t config_page_dma; ++ ++ /* scsiio request */ ++ u16 hba_queue_depth; ++ u16 sge_size; ++ u16 scsiio_depth; ++ u16 request_sz; ++ u8 *request; ++ dma_addr_t request_dma; ++ u32 request_dma_sz; ++ struct scsiio_tracker *scsi_lookup; ++ ulong scsi_lookup_pages; ++ spinlock_t scsi_lookup_lock; ++ struct list_head free_list; ++ int pending_io_count; ++ wait_queue_head_t reset_wq; ++ ++ /* chain */ ++ struct chain_tracker *chain_lookup; ++ struct list_head free_chain_list; ++ struct dma_pool *chain_dma_pool; ++ ulong chain_pages; ++ u16 max_sges_in_main_message; ++ u16 max_sges_in_chain_message; ++ u16 chains_needed_per_io; ++ u32 chain_depth; ++ u16 chain_segment_sz; ++ ++ /* hi-priority queue */ ++ u16 hi_priority_smid; ++ u8 *hi_priority; ++ dma_addr_t hi_priority_dma; ++ u16 hi_priority_depth; ++ struct request_tracker *hpr_lookup; ++ struct list_head hpr_free_list; ++ ++ /* internal queue */ ++ u16 internal_smid; ++ u8 *internal; ++ dma_addr_t internal_dma; ++ u16 internal_depth; ++ struct request_tracker *internal_lookup; ++ struct list_head internal_free_list; ++ ++ /* sense */ ++ u8 *sense; ++ dma_addr_t sense_dma; ++ struct dma_pool *sense_dma_pool; ++ ++ /* reply */ ++ u16 reply_sz; ++ u8 *reply; ++ dma_addr_t reply_dma; ++ u32 reply_dma_max_address; ++ u32 reply_dma_min_address; ++ struct dma_pool *reply_dma_pool; ++ ++ /* reply free queue */ ++ u16 reply_free_queue_depth; ++ __le32 *reply_free; ++ dma_addr_t reply_free_dma; ++ struct dma_pool *reply_free_dma_pool; ++ u32 reply_free_host_index; ++ ++ /* reply post queue */ ++ u16 reply_post_queue_depth; ++ struct reply_post_struct *reply_post; ++ u8 rdpq_array_capable; ++ u8 rdpq_array_enable; ++ u8 rdpq_array_enable_assigned; ++ struct dma_pool *reply_post_free_dma_pool; ++ u8 reply_queue_count; ++ struct list_head reply_queue_list; ++ ++ u8 msix96_vector; ++ /* reply post register index */ ++ resource_size_t **replyPostRegisterIndex; ++ ++ struct list_head delayed_tr_list; ++ struct list_head delayed_tr_volume_list; ++ struct list_head delayed_sc_list; ++ struct list_head delayed_event_ack_list; ++ u8 temp_sensors_count; ++ struct mutex pci_access_mutex; ++ ++ /* diag buffer support */ ++ u8 *diag_buffer[MPI2_DIAG_BUF_TYPE_COUNT]; ++ u32 diag_buffer_sz[MPI2_DIAG_BUF_TYPE_COUNT]; ++ dma_addr_t diag_buffer_dma[MPI2_DIAG_BUF_TYPE_COUNT]; ++ u8 diag_buffer_status[MPI2_DIAG_BUF_TYPE_COUNT]; ++ u32 unique_id[MPI2_DIAG_BUF_TYPE_COUNT]; ++ u32 product_specific[MPI2_DIAG_BUF_TYPE_COUNT][23]; ++ u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT]; ++ u32 ring_buffer_offset; ++ u32 ring_buffer_sz; ++ u8 is_warpdrive; ++ u8 hide_ir_msg; ++ u8 mfg_pg10_hide_flag; ++ u8 hide_drives; ++ spinlock_t diag_trigger_lock; ++ u8 diag_trigger_active; ++ struct SL_WH_MASTER_TRIGGER_T diag_trigger_master; ++ struct SL_WH_EVENT_TRIGGERS_T diag_trigger_event; ++ struct SL_WH_SCSI_TRIGGERS_T diag_trigger_scsi; ++ struct SL_WH_MPI_TRIGGERS_T diag_trigger_mpi; ++}; ++ ++typedef u8 (*MPT_CALLBACK)(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply); ++ ++ ++/* base shared API */ ++extern struct list_head mpt2sas_ioc_list; ++extern char driver_name[MPT_NAME_LENGTH]; ++/* spinlock on list operations over IOCs ++ * Case: when multiple warpdrive cards(IOCs) are in use ++ * Each IOC will added to the ioc list structure on initialization. ++ * Watchdog threads run at regular intervals to check IOC for any ++ * fault conditions which will trigger the dead_ioc thread to ++ * deallocate pci resource, resulting deleting the IOC netry from list, ++ * this deletion need to protected by spinlock to enusre that ++ * ioc removal is syncrhonized, if not synchronized it might lead to ++ * list_del corruption as the ioc list is traversed in cli path. ++ */ ++extern spinlock_t gioc_lock_mpt2sas; ++ ++void mpt2sas_base_start_watchdog(struct MPT3SAS_ADAPTER *ioc); ++void mpt2sas_base_stop_watchdog(struct MPT3SAS_ADAPTER *ioc); ++ ++int mpt2sas_base_attach(struct MPT3SAS_ADAPTER *ioc); ++void mpt2sas_base_detach(struct MPT3SAS_ADAPTER *ioc); ++int mpt2sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc); ++void mpt2sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc); ++int mpt2sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, ++ enum reset_type type); ++ ++void *mpt2sas_base_get_msg_frame(struct MPT3SAS_ADAPTER *ioc, u16 smid); ++void *mpt2sas_base_get_sense_buffer(struct MPT3SAS_ADAPTER *ioc, u16 smid); ++__le32 mpt2sas_base_get_sense_buffer_dma(struct MPT3SAS_ADAPTER *ioc, ++ u16 smid); ++ ++void mpt2sas_base_sync_reply_irqs(struct MPT3SAS_ADAPTER *ioc); ++ ++/* hi-priority queue */ ++u16 mpt2sas_base_get_smid_hpr(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx); ++u16 mpt2sas_base_get_smid_scsiio(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx, ++ struct scsi_cmnd *scmd); ++ ++u16 mpt2sas_base_get_smid(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx); ++void mpt2sas_base_free_smid(struct MPT3SAS_ADAPTER *ioc, u16 smid); ++void mpt2sas_base_put_smid_scsi_io(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ u16 handle); ++void mpt2sas_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ u16 handle); ++void mpt2sas_base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, ++ u16 smid, u16 msix_task); ++void mpt2sas_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid); ++void mpt2sas_base_initialize_callback_handler(void); ++u8 mpt2sas_base_register_callback_handler(MPT_CALLBACK cb_func); ++void mpt2sas_base_release_callback_handler(u8 cb_idx); ++ ++u8 mpt2sas_base_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply); ++u8 mpt2sas_port_enable_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ u8 msix_index, u32 reply); ++void *mpt2sas_base_get_reply_virt_addr(struct MPT3SAS_ADAPTER *ioc, ++ u32 phys_addr); ++ ++u32 mpt2sas_base_get_iocstate(struct MPT3SAS_ADAPTER *ioc, int cooked); ++ ++void mpt2sas_base_fault_info(struct MPT3SAS_ADAPTER *ioc , u16 fault_code); ++int mpt2sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2SasIoUnitControlReply_t *mpi_reply, ++ Mpi2SasIoUnitControlRequest_t *mpi_request); ++int mpt2sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request); ++ ++void mpt2sas_base_validate_event_type(struct MPT3SAS_ADAPTER *ioc, ++ u32 *event_type); ++ ++void mpt2sas_halt_firmware(struct MPT3SAS_ADAPTER *ioc); ++ ++void mpt2sas_base_update_missing_delay(struct MPT3SAS_ADAPTER *ioc, ++ u16 device_missing_delay, u8 io_missing_delay); ++ ++int mpt2sas_port_enable(struct MPT3SAS_ADAPTER *ioc); ++ ++ ++/* scsih shared API */ ++u8 mpt2sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, ++ u32 reply); ++void mpt2sas_scsih_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase); ++ ++int mpt2sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, ++ uint channel, uint id, uint lun, u8 type, u16 smid_task, ++ ulong timeout, enum mutex_type m_type); ++void mpt2sas_scsih_set_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle); ++void mpt2sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle); ++void mpt2sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address); ++void mpt2sas_device_remove_by_sas_address(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address); ++u8 mpt2sas_check_for_pending_internal_cmds(struct MPT3SAS_ADAPTER *ioc, ++ u16 smid); ++ ++struct _sas_node *mpt2sas_scsih_expander_find_by_handle( ++ struct MPT3SAS_ADAPTER *ioc, u16 handle); ++struct _sas_node *mpt2sas_scsih_expander_find_by_sas_address( ++ struct MPT3SAS_ADAPTER *ioc, u64 sas_address); ++struct _sas_device *mpt2sas_get_sdev_by_addr( ++ struct MPT3SAS_ADAPTER *ioc, u64 sas_address); ++struct _sas_device *__mpt2sas_get_sdev_by_addr( ++ struct MPT3SAS_ADAPTER *ioc, u64 sas_address); ++ ++void mpt2sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc); ++struct _raid_device * ++mpt2sas_raid_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle); ++ ++/* config shared API */ ++u8 mpt2sas_config_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply); ++int mpt2sas_config_get_number_hba_phys(struct MPT3SAS_ADAPTER *ioc, ++ u8 *num_phys); ++int mpt2sas_config_get_manufacturing_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page); ++int mpt2sas_config_get_manufacturing_pg7(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage7_t *config_page, ++ u16 sz); ++int mpt2sas_config_get_manufacturing_pg10(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, ++ struct Mpi2ManufacturingPage10_t *config_page); ++ ++int mpt2sas_config_get_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, ++ struct Mpi2ManufacturingPage11_t *config_page); ++int mpt2sas_config_set_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, ++ struct Mpi2ManufacturingPage11_t *config_page); ++ ++int mpt2sas_config_get_bios_pg2(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2BiosPage2_t *config_page); ++int mpt2sas_config_get_bios_pg3(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2BiosPage3_t *config_page); ++int mpt2sas_config_get_iounit_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2IOUnitPage0_t *config_page); ++int mpt2sas_config_get_sas_device_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage0_t *config_page, ++ u32 form, u32 handle); ++int mpt2sas_config_get_sas_device_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage1_t *config_page, ++ u32 form, u32 handle); ++int mpt2sas_config_get_sas_iounit_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage0_t *config_page, ++ u16 sz); ++int mpt2sas_config_get_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2IOUnitPage1_t *config_page); ++int mpt2sas_config_get_iounit_pg3(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage3_t *config_page, u16 sz); ++int mpt2sas_config_set_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2IOUnitPage1_t *config_page); ++int mpt2sas_config_get_iounit_pg8(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2IOUnitPage8_t *config_page); ++int mpt2sas_config_get_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, ++ u16 sz); ++int mpt2sas_config_set_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, ++ u16 sz); ++int mpt2sas_config_get_ioc_pg8(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2IOCPage8_t *config_page); ++int mpt2sas_config_get_expander_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2ExpanderPage0_t *config_page, ++ u32 form, u32 handle); ++int mpt2sas_config_get_expander_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2ExpanderPage1_t *config_page, ++ u32 phy_number, u16 handle); ++int mpt2sas_config_get_enclosure_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasEnclosurePage0_t *config_page, ++ u32 form, u32 handle); ++int mpt2sas_config_get_phy_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number); ++int mpt2sas_config_get_phy_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number); ++int mpt2sas_config_get_raid_volume_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form, ++ u32 handle); ++int mpt2sas_config_get_number_pds(struct MPT3SAS_ADAPTER *ioc, u16 handle, ++ u8 *num_pds); ++int mpt2sas_config_get_raid_volume_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form, ++ u32 handle, u16 sz); ++int mpt2sas_config_get_phys_disk_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page, ++ u32 form, u32 form_specific); ++int mpt2sas_config_get_volume_handle(struct MPT3SAS_ADAPTER *ioc, u16 pd_handle, ++ u16 *volume_handle); ++int mpt2sas_config_get_volume_wwid(struct MPT3SAS_ADAPTER *ioc, ++ u16 volume_handle, u64 *wwid); ++ ++/* ctl shared API */ ++extern struct device_attribute *mpt2sas_host_attrs[]; ++extern struct device_attribute *mpt2sas_dev_attrs[]; ++void mpt2sas_ctl_init(ushort hbas_to_enumerate); ++void mpt2sas_ctl_exit(ushort hbas_to_enumerate); ++u8 mpt2sas_ctl_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply); ++void mpt2sas_ctl_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase); ++u8 mpt2sas_ctl_event_callback(struct MPT3SAS_ADAPTER *ioc, ++ u8 msix_index, u32 reply); ++void mpt2sas_ctl_add_to_event_log(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventNotificationReply_t *mpi_reply); ++ ++void mpt2sas_enable_diag_buffer(struct MPT3SAS_ADAPTER *ioc, ++ u8 bits_to_regsiter); ++int mpt2sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type, ++ u8 *issue_reset); ++ ++/* transport shared API */ ++extern struct scsi_transport_template *mpt2sas_transport_template; ++u8 mpt2sas_transport_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply); ++struct _sas_port *mpt2sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, ++ u16 handle, u64 sas_address); ++void mpt2sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, ++ u64 sas_address_parent); ++int mpt2sas_transport_add_host_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy ++ *mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev); ++int mpt2sas_transport_add_expander_phy(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_phy *mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, ++ struct device *parent_dev); ++void mpt2sas_transport_update_links(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address, u16 handle, u8 phy_number, u8 link_rate); ++extern struct sas_function_template mpt2sas_transport_functions; ++extern struct scsi_transport_template *mpt2sas_transport_template; ++extern int scsi_internal_device_block(struct scsi_device *sdev); ++extern int scsi_internal_device_unblock(struct scsi_device *sdev, ++ enum scsi_device_state new_state); ++/* trigger data externs */ ++void mpt2sas_send_trigger_data_event(struct MPT3SAS_ADAPTER *ioc, ++ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data); ++void mpt2sas_process_trigger_data(struct MPT3SAS_ADAPTER *ioc, ++ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data); ++void mpt2sas_trigger_master(struct MPT3SAS_ADAPTER *ioc, ++ u32 tigger_bitmask); ++void mpt2sas_trigger_event(struct MPT3SAS_ADAPTER *ioc, u16 event, ++ u16 log_entry_qualifier); ++void mpt2sas_trigger_scsi(struct MPT3SAS_ADAPTER *ioc, u8 sense_key, ++ u8 asc, u8 ascq); ++void mpt2sas_trigger_mpi(struct MPT3SAS_ADAPTER *ioc, u16 ioc_status, ++ u32 loginfo); ++ ++/* warpdrive APIs */ ++u8 mpt2sas_get_num_volumes(struct MPT3SAS_ADAPTER *ioc); ++void mpt2sas_init_warpdrive_properties(struct MPT3SAS_ADAPTER *ioc, ++ struct _raid_device *raid_device); ++u8 ++mpt2sas_scsi_direct_io_get(struct MPT3SAS_ADAPTER *ioc, u16 smid); ++void ++mpt2sas_scsi_direct_io_set(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 direct_io); ++void ++mpt2sas_setup_direct_io(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, ++ struct _raid_device *raid_device, Mpi2SCSIIORequest_t *mpi_request, ++ u16 smid); ++ ++#endif /* MPT3SAS_BASE_H_INCLUDED */ +diff --git a/drivers/scsi/mpt2sas/mpt3sas_config.c b/drivers/scsi/mpt2sas/mpt3sas_config.c +new file mode 100644 +index 0000000..0f67b2c +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_config.c +@@ -0,0 +1,1716 @@ ++/* ++ * This module provides common API for accessing firmware configuration pages ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.c ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mpt3sas_base.h" ++ ++/* local definitions */ ++ ++/* Timeout for config page request (in seconds) */ ++#define MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT 15 ++ ++/* Common sgl flags for READING a config page. */ ++#define MPT3_CONFIG_COMMON_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \ ++ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \ ++ | MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT) ++ ++/* Common sgl flags for WRITING a config page. */ ++#define MPT3_CONFIG_COMMON_WRITE_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \ ++ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \ ++ | MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC) \ ++ << MPI2_SGE_FLAGS_SHIFT) ++ ++/** ++ * struct config_request - obtain dma memory via routine ++ * @sz: size ++ * @page: virt pointer ++ * @page_dma: phys pointer ++ * ++ */ ++struct config_request { ++ u16 sz; ++ void *page; ++ dma_addr_t page_dma; ++}; ++ ++/** ++ * _config_display_some_debug - debug routine ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @calling_function_name: string pass from calling function ++ * @mpi_reply: reply message frame ++ * Context: none. ++ * ++ * Function for displaying debug info helpful when debugging issues ++ * in this module. ++ */ ++static void ++_config_display_some_debug(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ char *calling_function_name, MPI2DefaultReply_t *mpi_reply) ++{ ++ Mpi2ConfigRequest_t *mpi_request; ++ char *desc = NULL; ++ ++ if (!(ioc->logging_level & MPT_DEBUG_CONFIG)) ++ return; ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ switch (mpi_request->Header.PageType & MPI2_CONFIG_PAGETYPE_MASK) { ++ case MPI2_CONFIG_PAGETYPE_IO_UNIT: ++ desc = "io_unit"; ++ break; ++ case MPI2_CONFIG_PAGETYPE_IOC: ++ desc = "ioc"; ++ break; ++ case MPI2_CONFIG_PAGETYPE_BIOS: ++ desc = "bios"; ++ break; ++ case MPI2_CONFIG_PAGETYPE_RAID_VOLUME: ++ desc = "raid_volume"; ++ break; ++ case MPI2_CONFIG_PAGETYPE_MANUFACTURING: ++ desc = "manufaucturing"; ++ break; ++ case MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK: ++ desc = "physdisk"; ++ break; ++ case MPI2_CONFIG_PAGETYPE_EXTENDED: ++ switch (mpi_request->ExtPageType) { ++ case MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT: ++ desc = "sas_io_unit"; ++ break; ++ case MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER: ++ desc = "sas_expander"; ++ break; ++ case MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE: ++ desc = "sas_device"; ++ break; ++ case MPI2_CONFIG_EXTPAGETYPE_SAS_PHY: ++ desc = "sas_phy"; ++ break; ++ case MPI2_CONFIG_EXTPAGETYPE_LOG: ++ desc = "log"; ++ break; ++ case MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE: ++ desc = "enclosure"; ++ break; ++ case MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG: ++ desc = "raid_config"; ++ break; ++ case MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING: ++ desc = "driver_mapping"; ++ break; ++ } ++ break; ++ } ++ ++ if (!desc) ++ return; ++ ++ pr_info(MPT3SAS_FMT ++ "%s: %s(%d), action(%d), form(0x%08x), smid(%d)\n", ++ ioc->name, calling_function_name, desc, ++ mpi_request->Header.PageNumber, mpi_request->Action, ++ le32_to_cpu(mpi_request->PageAddress), smid); ++ ++ if (!mpi_reply) ++ return; ++ ++ if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo) ++ pr_info(MPT3SAS_FMT ++ "\tiocstatus(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply->IOCStatus), ++ le32_to_cpu(mpi_reply->IOCLogInfo)); ++} ++ ++/** ++ * _config_alloc_config_dma_memory - obtain physical memory ++ * @ioc: per adapter object ++ * @mem: struct config_request ++ * ++ * A wrapper for obtaining dma-able memory for config page request. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_config_alloc_config_dma_memory(struct MPT3SAS_ADAPTER *ioc, ++ struct config_request *mem) ++{ ++ int r = 0; ++ ++ if (mem->sz > ioc->config_page_sz) { ++ mem->page = dma_alloc_coherent(&ioc->pdev->dev, mem->sz, ++ &mem->page_dma, GFP_KERNEL); ++ if (!mem->page) { ++ pr_err(MPT3SAS_FMT ++ "%s: dma_alloc_coherent failed asking for (%d) bytes!!\n", ++ ioc->name, __func__, mem->sz); ++ r = -ENOMEM; ++ } ++ } else { /* use tmp buffer if less than 512 bytes */ ++ mem->page = ioc->config_page; ++ mem->page_dma = ioc->config_page_dma; ++ } ++ return r; ++} ++ ++/** ++ * _config_free_config_dma_memory - wrapper to free the memory ++ * @ioc: per adapter object ++ * @mem: struct config_request ++ * ++ * A wrapper to free dma-able memory when using _config_alloc_config_dma_memory. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static void ++_config_free_config_dma_memory(struct MPT3SAS_ADAPTER *ioc, ++ struct config_request *mem) ++{ ++ if (mem->sz > ioc->config_page_sz) ++ dma_free_coherent(&ioc->pdev->dev, mem->sz, mem->page, ++ mem->page_dma); ++} ++ ++/** ++ * mpt2sas_config_done - config page completion routine ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: none. ++ * ++ * The callback handler when using _config_request. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_config_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ ++ if (ioc->config_cmds.status == MPT3_CMD_NOT_USED) ++ return 1; ++ if (ioc->config_cmds.smid != smid) ++ return 1; ++ ioc->config_cmds.status |= MPT3_CMD_COMPLETE; ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (mpi_reply) { ++ ioc->config_cmds.status |= MPT3_CMD_REPLY_VALID; ++ memcpy(ioc->config_cmds.reply, mpi_reply, ++ mpi_reply->MsgLength*4); ++ } ++ ioc->config_cmds.status &= ~MPT3_CMD_PENDING; ++ _config_display_some_debug(ioc, smid, "config_done", mpi_reply); ++ ioc->config_cmds.smid = USHRT_MAX; ++ complete(&ioc->config_cmds.done); ++ return 1; ++} ++ ++/** ++ * _config_request - main routine for sending config page requests ++ * @ioc: per adapter object ++ * @mpi_request: request message frame ++ * @mpi_reply: reply mf payload returned from firmware ++ * @timeout: timeout in seconds ++ * @config_page: contents of the config page ++ * @config_page_sz: size of config page ++ * Context: sleep ++ * ++ * A generic API for config page requests to firmware. ++ * ++ * The ioc->config_cmds.status flag should be MPT3_CMD_NOT_USED before calling ++ * this API. ++ * ++ * The callback index is set inside `ioc->config_cb_idx. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t ++ *mpi_request, Mpi2ConfigReply_t *mpi_reply, int timeout, ++ void *config_page, u16 config_page_sz) ++{ ++ u16 smid; ++ u32 ioc_state; ++ unsigned long timeleft; ++ Mpi2ConfigRequest_t *config_request; ++ int r; ++ u8 retry_count, issue_host_reset = 0; ++ u16 wait_state_count; ++ struct config_request mem; ++ u32 ioc_status = UINT_MAX; ++ ++ mutex_lock(&ioc->config_cmds.mutex); ++ if (ioc->config_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: config_cmd in use\n", ++ ioc->name, __func__); ++ mutex_unlock(&ioc->config_cmds.mutex); ++ return -EAGAIN; ++ } ++ ++ retry_count = 0; ++ memset(&mem, 0, sizeof(struct config_request)); ++ ++ mpi_request->VF_ID = 0; /* TODO */ ++ mpi_request->VP_ID = 0; ++ ++ if (config_page) { ++ mpi_request->Header.PageVersion = mpi_reply->Header.PageVersion; ++ mpi_request->Header.PageNumber = mpi_reply->Header.PageNumber; ++ mpi_request->Header.PageType = mpi_reply->Header.PageType; ++ mpi_request->Header.PageLength = mpi_reply->Header.PageLength; ++ mpi_request->ExtPageLength = mpi_reply->ExtPageLength; ++ mpi_request->ExtPageType = mpi_reply->ExtPageType; ++ if (mpi_request->Header.PageLength) ++ mem.sz = mpi_request->Header.PageLength * 4; ++ else ++ mem.sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4; ++ r = _config_alloc_config_dma_memory(ioc, &mem); ++ if (r != 0) ++ goto out; ++ if (mpi_request->Action == ++ MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT || ++ mpi_request->Action == ++ MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM) { ++ ioc->base_add_sg_single(&mpi_request->PageBufferSGE, ++ MPT3_CONFIG_COMMON_WRITE_SGLFLAGS | mem.sz, ++ mem.page_dma); ++ memcpy(mem.page, config_page, min_t(u16, mem.sz, ++ config_page_sz)); ++ } else { ++ memset(config_page, 0, config_page_sz); ++ ioc->base_add_sg_single(&mpi_request->PageBufferSGE, ++ MPT3_CONFIG_COMMON_SGLFLAGS | mem.sz, mem.page_dma); ++ memset(mem.page, 0, min_t(u16, mem.sz, config_page_sz)); ++ } ++ } ++ ++ retry_config: ++ if (retry_count) { ++ if (retry_count > 2) { /* attempt only 2 retries */ ++ r = -EFAULT; ++ goto free_mem; ++ } ++ pr_info(MPT3SAS_FMT "%s: attempting retry (%d)\n", ++ ioc->name, __func__, retry_count); ++ } ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ ioc->config_cmds.status = MPT3_CMD_NOT_USED; ++ r = -EFAULT; ++ goto free_mem; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, __func__, wait_state_count); ++ } ++ if (wait_state_count) ++ pr_info(MPT3SAS_FMT "%s: ioc is operational\n", ++ ioc->name, __func__); ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->config_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ ioc->config_cmds.status = MPT3_CMD_NOT_USED; ++ r = -EAGAIN; ++ goto free_mem; ++ } ++ ++ r = 0; ++ memset(mpi_reply, 0, sizeof(Mpi2ConfigReply_t)); ++ ioc->config_cmds.status = MPT3_CMD_PENDING; ++ config_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->config_cmds.smid = smid; ++ memcpy(config_request, mpi_request, sizeof(Mpi2ConfigRequest_t)); ++ _config_display_some_debug(ioc, smid, "config_request", NULL); ++ init_completion(&ioc->config_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->config_cmds.done, ++ timeout*HZ); ++ if (!(ioc->config_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2ConfigRequest_t)/4); ++ retry_count++; ++ if (ioc->config_cmds.smid == smid) ++ mpt2sas_base_free_smid(ioc, smid); ++ if ((ioc->shost_recovery) || (ioc->config_cmds.status & ++ MPT3_CMD_RESET) || ioc->pci_error_recovery) ++ goto retry_config; ++ issue_host_reset = 1; ++ r = -EFAULT; ++ goto free_mem; ++ } ++ ++ if (ioc->config_cmds.status & MPT3_CMD_REPLY_VALID) { ++ memcpy(mpi_reply, ioc->config_cmds.reply, ++ sizeof(Mpi2ConfigReply_t)); ++ ++ /* Reply Frame Sanity Checks to workaround FW issues */ ++ if ((mpi_request->Header.PageType & 0xF) != ++ (mpi_reply->Header.PageType & 0xF)) { ++ _debug_dump_mf(mpi_request, ioc->request_sz/4); ++ _debug_dump_reply(mpi_reply, ioc->request_sz/4); ++ panic(KERN_WARNING MPT3SAS_FMT "%s: Firmware BUG:" \ ++ " mpi_reply mismatch: Requested PageType(0x%02x)" \ ++ " Reply PageType(0x%02x)\n", \ ++ ioc->name, __func__, ++ (mpi_request->Header.PageType & 0xF), ++ (mpi_reply->Header.PageType & 0xF)); ++ } ++ ++ if (((mpi_request->Header.PageType & 0xF) == ++ MPI2_CONFIG_PAGETYPE_EXTENDED) && ++ mpi_request->ExtPageType != mpi_reply->ExtPageType) { ++ _debug_dump_mf(mpi_request, ioc->request_sz/4); ++ _debug_dump_reply(mpi_reply, ioc->request_sz/4); ++ panic(KERN_WARNING MPT3SAS_FMT "%s: Firmware BUG:" \ ++ " mpi_reply mismatch: Requested ExtPageType(0x%02x)" ++ " Reply ExtPageType(0x%02x)\n", ++ ioc->name, __func__, mpi_request->ExtPageType, ++ mpi_reply->ExtPageType); ++ } ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) ++ & MPI2_IOCSTATUS_MASK; ++ } ++ ++ if (retry_count) ++ pr_info(MPT3SAS_FMT "%s: retry (%d) completed!!\n", \ ++ ioc->name, __func__, retry_count); ++ ++ if ((ioc_status == MPI2_IOCSTATUS_SUCCESS) && ++ config_page && mpi_request->Action == ++ MPI2_CONFIG_ACTION_PAGE_READ_CURRENT) { ++ u8 *p = (u8 *)mem.page; ++ ++ /* Config Page Sanity Checks to workaround FW issues */ ++ if (p) { ++ if ((mpi_request->Header.PageType & 0xF) != ++ (p[3] & 0xF)) { ++ _debug_dump_mf(mpi_request, ioc->request_sz/4); ++ _debug_dump_reply(mpi_reply, ioc->request_sz/4); ++ _debug_dump_config(p, min_t(u16, mem.sz, ++ config_page_sz)/4); ++ panic(KERN_WARNING MPT3SAS_FMT ++ "%s: Firmware BUG:" \ ++ " config page mismatch:" ++ " Requested PageType(0x%02x)" ++ " Reply PageType(0x%02x)\n", ++ ioc->name, __func__, ++ (mpi_request->Header.PageType & 0xF), ++ (p[3] & 0xF)); ++ } ++ ++ if (((mpi_request->Header.PageType & 0xF) == ++ MPI2_CONFIG_PAGETYPE_EXTENDED) && ++ (mpi_request->ExtPageType != p[6])) { ++ _debug_dump_mf(mpi_request, ioc->request_sz/4); ++ _debug_dump_reply(mpi_reply, ioc->request_sz/4); ++ _debug_dump_config(p, min_t(u16, mem.sz, ++ config_page_sz)/4); ++ panic(KERN_WARNING MPT3SAS_FMT ++ "%s: Firmware BUG:" \ ++ " config page mismatch:" ++ " Requested ExtPageType(0x%02x)" ++ " Reply ExtPageType(0x%02x)\n", ++ ioc->name, __func__, ++ mpi_request->ExtPageType, p[6]); ++ } ++ } ++ memcpy(config_page, mem.page, min_t(u16, mem.sz, ++ config_page_sz)); ++ } ++ ++ free_mem: ++ if (config_page) ++ _config_free_config_dma_memory(ioc, &mem); ++ out: ++ ioc->config_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_unlock(&ioc->config_cmds.mutex); ++ ++ if (issue_host_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_manufacturing_pg0 - obtain manufacturing page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_manufacturing_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_manufacturing_pg7 - obtain manufacturing page 7 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @sz: size of buffer passed in config_page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_manufacturing_pg7(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage7_t *config_page, ++ u16 sz) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING; ++ mpi_request.Header.PageNumber = 7; ++ mpi_request.Header.PageVersion = MPI2_MANUFACTURING7_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sz); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_manufacturing_pg10 - obtain manufacturing page 10 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_manufacturing_pg10(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, ++ struct Mpi2ManufacturingPage10_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING; ++ mpi_request.Header.PageNumber = 10; ++ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_manufacturing_pg11 - obtain manufacturing page 11 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, ++ struct Mpi2ManufacturingPage11_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING; ++ mpi_request.Header.PageNumber = 11; ++ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_set_manufacturing_pg11 - set manufacturing page 11 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_set_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, ++ struct Mpi2ManufacturingPage11_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING; ++ mpi_request.Header.PageNumber = 11; ++ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_bios_pg2 - obtain bios page 2 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_bios_pg2(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage2_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS; ++ mpi_request.Header.PageNumber = 2; ++ mpi_request.Header.PageVersion = MPI2_BIOSPAGE2_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_bios_pg3 - obtain bios page 3 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_bios_pg3(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2BiosPage3_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS; ++ mpi_request.Header.PageNumber = 3; ++ mpi_request.Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_iounit_pg0 - obtain iounit page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_iounit_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage0_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_iounit_pg1 - obtain iounit page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; ++ mpi_request.Header.PageNumber = 1; ++ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_set_iounit_pg1 - set iounit page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_set_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; ++ mpi_request.Header.PageNumber = 1; ++ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_iounit_pg3 - obtain iounit page 3 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @sz: size of buffer passed in config_page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_iounit_pg3(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage3_t *config_page, u16 sz) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; ++ mpi_request.Header.PageNumber = 3; ++ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE3_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_iounit_pg8 - obtain iounit page 8 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_iounit_pg8(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage8_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; ++ mpi_request.Header.PageNumber = 8; ++ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE8_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_ioc_pg8 - obtain ioc page 8 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_ioc_pg8(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2IOCPage8_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IOC; ++ mpi_request.Header.PageNumber = 8; ++ mpi_request.Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_sas_device_pg0 - obtain sas device page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @form: GET_NEXT_HANDLE or HANDLE ++ * @handle: device handle ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_sas_device_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage0_t *config_page, ++ u32 form, u32 handle) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE; ++ mpi_request.Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION; ++ mpi_request.Header.PageNumber = 0; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = cpu_to_le32(form | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_sas_device_pg1 - obtain sas device page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @form: GET_NEXT_HANDLE or HANDLE ++ * @handle: device handle ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_sas_device_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage1_t *config_page, ++ u32 form, u32 handle) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE; ++ mpi_request.Header.PageVersion = MPI2_SASDEVICE1_PAGEVERSION; ++ mpi_request.Header.PageNumber = 1; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = cpu_to_le32(form | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_number_hba_phys - obtain number of phys on the host ++ * @ioc: per adapter object ++ * @num_phys: pointer returned with the number of phys ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_number_hba_phys(struct MPT3SAS_ADAPTER *ioc, u8 *num_phys) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ u16 ioc_status; ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasIOUnitPage0_t config_page; ++ ++ *num_phys = 0; ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, &mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, &mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, &config_page, ++ sizeof(Mpi2SasIOUnitPage0_t)); ++ if (!r) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) ++ *num_phys = config_page.NumPhys; ++ } ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_sas_iounit_pg0 - obtain sas iounit page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @sz: size of buffer passed in config_page ++ * Context: sleep. ++ * ++ * Calling function should call config_get_number_hba_phys prior to ++ * this function, so enough memory is allocated for config_page. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_sas_iounit_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage0_t *config_page, ++ u16 sz) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_sas_iounit_pg1 - obtain sas iounit page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @sz: size of buffer passed in config_page ++ * Context: sleep. ++ * ++ * Calling function should call config_get_number_hba_phys prior to ++ * this function, so enough memory is allocated for config_page. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, ++ u16 sz) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; ++ mpi_request.Header.PageNumber = 1; ++ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_set_sas_iounit_pg1 - send sas iounit page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @sz: size of buffer passed in config_page ++ * Context: sleep. ++ * ++ * Calling function should call config_get_number_hba_phys prior to ++ * this function, so enough memory is allocated for config_page. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_set_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, ++ u16 sz) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; ++ mpi_request.Header.PageNumber = 1; ++ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; ++ _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_expander_pg0 - obtain expander page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @form: GET_NEXT_HANDLE or HANDLE ++ * @handle: expander handle ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_expander_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2ExpanderPage0_t *config_page, u32 form, u32 handle) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_SASEXPANDER0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = cpu_to_le32(form | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_expander_pg1 - obtain expander page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @phy_number: phy number ++ * @handle: expander handle ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_expander_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2ExpanderPage1_t *config_page, u32 phy_number, ++ u16 handle) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER; ++ mpi_request.Header.PageNumber = 1; ++ mpi_request.Header.PageVersion = MPI2_SASEXPANDER1_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = ++ cpu_to_le32(MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM | ++ (phy_number << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_enclosure_pg0 - obtain enclosure page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @form: GET_NEXT_HANDLE or HANDLE ++ * @handle: expander handle ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_enclosure_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2SasEnclosurePage0_t *config_page, u32 form, u32 handle) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_SASENCLOSURE0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = cpu_to_le32(form | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_phy_pg0 - obtain phy page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @phy_number: phy number ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_phy_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_SASPHY0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = ++ cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_phy_pg1 - obtain phy page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @phy_number: phy number ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_phy_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY; ++ mpi_request.Header.PageNumber = 1; ++ mpi_request.Header.PageVersion = MPI2_SASPHY1_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = ++ cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_raid_volume_pg1 - obtain raid volume page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @form: GET_NEXT_HANDLE or HANDLE ++ * @handle: volume handle ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_raid_volume_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form, ++ u32 handle) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME; ++ mpi_request.Header.PageNumber = 1; ++ mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE1_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = cpu_to_le32(form | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_number_pds - obtain number of phys disk assigned to volume ++ * @ioc: per adapter object ++ * @handle: volume handle ++ * @num_pds: returns pds count ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_number_pds(struct MPT3SAS_ADAPTER *ioc, u16 handle, ++ u8 *num_pds) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ Mpi2RaidVolPage0_t config_page; ++ Mpi2ConfigReply_t mpi_reply; ++ int r; ++ u16 ioc_status; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ *num_pds = 0; ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, &mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = ++ cpu_to_le32(MPI2_RAID_VOLUME_PGAD_FORM_HANDLE | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, &mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, &config_page, ++ sizeof(Mpi2RaidVolPage0_t)); ++ if (!r) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) ++ *num_pds = config_page.NumPhysDisks; ++ } ++ ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_raid_volume_pg0 - obtain raid volume page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @form: GET_NEXT_HANDLE or HANDLE ++ * @handle: volume handle ++ * @sz: size of buffer passed in config_page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_raid_volume_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form, ++ u32 handle, u16 sz) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = cpu_to_le32(form | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_phys_disk_pg0 - obtain phys disk page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @form: GET_NEXT_PHYSDISKNUM, PHYSDISKNUM, DEVHANDLE ++ * @form_specific: specific to the form ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_phys_disk_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page, u32 form, ++ u32 form_specific) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = cpu_to_le32(form | form_specific); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_volume_handle - returns volume handle for give hidden ++ * raid components ++ * @ioc: per adapter object ++ * @pd_handle: phys disk handle ++ * @volume_handle: volume handle ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_volume_handle(struct MPT3SAS_ADAPTER *ioc, u16 pd_handle, ++ u16 *volume_handle) ++{ ++ Mpi2RaidConfigurationPage0_t *config_page = NULL; ++ Mpi2ConfigRequest_t mpi_request; ++ Mpi2ConfigReply_t mpi_reply; ++ int r, i, config_page_sz; ++ u16 ioc_status; ++ int config_num; ++ u16 element_type; ++ u16 phys_disk_dev_handle; ++ ++ *volume_handle = 0; ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG; ++ mpi_request.Header.PageVersion = MPI2_RAIDCONFIG0_PAGEVERSION; ++ mpi_request.Header.PageNumber = 0; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, &mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ config_page_sz = (le16_to_cpu(mpi_reply.ExtPageLength) * 4); ++ config_page = kmalloc(config_page_sz, GFP_KERNEL); ++ if (!config_page) { ++ r = -1; ++ goto out; ++ } ++ ++ config_num = 0xff; ++ while (1) { ++ mpi_request.PageAddress = cpu_to_le32(config_num + ++ MPI2_RAID_PGAD_FORM_GET_NEXT_CONFIGNUM); ++ r = _config_request(ioc, &mpi_request, &mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ config_page_sz); ++ if (r) ++ goto out; ++ r = -1; ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ goto out; ++ for (i = 0; i < config_page->NumElements; i++) { ++ element_type = le16_to_cpu(config_page-> ++ ConfigElement[i].ElementFlags) & ++ MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE; ++ if (element_type == ++ MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT || ++ element_type == ++ MPI2_RAIDCONFIG0_EFLAGS_OCE_ELEMENT) { ++ phys_disk_dev_handle = ++ le16_to_cpu(config_page->ConfigElement[i]. ++ PhysDiskDevHandle); ++ if (phys_disk_dev_handle == pd_handle) { ++ *volume_handle = ++ le16_to_cpu(config_page-> ++ ConfigElement[i].VolDevHandle); ++ r = 0; ++ goto out; ++ } ++ } else if (element_type == ++ MPI2_RAIDCONFIG0_EFLAGS_HOT_SPARE_ELEMENT) { ++ *volume_handle = 0; ++ r = 0; ++ goto out; ++ } ++ } ++ config_num = config_page->ConfigNum; ++ } ++ out: ++ kfree(config_page); ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_volume_wwid - returns wwid given the volume handle ++ * @ioc: per adapter object ++ * @volume_handle: volume handle ++ * @wwid: volume wwid ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_volume_wwid(struct MPT3SAS_ADAPTER *ioc, u16 volume_handle, ++ u64 *wwid) ++{ ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2RaidVolPage1_t raid_vol_pg1; ++ ++ *wwid = 0; ++ if (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, ++ &raid_vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, ++ volume_handle))) { ++ *wwid = le64_to_cpu(raid_vol_pg1.WWID); ++ return 0; ++ } else ++ return -1; ++} +diff --git a/drivers/scsi/mpt2sas/mpt3sas_ctl.c b/drivers/scsi/mpt2sas/mpt3sas_ctl.c +new file mode 100644 +index 0000000..5c0cc30 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_ctl.c +@@ -0,0 +1,3483 @@ ++/* ++ * Management Module Support for MPT (Message Passing Technology) based ++ * controllers ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_ctl.c ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "mpt3sas_base.h" ++#include "mpt3sas_ctl.h" ++ ++ ++static struct fasync_struct *async_queue; ++static DECLARE_WAIT_QUEUE_HEAD(ctl_poll_wait); ++ ++ ++/** ++ * enum block_state - blocking state ++ * @NON_BLOCKING: non blocking ++ * @BLOCKING: blocking ++ * ++ * These states are for ioctls that need to wait for a response ++ * from firmware, so they probably require sleep. ++ */ ++enum block_state { ++ NON_BLOCKING, ++ BLOCKING, ++}; ++ ++/** ++ * _ctl_sas_device_find_by_handle - sas device search ++ * @ioc: per adapter object ++ * @handle: sas device handle (assigned by firmware) ++ * Context: Calling function should acquire ioc->sas_device_lock ++ * ++ * This searches for sas_device based on sas_address, then return sas_device ++ * object. ++ */ ++static struct _sas_device * ++_ctl_sas_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _sas_device *sas_device, *r; ++ ++ r = NULL; ++ list_for_each_entry(sas_device, &ioc->sas_device_list, list) { ++ if (sas_device->handle != handle) ++ continue; ++ r = sas_device; ++ goto out; ++ } ++ ++ out: ++ return r; ++} ++ ++/** ++ * _ctl_display_some_debug - debug routine ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @calling_function_name: string pass from calling function ++ * @mpi_reply: reply message frame ++ * Context: none. ++ * ++ * Function for displaying debug info helpful when debugging issues ++ * in this module. ++ */ ++static void ++_ctl_display_some_debug(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ char *calling_function_name, MPI2DefaultReply_t *mpi_reply) ++{ ++ Mpi2ConfigRequest_t *mpi_request; ++ char *desc = NULL; ++ ++ if (!(ioc->logging_level & MPT_DEBUG_IOCTL)) ++ return; ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ switch (mpi_request->Function) { ++ case MPI2_FUNCTION_SCSI_IO_REQUEST: ++ { ++ Mpi2SCSIIORequest_t *scsi_request = ++ (Mpi2SCSIIORequest_t *)mpi_request; ++ ++ snprintf(ioc->tmp_string, MPT_STRING_LENGTH, ++ "scsi_io, cmd(0x%02x), cdb_len(%d)", ++ scsi_request->CDB.CDB32[0], ++ le16_to_cpu(scsi_request->IoFlags) & 0xF); ++ desc = ioc->tmp_string; ++ break; ++ } ++ case MPI2_FUNCTION_SCSI_TASK_MGMT: ++ desc = "task_mgmt"; ++ break; ++ case MPI2_FUNCTION_IOC_INIT: ++ desc = "ioc_init"; ++ break; ++ case MPI2_FUNCTION_IOC_FACTS: ++ desc = "ioc_facts"; ++ break; ++ case MPI2_FUNCTION_CONFIG: ++ { ++ Mpi2ConfigRequest_t *config_request = ++ (Mpi2ConfigRequest_t *)mpi_request; ++ ++ snprintf(ioc->tmp_string, MPT_STRING_LENGTH, ++ "config, type(0x%02x), ext_type(0x%02x), number(%d)", ++ (config_request->Header.PageType & ++ MPI2_CONFIG_PAGETYPE_MASK), config_request->ExtPageType, ++ config_request->Header.PageNumber); ++ desc = ioc->tmp_string; ++ break; ++ } ++ case MPI2_FUNCTION_PORT_FACTS: ++ desc = "port_facts"; ++ break; ++ case MPI2_FUNCTION_PORT_ENABLE: ++ desc = "port_enable"; ++ break; ++ case MPI2_FUNCTION_EVENT_NOTIFICATION: ++ desc = "event_notification"; ++ break; ++ case MPI2_FUNCTION_FW_DOWNLOAD: ++ desc = "fw_download"; ++ break; ++ case MPI2_FUNCTION_FW_UPLOAD: ++ desc = "fw_upload"; ++ break; ++ case MPI2_FUNCTION_RAID_ACTION: ++ desc = "raid_action"; ++ break; ++ case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH: ++ { ++ Mpi2SCSIIORequest_t *scsi_request = ++ (Mpi2SCSIIORequest_t *)mpi_request; ++ ++ snprintf(ioc->tmp_string, MPT_STRING_LENGTH, ++ "raid_pass, cmd(0x%02x), cdb_len(%d)", ++ scsi_request->CDB.CDB32[0], ++ le16_to_cpu(scsi_request->IoFlags) & 0xF); ++ desc = ioc->tmp_string; ++ break; ++ } ++ case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: ++ desc = "sas_iounit_cntl"; ++ break; ++ case MPI2_FUNCTION_SATA_PASSTHROUGH: ++ desc = "sata_pass"; ++ break; ++ case MPI2_FUNCTION_DIAG_BUFFER_POST: ++ desc = "diag_buffer_post"; ++ break; ++ case MPI2_FUNCTION_DIAG_RELEASE: ++ desc = "diag_release"; ++ break; ++ case MPI2_FUNCTION_SMP_PASSTHROUGH: ++ desc = "smp_passthrough"; ++ break; ++ } ++ ++ if (!desc) ++ return; ++ ++ pr_info(MPT3SAS_FMT "%s: %s, smid(%d)\n", ++ ioc->name, calling_function_name, desc, smid); ++ ++ if (!mpi_reply) ++ return; ++ ++ if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo) ++ pr_info(MPT3SAS_FMT ++ "\tiocstatus(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply->IOCStatus), ++ le32_to_cpu(mpi_reply->IOCLogInfo)); ++ ++ if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || ++ mpi_request->Function == ++ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { ++ Mpi2SCSIIOReply_t *scsi_reply = ++ (Mpi2SCSIIOReply_t *)mpi_reply; ++ struct _sas_device *sas_device = NULL; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = _ctl_sas_device_find_by_handle(ioc, ++ le16_to_cpu(scsi_reply->DevHandle)); ++ if (sas_device) { ++ pr_warn(MPT3SAS_FMT "\tsas_address(0x%016llx), phy(%d)\n", ++ ioc->name, (unsigned long long) ++ sas_device->sas_address, sas_device->phy); ++ pr_warn(MPT3SAS_FMT ++ "\tenclosure_logical_id(0x%016llx), slot(%d)\n", ++ ioc->name, (unsigned long long) ++ sas_device->enclosure_logical_id, sas_device->slot); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ if (scsi_reply->SCSIState || scsi_reply->SCSIStatus) ++ pr_info(MPT3SAS_FMT ++ "\tscsi_state(0x%02x), scsi_status" ++ "(0x%02x)\n", ioc->name, ++ scsi_reply->SCSIState, ++ scsi_reply->SCSIStatus); ++ } ++} ++ ++/** ++ * mpt2sas_ctl_done - ctl module completion routine ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: none. ++ * ++ * The callback handler when using ioc->ctl_cb_idx. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_ctl_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ Mpi2SCSIIOReply_t *scsiio_reply; ++ const void *sense_data; ++ u32 sz; ++ ++ if (ioc->ctl_cmds.status == MPT3_CMD_NOT_USED) ++ return 1; ++ if (ioc->ctl_cmds.smid != smid) ++ return 1; ++ ioc->ctl_cmds.status |= MPT3_CMD_COMPLETE; ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (mpi_reply) { ++ memcpy(ioc->ctl_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); ++ ioc->ctl_cmds.status |= MPT3_CMD_REPLY_VALID; ++ /* get sense data */ ++ if (mpi_reply->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || ++ mpi_reply->Function == ++ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { ++ scsiio_reply = (Mpi2SCSIIOReply_t *)mpi_reply; ++ if (scsiio_reply->SCSIState & ++ MPI2_SCSI_STATE_AUTOSENSE_VALID) { ++ sz = min_t(u32, SCSI_SENSE_BUFFERSIZE, ++ le32_to_cpu(scsiio_reply->SenseCount)); ++ sense_data = mpt2sas_base_get_sense_buffer(ioc, ++ smid); ++ memcpy(ioc->ctl_cmds.sense, sense_data, sz); ++ } ++ } ++ } ++ _ctl_display_some_debug(ioc, smid, "ctl_done", mpi_reply); ++ ioc->ctl_cmds.status &= ~MPT3_CMD_PENDING; ++ complete(&ioc->ctl_cmds.done); ++ return 1; ++} ++ ++/** ++ * _ctl_check_event_type - determines when an event needs logging ++ * @ioc: per adapter object ++ * @event: firmware event ++ * ++ * The bitmask in ioc->event_type[] indicates which events should be ++ * be saved in the driver event_log. This bitmask is set by application. ++ * ++ * Returns 1 when event should be captured, or zero means no match. ++ */ ++static int ++_ctl_check_event_type(struct MPT3SAS_ADAPTER *ioc, u16 event) ++{ ++ u16 i; ++ u32 desired_event; ++ ++ if (event >= 128 || !event || !ioc->event_log) ++ return 0; ++ ++ desired_event = (1 << (event % 32)); ++ if (!desired_event) ++ desired_event = 1; ++ i = event / 32; ++ return desired_event & ioc->event_type[i]; ++} ++ ++/** ++ * mpt2sas_ctl_add_to_event_log - add event ++ * @ioc: per adapter object ++ * @mpi_reply: reply message frame ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_ctl_add_to_event_log(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventNotificationReply_t *mpi_reply) ++{ ++ struct MPT3_IOCTL_EVENTS *event_log; ++ u16 event; ++ int i; ++ u32 sz, event_data_sz; ++ u8 send_aen = 0; ++ ++ if (!ioc->event_log) ++ return; ++ ++ event = le16_to_cpu(mpi_reply->Event); ++ ++ if (_ctl_check_event_type(ioc, event)) { ++ ++ /* insert entry into circular event_log */ ++ i = ioc->event_context % MPT3SAS_CTL_EVENT_LOG_SIZE; ++ event_log = ioc->event_log; ++ event_log[i].event = event; ++ event_log[i].context = ioc->event_context++; ++ ++ event_data_sz = le16_to_cpu(mpi_reply->EventDataLength)*4; ++ sz = min_t(u32, event_data_sz, MPT3_EVENT_DATA_SIZE); ++ memset(event_log[i].data, 0, MPT3_EVENT_DATA_SIZE); ++ memcpy(event_log[i].data, mpi_reply->EventData, sz); ++ send_aen = 1; ++ } ++ ++ /* This aen_event_read_flag flag is set until the ++ * application has read the event log. ++ * For MPI2_EVENT_LOG_ENTRY_ADDED, we always notify. ++ */ ++ if (event == MPI2_EVENT_LOG_ENTRY_ADDED || ++ (send_aen && !ioc->aen_event_read_flag)) { ++ ioc->aen_event_read_flag = 1; ++ wake_up_interruptible(&ctl_poll_wait); ++ if (async_queue) ++ kill_fasync(&async_queue, SIGIO, POLL_IN); ++ } ++} ++ ++/** ++ * mpt2sas_ctl_event_callback - firmware event handler (called at ISR time) ++ * @ioc: per adapter object ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: interrupt. ++ * ++ * This function merely adds a new work task into ioc->firmware_event_thread. ++ * The tasks are worked from _firmware_event_work in user context. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_ctl_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, ++ u32 reply) ++{ ++ Mpi2EventNotificationReply_t *mpi_reply; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (mpi_reply) ++ mpt2sas_ctl_add_to_event_log(ioc, mpi_reply); ++ return 1; ++} ++ ++/** ++ * _ctl_verify_adapter - validates ioc_number passed from application ++ * @ioc: per adapter object ++ * @iocpp: The ioc pointer is returned in this. ++ * @mpi_version: will be MPI2_VERSION for mpt2ctl ioctl device & ++ * MPI25_VERSION | MPI26_VERSION for mpt3ctl ioctl device. ++ * ++ * Return (-1) means error, else ioc_number. ++ */ ++static int ++_ctl_verify_adapter(int ioc_number, struct MPT3SAS_ADAPTER **iocpp, ++ int mpi_version) ++{ ++ struct MPT3SAS_ADAPTER *ioc; ++ int version = 0; ++ /* global ioc lock to protect controller on list operations */ ++ spin_lock(&gioc_lock_mpt2sas); ++ list_for_each_entry(ioc, &mpt2sas_ioc_list, list) { ++ if (ioc->id != ioc_number) ++ continue; ++ /* Check whether this ioctl command is from right ++ * ioctl device or not, if not continue the search. ++ */ ++ version = ioc->hba_mpi_version_belonged; ++ /* MPI25_VERSION and MPI26_VERSION uses same ioctl ++ * device. ++ */ ++ if (mpi_version == (MPI25_VERSION | MPI26_VERSION)) { ++ if ((version == MPI25_VERSION) || ++ (version == MPI26_VERSION)) ++ goto out; ++ else ++ continue; ++ } else { ++ if (version != mpi_version) ++ continue; ++ } ++out: ++ spin_unlock(&gioc_lock_mpt2sas); ++ *iocpp = ioc; ++ return ioc_number; ++ } ++ spin_unlock(&gioc_lock_mpt2sas); ++ *iocpp = NULL; ++ return -1; ++} ++ ++/** ++ * mpt2sas_ctl_reset_handler - reset callback handler (for ctl) ++ * @ioc: per adapter object ++ * @reset_phase: phase ++ * ++ * The handler for doing any required cleanup or initialization. ++ * ++ * The reset phase can be MPT3_IOC_PRE_RESET, MPT3_IOC_AFTER_RESET, ++ * MPT3_IOC_DONE_RESET ++ */ ++void ++mpt2sas_ctl_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase) ++{ ++ int i; ++ u8 issue_reset; ++ ++ switch (reset_phase) { ++ case MPT3_IOC_PRE_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_PRE_RESET\n", ioc->name, __func__)); ++ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) { ++ if (!(ioc->diag_buffer_status[i] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED)) ++ continue; ++ if ((ioc->diag_buffer_status[i] & ++ MPT3_DIAG_BUFFER_IS_RELEASED)) ++ continue; ++ mpt2sas_send_diag_release(ioc, i, &issue_reset); ++ } ++ break; ++ case MPT3_IOC_AFTER_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_AFTER_RESET\n", ioc->name, __func__)); ++ if (ioc->ctl_cmds.status & MPT3_CMD_PENDING) { ++ ioc->ctl_cmds.status |= MPT3_CMD_RESET; ++ mpt2sas_base_free_smid(ioc, ioc->ctl_cmds.smid); ++ complete(&ioc->ctl_cmds.done); ++ } ++ break; ++ case MPT3_IOC_DONE_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_DONE_RESET\n", ioc->name, __func__)); ++ ++ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) { ++ if (!(ioc->diag_buffer_status[i] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED)) ++ continue; ++ if ((ioc->diag_buffer_status[i] & ++ MPT3_DIAG_BUFFER_IS_RELEASED)) ++ continue; ++ ioc->diag_buffer_status[i] |= ++ MPT3_DIAG_BUFFER_IS_DIAG_RESET; ++ } ++ break; ++ } ++} ++ ++/** ++ * _ctl_fasync_mpt2sas - ++ * @fd - ++ * @filep - ++ * @mode - ++ * ++ * Called when application request fasyn callback handler. ++ */ ++int ++_ctl_fasync_mpt2sas(int fd, struct file *filep, int mode) ++{ ++ return fasync_helper(fd, filep, mode, &async_queue); ++} ++ ++/** ++ * _ctl_poll_mpt2sas - ++ * @file - ++ * @wait - ++ * ++ */ ++unsigned int ++_ctl_poll_mpt2sas(struct file *filep, poll_table *wait) ++{ ++ struct MPT3SAS_ADAPTER *ioc; ++ ++ poll_wait(filep, &ctl_poll_wait, wait); ++ ++ /* global ioc lock to protect controller on list operations */ ++ spin_lock(&gioc_lock_mpt2sas); ++ list_for_each_entry(ioc, &mpt2sas_ioc_list, list) { ++ if (ioc->aen_event_read_flag) { ++ spin_unlock(&gioc_lock_mpt2sas); ++ return POLLIN | POLLRDNORM; ++ } ++ } ++ spin_unlock(&gioc_lock_mpt2sas); ++ return 0; ++} ++ ++/** ++ * _ctl_set_task_mid - assign an active smid to tm request ++ * @ioc: per adapter object ++ * @karg - (struct mpt3_ioctl_command) ++ * @tm_request - pointer to mf from user space ++ * ++ * Returns 0 when an smid if found, else fail. ++ * during failure, the reply frame is filled. ++ */ ++static int ++_ctl_set_task_mid(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command *karg, ++ Mpi2SCSITaskManagementRequest_t *tm_request) ++{ ++ u8 found = 0; ++ u16 i; ++ u16 handle; ++ struct scsi_cmnd *scmd; ++ struct MPT3SAS_DEVICE *priv_data; ++ unsigned long flags; ++ Mpi2SCSITaskManagementReply_t *tm_reply; ++ u32 sz; ++ u32 lun; ++ char *desc = NULL; ++ ++ if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK) ++ desc = "abort_task"; ++ else if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK) ++ desc = "query_task"; ++ else ++ return 0; ++ ++ lun = scsilun_to_int((struct scsi_lun *)tm_request->LUN); ++ ++ handle = le16_to_cpu(tm_request->DevHandle); ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ for (i = ioc->scsiio_depth; i && !found; i--) { ++ scmd = ioc->scsi_lookup[i - 1].scmd; ++ if (scmd == NULL || scmd->device == NULL || ++ scmd->device->hostdata == NULL) ++ continue; ++ if (lun != scmd->device->lun) ++ continue; ++ priv_data = scmd->device->hostdata; ++ if (priv_data->sas_target == NULL) ++ continue; ++ if (priv_data->sas_target->handle != handle) ++ continue; ++ tm_request->TaskMID = cpu_to_le16(ioc->scsi_lookup[i - 1].smid); ++ found = 1; ++ } ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ ++ if (!found) { ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: handle(0x%04x), lun(%d), no active mid!!\n", ++ ioc->name, ++ desc, le16_to_cpu(tm_request->DevHandle), lun)); ++ tm_reply = ioc->ctl_cmds.reply; ++ tm_reply->DevHandle = tm_request->DevHandle; ++ tm_reply->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; ++ tm_reply->TaskType = tm_request->TaskType; ++ tm_reply->MsgLength = sizeof(Mpi2SCSITaskManagementReply_t)/4; ++ tm_reply->VP_ID = tm_request->VP_ID; ++ tm_reply->VF_ID = tm_request->VF_ID; ++ sz = min_t(u32, karg->max_reply_bytes, ioc->reply_sz); ++ if (copy_to_user(karg->reply_frame_buf_ptr, ioc->ctl_cmds.reply, ++ sz)) ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ return 1; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: handle(0x%04x), lun(%d), task_mid(%d)\n", ioc->name, ++ desc, le16_to_cpu(tm_request->DevHandle), lun, ++ le16_to_cpu(tm_request->TaskMID))); ++ return 0; ++} ++ ++/** ++ * _ctl_do_mpt_command - main handler for MPT3COMMAND opcode ++ * @ioc: per adapter object ++ * @karg - (struct mpt3_ioctl_command) ++ * @mf - pointer to mf in user space ++ */ ++static long ++_ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, ++ void __user *mf) ++{ ++ MPI2RequestHeader_t *mpi_request = NULL, *request; ++ MPI2DefaultReply_t *mpi_reply; ++ u32 ioc_state; ++ u16 ioc_status; ++ u16 smid; ++ unsigned long timeout, timeleft; ++ u8 issue_reset; ++ u32 sz; ++ void *psge; ++ void *data_out = NULL; ++ dma_addr_t data_out_dma = 0; ++ size_t data_out_sz = 0; ++ void *data_in = NULL; ++ dma_addr_t data_in_dma = 0; ++ size_t data_in_sz = 0; ++ long ret; ++ u16 wait_state_count; ++ ++ issue_reset = 0; ++ ++ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: ctl_cmd in use\n", ++ ioc->name, __func__); ++ ret = -EAGAIN; ++ goto out; ++ } ++ ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ ret = -EFAULT; ++ goto out; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, ++ __func__, wait_state_count); ++ } ++ if (wait_state_count) ++ pr_info(MPT3SAS_FMT "%s: ioc is operational\n", ++ ioc->name, __func__); ++ ++ mpi_request = kzalloc(ioc->request_sz, GFP_KERNEL); ++ if (!mpi_request) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed obtaining a memory for mpi_request\n", ++ ioc->name, __func__); ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ /* Check for overflow and wraparound */ ++ if (karg.data_sge_offset * 4 > ioc->request_sz || ++ karg.data_sge_offset > (UINT_MAX / 4)) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* copy in request message frame from user */ ++ if (copy_from_user(mpi_request, mf, karg.data_sge_offset*4)) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, __LINE__, ++ __func__); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { ++ smid = mpt2sas_base_get_smid_hpr(ioc, ioc->ctl_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ ret = -EAGAIN; ++ goto out; ++ } ++ } else { ++ ++ smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->ctl_cb_idx, NULL); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ ret = -EAGAIN; ++ goto out; ++ } ++ } ++ ++ ret = 0; ++ ioc->ctl_cmds.status = MPT3_CMD_PENDING; ++ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); ++ request = mpt2sas_base_get_msg_frame(ioc, smid); ++ memcpy(request, mpi_request, karg.data_sge_offset*4); ++ ioc->ctl_cmds.smid = smid; ++ data_out_sz = karg.data_out_size; ++ data_in_sz = karg.data_in_size; ++ ++ if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || ++ mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { ++ if (!le16_to_cpu(mpi_request->FunctionDependent1) || ++ le16_to_cpu(mpi_request->FunctionDependent1) > ++ ioc->facts.MaxDevHandle) { ++ ret = -EINVAL; ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ } ++ ++ /* obtain dma-able memory for data transfer */ ++ if (data_out_sz) /* WRITE */ { ++ data_out = pci_alloc_consistent(ioc->pdev, data_out_sz, ++ &data_out_dma); ++ if (!data_out) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ ret = -ENOMEM; ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ if (copy_from_user(data_out, karg.data_out_buf_ptr, ++ data_out_sz)) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ ret = -EFAULT; ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ } ++ ++ if (data_in_sz) /* READ */ { ++ data_in = pci_alloc_consistent(ioc->pdev, data_in_sz, ++ &data_in_dma); ++ if (!data_in) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ ret = -ENOMEM; ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ } ++ ++ psge = (void *)request + (karg.data_sge_offset*4); ++ ++ /* send command to firmware */ ++ _ctl_display_some_debug(ioc, smid, "ctl_request", NULL); ++ ++ init_completion(&ioc->ctl_cmds.done); ++ switch (mpi_request->Function) { ++ case MPI2_FUNCTION_SCSI_IO_REQUEST: ++ case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH: ++ { ++ Mpi2SCSIIORequest_t *scsiio_request = ++ (Mpi2SCSIIORequest_t *)request; ++ scsiio_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE; ++ scsiio_request->SenseBufferLowAddress = ++ mpt2sas_base_get_sense_buffer_dma(ioc, smid); ++ memset(ioc->ctl_cmds.sense, 0, SCSI_SENSE_BUFFERSIZE); ++ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, ++ data_in_dma, data_in_sz); ++ ++ if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST) ++ mpt2sas_base_put_smid_scsi_io(ioc, smid, ++ le16_to_cpu(mpi_request->FunctionDependent1)); ++ else ++ mpt2sas_base_put_smid_default(ioc, smid); ++ break; ++ } ++ case MPI2_FUNCTION_SCSI_TASK_MGMT: ++ { ++ Mpi2SCSITaskManagementRequest_t *tm_request = ++ (Mpi2SCSITaskManagementRequest_t *)request; ++ ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "TASK_MGMT: handle(0x%04x), task_type(0x%02x)\n", ++ ioc->name, ++ le16_to_cpu(tm_request->DevHandle), tm_request->TaskType)); ++ ++ if (tm_request->TaskType == ++ MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK || ++ tm_request->TaskType == ++ MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK) { ++ if (_ctl_set_task_mid(ioc, &karg, tm_request)) { ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ } ++ ++ mpt2sas_scsih_set_tm_flag(ioc, le16_to_cpu( ++ tm_request->DevHandle)); ++ ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz, ++ data_in_dma, data_in_sz); ++ mpt2sas_base_put_smid_hi_priority(ioc, smid, 0); ++ break; ++ } ++ case MPI2_FUNCTION_SMP_PASSTHROUGH: ++ { ++ Mpi2SmpPassthroughRequest_t *smp_request = ++ (Mpi2SmpPassthroughRequest_t *)mpi_request; ++ u8 *data; ++ ++ /* ioc determines which port to use */ ++ smp_request->PhysicalPort = 0xFF; ++ if (smp_request->PassthroughFlags & ++ MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE) ++ data = (u8 *)&smp_request->SGL; ++ else { ++ if (unlikely(data_out == NULL)) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ mpt2sas_base_free_smid(ioc, smid); ++ ret = -EINVAL; ++ goto out; ++ } ++ data = data_out; ++ } ++ ++ if (data[1] == 0x91 && (data[10] == 1 || data[10] == 2)) { ++ ioc->ioc_link_reset_in_progress = 1; ++ ioc->ignore_loginfos = 1; ++ } ++ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma, ++ data_in_sz); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ break; ++ } ++ case MPI2_FUNCTION_SATA_PASSTHROUGH: ++ case MPI2_FUNCTION_FW_DOWNLOAD: ++ case MPI2_FUNCTION_FW_UPLOAD: ++ { ++ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma, ++ data_in_sz); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ break; ++ } ++ case MPI2_FUNCTION_TOOLBOX: ++ { ++ Mpi2ToolboxCleanRequest_t *toolbox_request = ++ (Mpi2ToolboxCleanRequest_t *)mpi_request; ++ ++ if (toolbox_request->Tool == MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL) { ++ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, ++ data_in_dma, data_in_sz); ++ } else { ++ ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz, ++ data_in_dma, data_in_sz); ++ } ++ mpt2sas_base_put_smid_default(ioc, smid); ++ break; ++ } ++ case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: ++ { ++ Mpi2SasIoUnitControlRequest_t *sasiounit_request = ++ (Mpi2SasIoUnitControlRequest_t *)mpi_request; ++ ++ if (sasiounit_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET ++ || sasiounit_request->Operation == ++ MPI2_SAS_OP_PHY_LINK_RESET) { ++ ioc->ioc_link_reset_in_progress = 1; ++ ioc->ignore_loginfos = 1; ++ } ++ /* drop to default case for posting the request */ ++ } ++ default: ++ ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz, ++ data_in_dma, data_in_sz); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ break; ++ } ++ ++ if (karg.timeout < MPT3_IOCTL_DEFAULT_TIMEOUT) ++ timeout = MPT3_IOCTL_DEFAULT_TIMEOUT; ++ else ++ timeout = karg.timeout; ++ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, ++ timeout*HZ); ++ if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { ++ Mpi2SCSITaskManagementRequest_t *tm_request = ++ (Mpi2SCSITaskManagementRequest_t *)mpi_request; ++ mpt2sas_scsih_clear_tm_flag(ioc, le16_to_cpu( ++ tm_request->DevHandle)); ++ mpt2sas_trigger_master(ioc, MASTER_TRIGGER_TASK_MANAGMENT); ++ } else if ((mpi_request->Function == MPI2_FUNCTION_SMP_PASSTHROUGH || ++ mpi_request->Function == MPI2_FUNCTION_SAS_IO_UNIT_CONTROL) && ++ ioc->ioc_link_reset_in_progress) { ++ ioc->ioc_link_reset_in_progress = 0; ++ ioc->ignore_loginfos = 0; ++ } ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, ++ __func__); ++ _debug_dump_mf(mpi_request, karg.data_sge_offset); ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ goto issue_host_reset; ++ } ++ ++ mpi_reply = ioc->ctl_cmds.reply; ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; ++ ++ if (mpi_reply->Function == MPI2_FUNCTION_SCSI_TASK_MGMT && ++ (ioc->logging_level & MPT_DEBUG_TM)) { ++ Mpi2SCSITaskManagementReply_t *tm_reply = ++ (Mpi2SCSITaskManagementReply_t *)mpi_reply; ++ ++ pr_info(MPT3SAS_FMT "TASK_MGMT: " \ ++ "IOCStatus(0x%04x), IOCLogInfo(0x%08x), " ++ "TerminationCount(0x%08x)\n", ioc->name, ++ le16_to_cpu(tm_reply->IOCStatus), ++ le32_to_cpu(tm_reply->IOCLogInfo), ++ le32_to_cpu(tm_reply->TerminationCount)); ++ } ++ ++ /* copy out xdata to user */ ++ if (data_in_sz) { ++ if (copy_to_user(karg.data_in_buf_ptr, data_in, ++ data_in_sz)) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ ret = -ENODATA; ++ goto out; ++ } ++ } ++ ++ /* copy out reply message frame to user */ ++ if (karg.max_reply_bytes) { ++ sz = min_t(u32, karg.max_reply_bytes, ioc->reply_sz); ++ if (copy_to_user(karg.reply_frame_buf_ptr, ioc->ctl_cmds.reply, ++ sz)) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ ret = -ENODATA; ++ goto out; ++ } ++ } ++ ++ /* copy out sense to user */ ++ if (karg.max_sense_bytes && (mpi_request->Function == ++ MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function == ++ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { ++ sz = min_t(u32, karg.max_sense_bytes, SCSI_SENSE_BUFFERSIZE); ++ if (copy_to_user(karg.sense_data_ptr, ioc->ctl_cmds.sense, ++ sz)) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ ret = -ENODATA; ++ goto out; ++ } ++ } ++ ++ issue_host_reset: ++ if (issue_reset) { ++ ret = -ENODATA; ++ if ((mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || ++ mpi_request->Function == ++ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH || ++ mpi_request->Function == MPI2_FUNCTION_SATA_PASSTHROUGH)) { ++ pr_info(MPT3SAS_FMT "issue target reset: handle = (0x%04x)\n", ++ ioc->name, ++ le16_to_cpu(mpi_request->FunctionDependent1)); ++ mpt2sas_halt_firmware(ioc); ++ mpt2sas_scsih_issue_tm(ioc, ++ le16_to_cpu(mpi_request->FunctionDependent1), 0, 0, ++ 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30, ++ TM_MUTEX_ON); ++ } else ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ } ++ ++ out: ++ ++ /* free memory associated with sg buffers */ ++ if (data_in) ++ pci_free_consistent(ioc->pdev, data_in_sz, data_in, ++ data_in_dma); ++ ++ if (data_out) ++ pci_free_consistent(ioc->pdev, data_out_sz, data_out, ++ data_out_dma); ++ ++ kfree(mpi_request); ++ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED; ++ return ret; ++} ++ ++/** ++ * _ctl_getiocinfo - main handler for MPT3IOCINFO opcode ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ */ ++static long ++_ctl_getiocinfo(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_ioctl_iocinfo karg; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name, ++ __func__)); ++ ++ memset(&karg, 0 , sizeof(karg)); ++ if (ioc->pfacts) ++ karg.port_number = ioc->pfacts[0].PortNumber; ++ karg.hw_rev = ioc->pdev->revision; ++ karg.pci_id = ioc->pdev->device; ++ karg.subsystem_device = ioc->pdev->subsystem_device; ++ karg.subsystem_vendor = ioc->pdev->subsystem_vendor; ++ karg.pci_information.u.bits.bus = ioc->pdev->bus->number; ++ karg.pci_information.u.bits.device = PCI_SLOT(ioc->pdev->devfn); ++ karg.pci_information.u.bits.function = PCI_FUNC(ioc->pdev->devfn); ++ karg.pci_information.segment_id = pci_domain_nr(ioc->pdev->bus); ++ karg.firmware_version = ioc->facts.FWVersion.Word; ++ strcpy(karg.driver_version, ioc->driver_name); ++ strcat(karg.driver_version, "-"); ++ switch (ioc->hba_mpi_version_belonged) { ++ case MPI2_VERSION: ++ if (ioc->is_warpdrive) ++ karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2_SSS6200; ++ else ++ karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2; ++ strcat(karg.driver_version, MPT2SAS_DRIVER_VERSION); ++ break; ++ case MPI25_VERSION: ++ case MPI26_VERSION: ++ karg.adapter_type = MPT3_IOCTL_INTERFACE_SAS3; ++ strcat(karg.driver_version, MPT3SAS_DRIVER_VERSION); ++ break; ++ } ++ karg.bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion); ++ ++ if (copy_to_user(arg, &karg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ return 0; ++} ++ ++/** ++ * _ctl_eventquery - main handler for MPT3EVENTQUERY opcode ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ */ ++static long ++_ctl_eventquery(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_ioctl_eventquery karg; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name, ++ __func__)); ++ ++ karg.event_entries = MPT3SAS_CTL_EVENT_LOG_SIZE; ++ memcpy(karg.event_types, ioc->event_type, ++ MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32)); ++ ++ if (copy_to_user(arg, &karg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ return 0; ++} ++ ++/** ++ * _ctl_eventenable - main handler for MPT3EVENTENABLE opcode ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ */ ++static long ++_ctl_eventenable(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_ioctl_eventenable karg; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name, ++ __func__)); ++ ++ memcpy(ioc->event_type, karg.event_types, ++ MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32)); ++ mpt2sas_base_validate_event_type(ioc, ioc->event_type); ++ ++ if (ioc->event_log) ++ return 0; ++ /* initialize event_log */ ++ ioc->event_context = 0; ++ ioc->aen_event_read_flag = 0; ++ ioc->event_log = kcalloc(MPT3SAS_CTL_EVENT_LOG_SIZE, ++ sizeof(struct MPT3_IOCTL_EVENTS), GFP_KERNEL); ++ if (!ioc->event_log) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -ENOMEM; ++ } ++ return 0; ++} ++ ++/** ++ * _ctl_eventreport - main handler for MPT3EVENTREPORT opcode ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ */ ++static long ++_ctl_eventreport(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_ioctl_eventreport karg; ++ u32 number_bytes, max_events, max; ++ struct mpt3_ioctl_eventreport __user *uarg = arg; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name, ++ __func__)); ++ ++ number_bytes = karg.hdr.max_data_size - ++ sizeof(struct mpt3_ioctl_header); ++ max_events = number_bytes/sizeof(struct MPT3_IOCTL_EVENTS); ++ max = min_t(u32, MPT3SAS_CTL_EVENT_LOG_SIZE, max_events); ++ ++ /* If fewer than 1 event is requested, there must have ++ * been some type of error. ++ */ ++ if (!max || !ioc->event_log) ++ return -ENODATA; ++ ++ number_bytes = max * sizeof(struct MPT3_IOCTL_EVENTS); ++ if (copy_to_user(uarg->event_data, ioc->event_log, number_bytes)) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ /* reset flag so SIGIO can restart */ ++ ioc->aen_event_read_flag = 0; ++ return 0; ++} ++ ++/** ++ * _ctl_do_reset - main handler for MPT3HARDRESET opcode ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ */ ++static long ++_ctl_do_reset(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_ioctl_diag_reset karg; ++ int retval; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery || ++ ioc->is_driver_loading) ++ return -EAGAIN; ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name, ++ __func__)); ++ ++ retval = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ pr_info(MPT3SAS_FMT "host reset: %s\n", ++ ioc->name, ((!retval) ? "SUCCESS" : "FAILED")); ++ return 0; ++} ++ ++/** ++ * _ctl_btdh_search_sas_device - searching for sas device ++ * @ioc: per adapter object ++ * @btdh: btdh ioctl payload ++ */ ++static int ++_ctl_btdh_search_sas_device(struct MPT3SAS_ADAPTER *ioc, ++ struct mpt3_ioctl_btdh_mapping *btdh) ++{ ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ int rc = 0; ++ ++ if (list_empty(&ioc->sas_device_list)) ++ return rc; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ list_for_each_entry(sas_device, &ioc->sas_device_list, list) { ++ if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF && ++ btdh->handle == sas_device->handle) { ++ btdh->bus = sas_device->channel; ++ btdh->id = sas_device->id; ++ rc = 1; ++ goto out; ++ } else if (btdh->bus == sas_device->channel && btdh->id == ++ sas_device->id && btdh->handle == 0xFFFF) { ++ btdh->handle = sas_device->handle; ++ rc = 1; ++ goto out; ++ } ++ } ++ out: ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ return rc; ++} ++ ++/** ++ * _ctl_btdh_search_raid_device - searching for raid device ++ * @ioc: per adapter object ++ * @btdh: btdh ioctl payload ++ */ ++static int ++_ctl_btdh_search_raid_device(struct MPT3SAS_ADAPTER *ioc, ++ struct mpt3_ioctl_btdh_mapping *btdh) ++{ ++ struct _raid_device *raid_device; ++ unsigned long flags; ++ int rc = 0; ++ ++ if (list_empty(&ioc->raid_device_list)) ++ return rc; ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ list_for_each_entry(raid_device, &ioc->raid_device_list, list) { ++ if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF && ++ btdh->handle == raid_device->handle) { ++ btdh->bus = raid_device->channel; ++ btdh->id = raid_device->id; ++ rc = 1; ++ goto out; ++ } else if (btdh->bus == raid_device->channel && btdh->id == ++ raid_device->id && btdh->handle == 0xFFFF) { ++ btdh->handle = raid_device->handle; ++ rc = 1; ++ goto out; ++ } ++ } ++ out: ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ return rc; ++} ++ ++/** ++ * _ctl_btdh_mapping - main handler for MPT3BTDHMAPPING opcode ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ */ ++static long ++_ctl_btdh_mapping(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_ioctl_btdh_mapping karg; ++ int rc; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ rc = _ctl_btdh_search_sas_device(ioc, &karg); ++ if (!rc) ++ _ctl_btdh_search_raid_device(ioc, &karg); ++ ++ if (copy_to_user(arg, &karg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ return 0; ++} ++ ++/** ++ * _ctl_diag_capability - return diag buffer capability ++ * @ioc: per adapter object ++ * @buffer_type: specifies either TRACE, SNAPSHOT, or EXTENDED ++ * ++ * returns 1 when diag buffer support is enabled in firmware ++ */ ++static u8 ++_ctl_diag_capability(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type) ++{ ++ u8 rc = 0; ++ ++ switch (buffer_type) { ++ case MPI2_DIAG_BUF_TYPE_TRACE: ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) ++ rc = 1; ++ break; ++ case MPI2_DIAG_BUF_TYPE_SNAPSHOT: ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) ++ rc = 1; ++ break; ++ case MPI2_DIAG_BUF_TYPE_EXTENDED: ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) ++ rc = 1; ++ } ++ ++ return rc; ++} ++ ++ ++/** ++ * _ctl_diag_register_2 - wrapper for registering diag buffer support ++ * @ioc: per adapter object ++ * @diag_register: the diag_register struct passed in from user space ++ * ++ */ ++static long ++_ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc, ++ struct mpt3_diag_register *diag_register) ++{ ++ int rc, i; ++ void *request_data = NULL; ++ dma_addr_t request_data_dma; ++ u32 request_data_sz = 0; ++ Mpi2DiagBufferPostRequest_t *mpi_request; ++ Mpi2DiagBufferPostReply_t *mpi_reply; ++ u8 buffer_type; ++ unsigned long timeleft; ++ u16 smid; ++ u16 ioc_status; ++ u32 ioc_state; ++ u8 issue_reset = 0; ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: ctl_cmd in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ buffer_type = diag_register->buffer_type; ++ if (!_ctl_diag_capability(ioc, buffer_type)) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have capability for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -EPERM; ++ } ++ ++ if (ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) { ++ pr_err(MPT3SAS_FMT ++ "%s: already has a registered buffer for buffer_type(0x%02x)\n", ++ ioc->name, __func__, ++ buffer_type); ++ return -EINVAL; ++ } ++ ++ if (diag_register->requested_buffer_size % 4) { ++ pr_err(MPT3SAS_FMT ++ "%s: the requested_buffer_size is not 4 byte aligned\n", ++ ioc->name, __func__); ++ return -EINVAL; ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ rc = 0; ++ ioc->ctl_cmds.status = MPT3_CMD_PENDING; ++ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->ctl_cmds.smid = smid; ++ ++ request_data = ioc->diag_buffer[buffer_type]; ++ request_data_sz = diag_register->requested_buffer_size; ++ ioc->unique_id[buffer_type] = diag_register->unique_id; ++ ioc->diag_buffer_status[buffer_type] = 0; ++ memcpy(ioc->product_specific[buffer_type], ++ diag_register->product_specific, MPT3_PRODUCT_SPECIFIC_DWORDS); ++ ioc->diagnostic_flags[buffer_type] = diag_register->diagnostic_flags; ++ ++ if (request_data) { ++ request_data_dma = ioc->diag_buffer_dma[buffer_type]; ++ if (request_data_sz != ioc->diag_buffer_sz[buffer_type]) { ++ pci_free_consistent(ioc->pdev, ++ ioc->diag_buffer_sz[buffer_type], ++ request_data, request_data_dma); ++ request_data = NULL; ++ } ++ } ++ ++ if (request_data == NULL) { ++ ioc->diag_buffer_sz[buffer_type] = 0; ++ ioc->diag_buffer_dma[buffer_type] = 0; ++ request_data = pci_alloc_consistent( ++ ioc->pdev, request_data_sz, &request_data_dma); ++ if (request_data == NULL) { ++ pr_err(MPT3SAS_FMT "%s: failed allocating memory" \ ++ " for diag buffers, requested size(%d)\n", ++ ioc->name, __func__, request_data_sz); ++ mpt2sas_base_free_smid(ioc, smid); ++ return -ENOMEM; ++ } ++ ioc->diag_buffer[buffer_type] = request_data; ++ ioc->diag_buffer_sz[buffer_type] = request_data_sz; ++ ioc->diag_buffer_dma[buffer_type] = request_data_dma; ++ } ++ ++ mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST; ++ mpi_request->BufferType = diag_register->buffer_type; ++ mpi_request->Flags = cpu_to_le32(diag_register->diagnostic_flags); ++ mpi_request->BufferAddress = cpu_to_le64(request_data_dma); ++ mpi_request->BufferLength = cpu_to_le32(request_data_sz); ++ mpi_request->VF_ID = 0; /* TODO */ ++ mpi_request->VP_ID = 0; ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: diag_buffer(0x%p), dma(0x%llx), sz(%d)\n", ++ ioc->name, __func__, request_data, ++ (unsigned long long)request_data_dma, ++ le32_to_cpu(mpi_request->BufferLength))); ++ ++ for (i = 0; i < MPT3_PRODUCT_SPECIFIC_DWORDS; i++) ++ mpi_request->ProductSpecific[i] = ++ cpu_to_le32(ioc->product_specific[buffer_type][i]); ++ ++ init_completion(&ioc->ctl_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, ++ MPT3_IOCTL_DEFAULT_TIMEOUT*HZ); ++ ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, ++ __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2DiagBufferPostRequest_t)/4); ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ goto issue_host_reset; ++ } ++ ++ /* process the completed Reply Message Frame */ ++ if ((ioc->ctl_cmds.status & MPT3_CMD_REPLY_VALID) == 0) { ++ pr_err(MPT3SAS_FMT "%s: no reply message\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ++ mpi_reply = ioc->ctl_cmds.reply; ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; ++ ++ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ++ ioc->diag_buffer_status[buffer_type] |= ++ MPT3_DIAG_BUFFER_IS_REGISTERED; ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: success\n", ++ ioc->name, __func__)); ++ } else { ++ pr_info(MPT3SAS_FMT ++ "%s: ioc_status(0x%04x) log_info(0x%08x)\n", ++ ioc->name, __func__, ++ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); ++ rc = -EFAULT; ++ } ++ ++ issue_host_reset: ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ ++ out: ++ ++ if (rc && request_data) ++ pci_free_consistent(ioc->pdev, request_data_sz, ++ request_data, request_data_dma); ++ ++ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED; ++ return rc; ++} ++ ++/** ++ * mpt2sas_enable_diag_buffer - enabling diag_buffers support driver load time ++ * @ioc: per adapter object ++ * @bits_to_register: bitwise field where trace is bit 0, and snapshot is bit 1 ++ * ++ * This is called when command line option diag_buffer_enable is enabled ++ * at driver load time. ++ */ ++void ++mpt2sas_enable_diag_buffer(struct MPT3SAS_ADAPTER *ioc, u8 bits_to_register) ++{ ++ struct mpt3_diag_register diag_register; ++ ++ memset(&diag_register, 0, sizeof(struct mpt3_diag_register)); ++ ++ if (bits_to_register & 1) { ++ pr_info(MPT3SAS_FMT "registering trace buffer support\n", ++ ioc->name); ++ ioc->diag_trigger_master.MasterData = ++ (MASTER_TRIGGER_FW_FAULT + MASTER_TRIGGER_ADAPTER_RESET); ++ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE; ++ /* register for 2MB buffers */ ++ diag_register.requested_buffer_size = 2 * (1024 * 1024); ++ diag_register.unique_id = 0x7075900; ++ _ctl_diag_register_2(ioc, &diag_register); ++ } ++ ++ if (bits_to_register & 2) { ++ pr_info(MPT3SAS_FMT "registering snapshot buffer support\n", ++ ioc->name); ++ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_SNAPSHOT; ++ /* register for 2MB buffers */ ++ diag_register.requested_buffer_size = 2 * (1024 * 1024); ++ diag_register.unique_id = 0x7075901; ++ _ctl_diag_register_2(ioc, &diag_register); ++ } ++ ++ if (bits_to_register & 4) { ++ pr_info(MPT3SAS_FMT "registering extended buffer support\n", ++ ioc->name); ++ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_EXTENDED; ++ /* register for 2MB buffers */ ++ diag_register.requested_buffer_size = 2 * (1024 * 1024); ++ diag_register.unique_id = 0x7075901; ++ _ctl_diag_register_2(ioc, &diag_register); ++ } ++} ++ ++/** ++ * _ctl_diag_register - application register with driver ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ * ++ * This will allow the driver to setup any required buffers that will be ++ * needed by firmware to communicate with the driver. ++ */ ++static long ++_ctl_diag_register(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_diag_register karg; ++ long rc; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ rc = _ctl_diag_register_2(ioc, &karg); ++ return rc; ++} ++ ++/** ++ * _ctl_diag_unregister - application unregister with driver ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ * ++ * This will allow the driver to cleanup any memory allocated for diag ++ * messages and to free up any resources. ++ */ ++static long ++_ctl_diag_unregister(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_diag_unregister karg; ++ void *request_data; ++ dma_addr_t request_data_dma; ++ u32 request_data_sz; ++ u8 buffer_type; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ buffer_type = karg.unique_id & 0x000000ff; ++ if (!_ctl_diag_capability(ioc, buffer_type)) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have capability for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -EPERM; ++ } ++ ++ if ((ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ pr_err(MPT3SAS_FMT ++ "%s: buffer_type(0x%02x) is not registered\n", ++ ioc->name, __func__, buffer_type); ++ return -EINVAL; ++ } ++ if ((ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) == 0) { ++ pr_err(MPT3SAS_FMT ++ "%s: buffer_type(0x%02x) has not been released\n", ++ ioc->name, __func__, buffer_type); ++ return -EINVAL; ++ } ++ ++ if (karg.unique_id != ioc->unique_id[buffer_type]) { ++ pr_err(MPT3SAS_FMT ++ "%s: unique_id(0x%08x) is not registered\n", ++ ioc->name, __func__, karg.unique_id); ++ return -EINVAL; ++ } ++ ++ request_data = ioc->diag_buffer[buffer_type]; ++ if (!request_data) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have memory allocated for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -ENOMEM; ++ } ++ ++ request_data_sz = ioc->diag_buffer_sz[buffer_type]; ++ request_data_dma = ioc->diag_buffer_dma[buffer_type]; ++ pci_free_consistent(ioc->pdev, request_data_sz, ++ request_data, request_data_dma); ++ ioc->diag_buffer[buffer_type] = NULL; ++ ioc->diag_buffer_status[buffer_type] = 0; ++ return 0; ++} ++ ++/** ++ * _ctl_diag_query - query relevant info associated with diag buffers ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ * ++ * The application will send only buffer_type and unique_id. Driver will ++ * inspect unique_id first, if valid, fill in all the info. If unique_id is ++ * 0x00, the driver will return info specified by Buffer Type. ++ */ ++static long ++_ctl_diag_query(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_diag_query karg; ++ void *request_data; ++ int i; ++ u8 buffer_type; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ karg.application_flags = 0; ++ buffer_type = karg.buffer_type; ++ ++ if (!_ctl_diag_capability(ioc, buffer_type)) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have capability for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -EPERM; ++ } ++ ++ if ((ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ pr_err(MPT3SAS_FMT ++ "%s: buffer_type(0x%02x) is not registered\n", ++ ioc->name, __func__, buffer_type); ++ return -EINVAL; ++ } ++ ++ if (karg.unique_id & 0xffffff00) { ++ if (karg.unique_id != ioc->unique_id[buffer_type]) { ++ pr_err(MPT3SAS_FMT ++ "%s: unique_id(0x%08x) is not registered\n", ++ ioc->name, __func__, karg.unique_id); ++ return -EINVAL; ++ } ++ } ++ ++ request_data = ioc->diag_buffer[buffer_type]; ++ if (!request_data) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have buffer for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -ENOMEM; ++ } ++ ++ if (ioc->diag_buffer_status[buffer_type] & MPT3_DIAG_BUFFER_IS_RELEASED) ++ karg.application_flags = (MPT3_APP_FLAGS_APP_OWNED | ++ MPT3_APP_FLAGS_BUFFER_VALID); ++ else ++ karg.application_flags = (MPT3_APP_FLAGS_APP_OWNED | ++ MPT3_APP_FLAGS_BUFFER_VALID | ++ MPT3_APP_FLAGS_FW_BUFFER_ACCESS); ++ ++ for (i = 0; i < MPT3_PRODUCT_SPECIFIC_DWORDS; i++) ++ karg.product_specific[i] = ++ ioc->product_specific[buffer_type][i]; ++ ++ karg.total_buffer_size = ioc->diag_buffer_sz[buffer_type]; ++ karg.driver_added_buffer_size = 0; ++ karg.unique_id = ioc->unique_id[buffer_type]; ++ karg.diagnostic_flags = ioc->diagnostic_flags[buffer_type]; ++ ++ if (copy_to_user(arg, &karg, sizeof(struct mpt3_diag_query))) { ++ pr_err(MPT3SAS_FMT ++ "%s: unable to write mpt3_diag_query data @ %p\n", ++ ioc->name, __func__, arg); ++ return -EFAULT; ++ } ++ return 0; ++} ++ ++/** ++ * mpt2sas_send_diag_release - Diag Release Message ++ * @ioc: per adapter object ++ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED ++ * @issue_reset - specifies whether host reset is required. ++ * ++ */ ++int ++mpt2sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type, ++ u8 *issue_reset) ++{ ++ Mpi2DiagReleaseRequest_t *mpi_request; ++ Mpi2DiagReleaseReply_t *mpi_reply; ++ u16 smid; ++ u16 ioc_status; ++ u32 ioc_state; ++ int rc; ++ unsigned long timeleft; ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ rc = 0; ++ *issue_reset = 0; ++ ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) ++ ioc->diag_buffer_status[buffer_type] |= ++ MPT3_DIAG_BUFFER_IS_RELEASED; ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: skipping due to FAULT state\n", ioc->name, ++ __func__)); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: ctl_cmd in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ ioc->ctl_cmds.status = MPT3_CMD_PENDING; ++ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->ctl_cmds.smid = smid; ++ ++ mpi_request->Function = MPI2_FUNCTION_DIAG_RELEASE; ++ mpi_request->BufferType = buffer_type; ++ mpi_request->VF_ID = 0; /* TODO */ ++ mpi_request->VP_ID = 0; ++ ++ init_completion(&ioc->ctl_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, ++ MPT3_IOCTL_DEFAULT_TIMEOUT*HZ); ++ ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, ++ __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2DiagReleaseRequest_t)/4); ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_RESET)) ++ *issue_reset = 1; ++ rc = -EFAULT; ++ goto out; ++ } ++ ++ /* process the completed Reply Message Frame */ ++ if ((ioc->ctl_cmds.status & MPT3_CMD_REPLY_VALID) == 0) { ++ pr_err(MPT3SAS_FMT "%s: no reply message\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ++ mpi_reply = ioc->ctl_cmds.reply; ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; ++ ++ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ++ ioc->diag_buffer_status[buffer_type] |= ++ MPT3_DIAG_BUFFER_IS_RELEASED; ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: success\n", ++ ioc->name, __func__)); ++ } else { ++ pr_info(MPT3SAS_FMT ++ "%s: ioc_status(0x%04x) log_info(0x%08x)\n", ++ ioc->name, __func__, ++ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); ++ rc = -EFAULT; ++ } ++ ++ out: ++ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED; ++ return rc; ++} ++ ++/** ++ * _ctl_diag_release - request to send Diag Release Message to firmware ++ * @arg - user space buffer containing ioctl content ++ * ++ * This allows ownership of the specified buffer to returned to the driver, ++ * allowing an application to read the buffer without fear that firmware is ++ * overwritting information in the buffer. ++ */ ++static long ++_ctl_diag_release(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_diag_release karg; ++ void *request_data; ++ int rc; ++ u8 buffer_type; ++ u8 issue_reset = 0; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ buffer_type = karg.unique_id & 0x000000ff; ++ if (!_ctl_diag_capability(ioc, buffer_type)) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have capability for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -EPERM; ++ } ++ ++ if ((ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ pr_err(MPT3SAS_FMT ++ "%s: buffer_type(0x%02x) is not registered\n", ++ ioc->name, __func__, buffer_type); ++ return -EINVAL; ++ } ++ ++ if (karg.unique_id != ioc->unique_id[buffer_type]) { ++ pr_err(MPT3SAS_FMT ++ "%s: unique_id(0x%08x) is not registered\n", ++ ioc->name, __func__, karg.unique_id); ++ return -EINVAL; ++ } ++ ++ if (ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) { ++ pr_err(MPT3SAS_FMT ++ "%s: buffer_type(0x%02x) is already released\n", ++ ioc->name, __func__, ++ buffer_type); ++ return 0; ++ } ++ ++ request_data = ioc->diag_buffer[buffer_type]; ++ ++ if (!request_data) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have memory allocated for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -ENOMEM; ++ } ++ ++ /* buffers were released by due to host reset */ ++ if ((ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_DIAG_RESET)) { ++ ioc->diag_buffer_status[buffer_type] |= ++ MPT3_DIAG_BUFFER_IS_RELEASED; ++ ioc->diag_buffer_status[buffer_type] &= ++ ~MPT3_DIAG_BUFFER_IS_DIAG_RESET; ++ pr_err(MPT3SAS_FMT ++ "%s: buffer_type(0x%02x) was released due to host reset\n", ++ ioc->name, __func__, buffer_type); ++ return 0; ++ } ++ ++ rc = mpt2sas_send_diag_release(ioc, buffer_type, &issue_reset); ++ ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ ++ return rc; ++} ++ ++/** ++ * _ctl_diag_read_buffer - request for copy of the diag buffer ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ */ ++static long ++_ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_diag_read_buffer karg; ++ struct mpt3_diag_read_buffer __user *uarg = arg; ++ void *request_data, *diag_data; ++ Mpi2DiagBufferPostRequest_t *mpi_request; ++ Mpi2DiagBufferPostReply_t *mpi_reply; ++ int rc, i; ++ u8 buffer_type; ++ unsigned long timeleft, request_size, copy_size; ++ u16 smid; ++ u16 ioc_status; ++ u8 issue_reset = 0; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ buffer_type = karg.unique_id & 0x000000ff; ++ if (!_ctl_diag_capability(ioc, buffer_type)) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have capability for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -EPERM; ++ } ++ ++ if (karg.unique_id != ioc->unique_id[buffer_type]) { ++ pr_err(MPT3SAS_FMT ++ "%s: unique_id(0x%08x) is not registered\n", ++ ioc->name, __func__, karg.unique_id); ++ return -EINVAL; ++ } ++ ++ request_data = ioc->diag_buffer[buffer_type]; ++ if (!request_data) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have buffer for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -ENOMEM; ++ } ++ ++ request_size = ioc->diag_buffer_sz[buffer_type]; ++ ++ if ((karg.starting_offset % 4) || (karg.bytes_to_read % 4)) { ++ pr_err(MPT3SAS_FMT "%s: either the starting_offset " \ ++ "or bytes_to_read are not 4 byte aligned\n", ioc->name, ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (karg.starting_offset > request_size) ++ return -EINVAL; ++ ++ diag_data = (void *)(request_data + karg.starting_offset); ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: diag_buffer(%p), offset(%d), sz(%d)\n", ++ ioc->name, __func__, ++ diag_data, karg.starting_offset, karg.bytes_to_read)); ++ ++ /* Truncate data on requests that are too large */ ++ if ((diag_data + karg.bytes_to_read < diag_data) || ++ (diag_data + karg.bytes_to_read > request_data + request_size)) ++ copy_size = request_size - karg.starting_offset; ++ else ++ copy_size = karg.bytes_to_read; ++ ++ if (copy_to_user((void __user *)uarg->diagnostic_data, ++ diag_data, copy_size)) { ++ pr_err(MPT3SAS_FMT ++ "%s: Unable to write mpt_diag_read_buffer_t data @ %p\n", ++ ioc->name, __func__, diag_data); ++ return -EFAULT; ++ } ++ ++ if ((karg.flags & MPT3_FLAGS_REREGISTER) == 0) ++ return 0; ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: Reregister buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type)); ++ if ((ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) == 0) { ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: buffer_type(0x%02x) is still registered\n", ++ ioc->name, __func__, buffer_type)); ++ return 0; ++ } ++ /* Get a free request frame and save the message context. ++ */ ++ ++ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: ctl_cmd in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ rc = 0; ++ ioc->ctl_cmds.status = MPT3_CMD_PENDING; ++ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->ctl_cmds.smid = smid; ++ ++ mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST; ++ mpi_request->BufferType = buffer_type; ++ mpi_request->BufferLength = ++ cpu_to_le32(ioc->diag_buffer_sz[buffer_type]); ++ mpi_request->BufferAddress = ++ cpu_to_le64(ioc->diag_buffer_dma[buffer_type]); ++ for (i = 0; i < MPT3_PRODUCT_SPECIFIC_DWORDS; i++) ++ mpi_request->ProductSpecific[i] = ++ cpu_to_le32(ioc->product_specific[buffer_type][i]); ++ mpi_request->VF_ID = 0; /* TODO */ ++ mpi_request->VP_ID = 0; ++ ++ init_completion(&ioc->ctl_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, ++ MPT3_IOCTL_DEFAULT_TIMEOUT*HZ); ++ ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, ++ __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2DiagBufferPostRequest_t)/4); ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ goto issue_host_reset; ++ } ++ ++ /* process the completed Reply Message Frame */ ++ if ((ioc->ctl_cmds.status & MPT3_CMD_REPLY_VALID) == 0) { ++ pr_err(MPT3SAS_FMT "%s: no reply message\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ++ mpi_reply = ioc->ctl_cmds.reply; ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; ++ ++ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ++ ioc->diag_buffer_status[buffer_type] |= ++ MPT3_DIAG_BUFFER_IS_REGISTERED; ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: success\n", ++ ioc->name, __func__)); ++ } else { ++ pr_info(MPT3SAS_FMT ++ "%s: ioc_status(0x%04x) log_info(0x%08x)\n", ++ ioc->name, __func__, ++ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); ++ rc = -EFAULT; ++ } ++ ++ issue_host_reset: ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ ++ out: ++ ++ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED; ++ return rc; ++} ++ ++ ++ ++#ifdef CONFIG_COMPAT ++/** ++ * _ctl_compat_mpt_command - convert 32bit pointers to 64bit. ++ * @ioc: per adapter object ++ * @cmd - ioctl opcode ++ * @arg - (struct mpt3_ioctl_command32) ++ * ++ * MPT3COMMAND32 - Handle 32bit applications running on 64bit os. ++ */ ++static long ++_ctl_compat_mpt_command(struct MPT3SAS_ADAPTER *ioc, unsigned cmd, ++ void __user *arg) ++{ ++ struct mpt3_ioctl_command32 karg32; ++ struct mpt3_ioctl_command32 __user *uarg; ++ struct mpt3_ioctl_command karg; ++ ++ if (_IOC_SIZE(cmd) != sizeof(struct mpt3_ioctl_command32)) ++ return -EINVAL; ++ ++ uarg = (struct mpt3_ioctl_command32 __user *) arg; ++ ++ if (copy_from_user(&karg32, (char __user *)arg, sizeof(karg32))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ memset(&karg, 0, sizeof(struct mpt3_ioctl_command)); ++ karg.hdr.ioc_number = karg32.hdr.ioc_number; ++ karg.hdr.port_number = karg32.hdr.port_number; ++ karg.hdr.max_data_size = karg32.hdr.max_data_size; ++ karg.timeout = karg32.timeout; ++ karg.max_reply_bytes = karg32.max_reply_bytes; ++ karg.data_in_size = karg32.data_in_size; ++ karg.data_out_size = karg32.data_out_size; ++ karg.max_sense_bytes = karg32.max_sense_bytes; ++ karg.data_sge_offset = karg32.data_sge_offset; ++ karg.reply_frame_buf_ptr = compat_ptr(karg32.reply_frame_buf_ptr); ++ karg.data_in_buf_ptr = compat_ptr(karg32.data_in_buf_ptr); ++ karg.data_out_buf_ptr = compat_ptr(karg32.data_out_buf_ptr); ++ karg.sense_data_ptr = compat_ptr(karg32.sense_data_ptr); ++ return _ctl_do_mpt_command(ioc, karg, &uarg->mf); ++} ++#endif ++ ++/** ++ * _ctl_ioctl_main - main ioctl entry point ++ * @file - (struct file) ++ * @cmd - ioctl opcode ++ * @arg - user space data buffer ++ * @compat - handles 32 bit applications in 64bit os ++ * @mpi_version: will be MPI2_VERSION for mpt2ctl ioctl device & ++ * MPI25_VERSION | MPI26_VERSION for mpt3ctl ioctl device. ++ */ ++static long ++_ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg, ++ u8 compat, u16 mpi_version) ++{ ++ struct MPT3SAS_ADAPTER *ioc; ++ struct mpt3_ioctl_header ioctl_header; ++ enum block_state state; ++ long ret = -EINVAL; ++ ++ /* get IOCTL header */ ++ if (copy_from_user(&ioctl_header, (char __user *)arg, ++ sizeof(struct mpt3_ioctl_header))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ if (_ctl_verify_adapter(ioctl_header.ioc_number, ++ &ioc, mpi_version) == -1 || !ioc) ++ return -ENODEV; ++ ++ /* pci_access_mutex lock acquired by ioctl path */ ++ mutex_lock(&ioc->pci_access_mutex); ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery || ++ ioc->is_driver_loading || ioc->remove_host) { ++ ret = -EAGAIN; ++ goto out_unlock_pciaccess; ++ } ++ ++ state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING; ++ if (state == NON_BLOCKING) { ++ if (!mutex_trylock(&ioc->ctl_cmds.mutex)) { ++ ret = -EAGAIN; ++ goto out_unlock_pciaccess; ++ } ++ } else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) { ++ ret = -ERESTARTSYS; ++ goto out_unlock_pciaccess; ++ } ++ ++ ++ switch (cmd) { ++ case MPT3IOCINFO: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_iocinfo)) ++ ret = _ctl_getiocinfo(ioc, arg); ++ break; ++#ifdef CONFIG_COMPAT ++ case MPT3COMMAND32: ++#endif ++ case MPT3COMMAND: ++ { ++ struct mpt3_ioctl_command __user *uarg; ++ struct mpt3_ioctl_command karg; ++ ++#ifdef CONFIG_COMPAT ++ if (compat) { ++ ret = _ctl_compat_mpt_command(ioc, cmd, arg); ++ break; ++ } ++#endif ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ ret = -EFAULT; ++ break; ++ } ++ ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_command)) { ++ uarg = arg; ++ ret = _ctl_do_mpt_command(ioc, karg, &uarg->mf); ++ } ++ break; ++ } ++ case MPT3EVENTQUERY: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_eventquery)) ++ ret = _ctl_eventquery(ioc, arg); ++ break; ++ case MPT3EVENTENABLE: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_eventenable)) ++ ret = _ctl_eventenable(ioc, arg); ++ break; ++ case MPT3EVENTREPORT: ++ ret = _ctl_eventreport(ioc, arg); ++ break; ++ case MPT3HARDRESET: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_diag_reset)) ++ ret = _ctl_do_reset(ioc, arg); ++ break; ++ case MPT3BTDHMAPPING: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_btdh_mapping)) ++ ret = _ctl_btdh_mapping(ioc, arg); ++ break; ++ case MPT3DIAGREGISTER: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_register)) ++ ret = _ctl_diag_register(ioc, arg); ++ break; ++ case MPT3DIAGUNREGISTER: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_unregister)) ++ ret = _ctl_diag_unregister(ioc, arg); ++ break; ++ case MPT3DIAGQUERY: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_query)) ++ ret = _ctl_diag_query(ioc, arg); ++ break; ++ case MPT3DIAGRELEASE: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_release)) ++ ret = _ctl_diag_release(ioc, arg); ++ break; ++ case MPT3DIAGREADBUFFER: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_read_buffer)) ++ ret = _ctl_diag_read_buffer(ioc, arg); ++ break; ++ default: ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "unsupported ioctl opcode(0x%08x)\n", ioc->name, cmd)); ++ break; ++ } ++ ++ mutex_unlock(&ioc->ctl_cmds.mutex); ++out_unlock_pciaccess: ++ mutex_unlock(&ioc->pci_access_mutex); ++ return ret; ++} ++ ++/** ++ * _ctl_ioctl_mpt2sas - mpt3ctl main ioctl entry point (unlocked) ++ * @file - (struct file) ++ * @cmd - ioctl opcode ++ * @arg - ++ */ ++long ++_ctl_ioctl_mpt2sas(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long ret; ++ ++ /* pass MPI25_VERSION | MPI26_VERSION value, ++ * to indicate that this ioctl cmd ++ * came from mpt3ctl ioctl device. ++ */ ++ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 0, ++ MPI25_VERSION | MPI26_VERSION); ++ return ret; ++} ++ ++/** ++ * _ctl_mpt2_ioctl_mpt2sas - mpt2ctl main ioctl entry point (unlocked) ++ * @file - (struct file) ++ * @cmd - ioctl opcode ++ * @arg - ++ */ ++long ++_ctl_mpt2_ioctl_mpt2sas(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long ret; ++ ++ /* pass MPI2_VERSION value, to indicate that this ioctl cmd ++ * came from mpt2ctl ioctl device. ++ */ ++ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 0, MPI2_VERSION); ++ return ret; ++} ++#ifdef CONFIG_COMPAT ++/** ++ *_ ctl_ioctl_compat - main ioctl entry point (compat) ++ * @file - ++ * @cmd - ++ * @arg - ++ * ++ * This routine handles 32 bit applications in 64bit os. ++ */ ++long ++_ctl_ioctl_compat_mpt2sas(struct file *file, unsigned cmd, unsigned long arg) ++{ ++ long ret; ++ ++ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 1, ++ MPI25_VERSION | MPI26_VERSION); ++ return ret; ++} ++ ++/** ++ *_ ctl_mpt2_ioctl_compat - main ioctl entry point (compat) ++ * @file - ++ * @cmd - ++ * @arg - ++ * ++ * This routine handles 32 bit applications in 64bit os. ++ */ ++long ++_ctl_mpt2_ioctl_compat_mpt2sas(struct file *file, unsigned cmd, unsigned long arg) ++{ ++ long ret; ++ ++ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 1, MPI2_VERSION); ++ return ret; ++} ++#endif ++ ++/* scsi host attributes */ ++/** ++ * _ctl_version_fw_show - firmware version ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_version_fw_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n", ++ (ioc->facts.FWVersion.Word & 0xFF000000) >> 24, ++ (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16, ++ (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8, ++ ioc->facts.FWVersion.Word & 0x000000FF); ++} ++static DEVICE_ATTR(version_fw, S_IRUGO, _ctl_version_fw_show, NULL); ++ ++/** ++ * _ctl_version_bios_show - bios version ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_version_bios_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ u32 version = le32_to_cpu(ioc->bios_pg3.BiosVersion); ++ ++ return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n", ++ (version & 0xFF000000) >> 24, ++ (version & 0x00FF0000) >> 16, ++ (version & 0x0000FF00) >> 8, ++ version & 0x000000FF); ++} ++static DEVICE_ATTR(version_bios, S_IRUGO, _ctl_version_bios_show, NULL); ++ ++/** ++ * _ctl_version_mpi_show - MPI (message passing interface) version ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_version_mpi_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%03x.%02x\n", ++ ioc->facts.MsgVersion, ioc->facts.HeaderVersion >> 8); ++} ++static DEVICE_ATTR(version_mpi, S_IRUGO, _ctl_version_mpi_show, NULL); ++ ++/** ++ * _ctl_version_product_show - product name ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_version_product_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.ChipName); ++} ++static DEVICE_ATTR(version_product, S_IRUGO, _ctl_version_product_show, NULL); ++ ++/** ++ * _ctl_version_nvdata_persistent_show - ndvata persistent version ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_version_nvdata_persistent_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%08xh\n", ++ le32_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word)); ++} ++static DEVICE_ATTR(version_nvdata_persistent, S_IRUGO, ++ _ctl_version_nvdata_persistent_show, NULL); ++ ++/** ++ * _ctl_version_nvdata_default_show - nvdata default version ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_version_nvdata_default_show(struct device *cdev, struct device_attribute ++ *attr, char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%08xh\n", ++ le32_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word)); ++} ++static DEVICE_ATTR(version_nvdata_default, S_IRUGO, ++ _ctl_version_nvdata_default_show, NULL); ++ ++/** ++ * _ctl_board_name_show - board name ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_board_name_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardName); ++} ++static DEVICE_ATTR(board_name, S_IRUGO, _ctl_board_name_show, NULL); ++ ++/** ++ * _ctl_board_assembly_show - board assembly name ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_board_assembly_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardAssembly); ++} ++static DEVICE_ATTR(board_assembly, S_IRUGO, _ctl_board_assembly_show, NULL); ++ ++/** ++ * _ctl_board_tracer_show - board tracer number ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_board_tracer_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardTracerNumber); ++} ++static DEVICE_ATTR(board_tracer, S_IRUGO, _ctl_board_tracer_show, NULL); ++ ++/** ++ * _ctl_io_delay_show - io missing delay ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is for firmware implemention for deboucing device ++ * removal events. ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_io_delay_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->io_missing_delay); ++} ++static DEVICE_ATTR(io_delay, S_IRUGO, _ctl_io_delay_show, NULL); ++ ++/** ++ * _ctl_device_delay_show - device missing delay ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is for firmware implemention for deboucing device ++ * removal events. ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_device_delay_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->device_missing_delay); ++} ++static DEVICE_ATTR(device_delay, S_IRUGO, _ctl_device_delay_show, NULL); ++ ++/** ++ * _ctl_fw_queue_depth_show - global credits ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is firmware queue depth limit ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_fw_queue_depth_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->facts.RequestCredit); ++} ++static DEVICE_ATTR(fw_queue_depth, S_IRUGO, _ctl_fw_queue_depth_show, NULL); ++ ++/** ++ * _ctl_sas_address_show - sas address ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is the controller sas address ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_host_sas_address_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++ ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "0x%016llx\n", ++ (unsigned long long)ioc->sas_hba.sas_address); ++} ++static DEVICE_ATTR(host_sas_address, S_IRUGO, ++ _ctl_host_sas_address_show, NULL); ++ ++/** ++ * _ctl_logging_level_show - logging level ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_logging_level_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%08xh\n", ioc->logging_level); ++} ++static ssize_t ++_ctl_logging_level_store(struct device *cdev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ int val = 0; ++ ++ if (sscanf(buf, "%x", &val) != 1) ++ return -EINVAL; ++ ++ ioc->logging_level = val; ++ pr_info(MPT3SAS_FMT "logging_level=%08xh\n", ioc->name, ++ ioc->logging_level); ++ return strlen(buf); ++} ++static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR, _ctl_logging_level_show, ++ _ctl_logging_level_store); ++ ++/** ++ * _ctl_fwfault_debug_show - show/store fwfault_debug ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * mpt2sas_fwfault_debug is command line option ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_fwfault_debug_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", ioc->fwfault_debug); ++} ++static ssize_t ++_ctl_fwfault_debug_store(struct device *cdev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ int val = 0; ++ ++ if (sscanf(buf, "%d", &val) != 1) ++ return -EINVAL; ++ ++ ioc->fwfault_debug = val; ++ pr_info(MPT3SAS_FMT "fwfault_debug=%d\n", ioc->name, ++ ioc->fwfault_debug); ++ return strlen(buf); ++} ++static DEVICE_ATTR(fwfault_debug, S_IRUGO | S_IWUSR, ++ _ctl_fwfault_debug_show, _ctl_fwfault_debug_store); ++ ++/** ++ * _ctl_ioc_reset_count_show - ioc reset count ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is firmware queue depth limit ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_ioc_reset_count_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", ioc->ioc_reset_count); ++} ++static DEVICE_ATTR(ioc_reset_count, S_IRUGO, _ctl_ioc_reset_count_show, NULL); ++ ++/** ++ * _ctl_ioc_reply_queue_count_show - number of reply queues ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is number of reply queues ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_ioc_reply_queue_count_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++{ ++ u8 reply_queue_count; ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ if ((ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable) ++ reply_queue_count = ioc->reply_queue_count; ++ else ++ reply_queue_count = 1; ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", reply_queue_count); ++} ++static DEVICE_ATTR(reply_queue_count, S_IRUGO, _ctl_ioc_reply_queue_count_show, ++ NULL); ++ ++/** ++ * _ctl_BRM_status_show - Backup Rail Monitor Status ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is number of reply queues ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ Mpi2IOUnitPage3_t *io_unit_pg3 = NULL; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 backup_rail_monitor_status = 0; ++ u16 ioc_status; ++ int sz; ++ ssize_t rc = 0; ++ ++ if (!ioc->is_warpdrive) { ++ pr_err(MPT3SAS_FMT "%s: BRM attribute is only for" ++ " warpdrive\n", ioc->name, __func__); ++ goto out; ++ } ++ /* pci_access_mutex lock acquired by sysfs show path */ ++ mutex_lock(&ioc->pci_access_mutex); ++ if (ioc->pci_error_recovery || ioc->remove_host) { ++ mutex_unlock(&ioc->pci_access_mutex); ++ return 0; ++ } ++ ++ /* allocate upto GPIOVal 36 entries */ ++ sz = offsetof(Mpi2IOUnitPage3_t, GPIOVal) + (sizeof(u16) * 36); ++ io_unit_pg3 = kzalloc(sz, GFP_KERNEL); ++ if (!io_unit_pg3) { ++ pr_err(MPT3SAS_FMT "%s: failed allocating memory " ++ "for iounit_pg3: (%d) bytes\n", ioc->name, __func__, sz); ++ goto out; ++ } ++ ++ if (mpt2sas_config_get_iounit_pg3(ioc, &mpi_reply, io_unit_pg3, sz) != ++ 0) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed reading iounit_pg3\n", ioc->name, ++ __func__); ++ goto out; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "%s: iounit_pg3 failed with " ++ "ioc_status(0x%04x)\n", ioc->name, __func__, ioc_status); ++ goto out; ++ } ++ ++ if (io_unit_pg3->GPIOCount < 25) { ++ pr_err(MPT3SAS_FMT "%s: iounit_pg3->GPIOCount less than " ++ "25 entries, detected (%d) entries\n", ioc->name, __func__, ++ io_unit_pg3->GPIOCount); ++ goto out; ++ } ++ ++ /* BRM status is in bit zero of GPIOVal[24] */ ++ backup_rail_monitor_status = le16_to_cpu(io_unit_pg3->GPIOVal[24]); ++ rc = snprintf(buf, PAGE_SIZE, "%d\n", (backup_rail_monitor_status & 1)); ++ ++ out: ++ kfree(io_unit_pg3); ++ mutex_unlock(&ioc->pci_access_mutex); ++ return rc; ++} ++static DEVICE_ATTR(BRM_status, S_IRUGO, _ctl_BRM_status_show, NULL); ++ ++struct DIAG_BUFFER_START { ++ __le32 Size; ++ __le32 DiagVersion; ++ u8 BufferType; ++ u8 Reserved[3]; ++ __le32 Reserved1; ++ __le32 Reserved2; ++ __le32 Reserved3; ++}; ++ ++/** ++ * _ctl_host_trace_buffer_size_show - host buffer size (trace only) ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_host_trace_buffer_size_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ u32 size = 0; ++ struct DIAG_BUFFER_START *request_data; ++ ++ if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) { ++ pr_err(MPT3SAS_FMT ++ "%s: host_trace_buffer is not registered\n", ++ ioc->name, __func__); ++ return 0; ++ } ++ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ pr_err(MPT3SAS_FMT ++ "%s: host_trace_buffer is not registered\n", ++ ioc->name, __func__); ++ return 0; ++ } ++ ++ request_data = (struct DIAG_BUFFER_START *) ++ ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]; ++ if ((le32_to_cpu(request_data->DiagVersion) == 0x00000000 || ++ le32_to_cpu(request_data->DiagVersion) == 0x01000000 || ++ le32_to_cpu(request_data->DiagVersion) == 0x01010000) && ++ le32_to_cpu(request_data->Reserved3) == 0x4742444c) ++ size = le32_to_cpu(request_data->Size); ++ ++ ioc->ring_buffer_sz = size; ++ return snprintf(buf, PAGE_SIZE, "%d\n", size); ++} ++static DEVICE_ATTR(host_trace_buffer_size, S_IRUGO, ++ _ctl_host_trace_buffer_size_show, NULL); ++ ++/** ++ * _ctl_host_trace_buffer_show - firmware ring buffer (trace only) ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ * ++ * You will only be able to read 4k bytes of ring buffer at a time. ++ * In order to read beyond 4k bytes, you will have to write out the ++ * offset to the same attribute, it will move the pointer. ++ */ ++static ssize_t ++_ctl_host_trace_buffer_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ void *request_data; ++ u32 size; ++ ++ if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) { ++ pr_err(MPT3SAS_FMT ++ "%s: host_trace_buffer is not registered\n", ++ ioc->name, __func__); ++ return 0; ++ } ++ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ pr_err(MPT3SAS_FMT ++ "%s: host_trace_buffer is not registered\n", ++ ioc->name, __func__); ++ return 0; ++ } ++ ++ if (ioc->ring_buffer_offset > ioc->ring_buffer_sz) ++ return 0; ++ ++ size = ioc->ring_buffer_sz - ioc->ring_buffer_offset; ++ size = (size >= PAGE_SIZE) ? (PAGE_SIZE - 1) : size; ++ request_data = ioc->diag_buffer[0] + ioc->ring_buffer_offset; ++ memcpy(buf, request_data, size); ++ return size; ++} ++ ++static ssize_t ++_ctl_host_trace_buffer_store(struct device *cdev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ int val = 0; ++ ++ if (sscanf(buf, "%d", &val) != 1) ++ return -EINVAL; ++ ++ ioc->ring_buffer_offset = val; ++ return strlen(buf); ++} ++static DEVICE_ATTR(host_trace_buffer, S_IRUGO | S_IWUSR, ++ _ctl_host_trace_buffer_show, _ctl_host_trace_buffer_store); ++ ++ ++/*****************************************/ ++ ++/** ++ * _ctl_host_trace_buffer_enable_show - firmware ring buffer (trace only) ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ * ++ * This is a mechnism to post/release host_trace_buffers ++ */ ++static ssize_t ++_ctl_host_trace_buffer_enable_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ if ((!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) || ++ ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0)) ++ return snprintf(buf, PAGE_SIZE, "off\n"); ++ else if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED)) ++ return snprintf(buf, PAGE_SIZE, "release\n"); ++ else ++ return snprintf(buf, PAGE_SIZE, "post\n"); ++} ++ ++static ssize_t ++_ctl_host_trace_buffer_enable_store(struct device *cdev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ char str[10] = ""; ++ struct mpt3_diag_register diag_register; ++ u8 issue_reset = 0; ++ ++ /* don't allow post/release occurr while recovery is active */ ++ if (ioc->shost_recovery || ioc->remove_host || ++ ioc->pci_error_recovery || ioc->is_driver_loading) ++ return -EBUSY; ++ ++ if (sscanf(buf, "%9s", str) != 1) ++ return -EINVAL; ++ ++ if (!strcmp(str, "post")) { ++ /* exit out if host buffers are already posted */ ++ if ((ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) && ++ (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) && ++ ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) == 0)) ++ goto out; ++ memset(&diag_register, 0, sizeof(struct mpt3_diag_register)); ++ pr_info(MPT3SAS_FMT "posting host trace buffers\n", ++ ioc->name); ++ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE; ++ diag_register.requested_buffer_size = (1024 * 1024); ++ diag_register.unique_id = 0x7075900; ++ ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] = 0; ++ _ctl_diag_register_2(ioc, &diag_register); ++ } else if (!strcmp(str, "release")) { ++ /* exit out if host buffers are already released */ ++ if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) ++ goto out; ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) ++ goto out; ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED)) ++ goto out; ++ pr_info(MPT3SAS_FMT "releasing host trace buffer\n", ++ ioc->name); ++ mpt2sas_send_diag_release(ioc, MPI2_DIAG_BUF_TYPE_TRACE, ++ &issue_reset); ++ } ++ ++ out: ++ return strlen(buf); ++} ++static DEVICE_ATTR(host_trace_buffer_enable, S_IRUGO | S_IWUSR, ++ _ctl_host_trace_buffer_enable_show, ++ _ctl_host_trace_buffer_enable_store); ++ ++/*********** diagnostic trigger suppport *********************************/ ++ ++/** ++ * _ctl_diag_trigger_master_show - show the diag_trigger_master attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_master_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++ ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t rc; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ rc = sizeof(struct SL_WH_MASTER_TRIGGER_T); ++ memcpy(buf, &ioc->diag_trigger_master, rc); ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return rc; ++} ++ ++/** ++ * _ctl_diag_trigger_master_store - store the diag_trigger_master attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_master_store(struct device *cdev, ++ struct device_attribute *attr, const char *buf, size_t count) ++ ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t rc; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ rc = min(sizeof(struct SL_WH_MASTER_TRIGGER_T), count); ++ memset(&ioc->diag_trigger_master, 0, ++ sizeof(struct SL_WH_MASTER_TRIGGER_T)); ++ memcpy(&ioc->diag_trigger_master, buf, rc); ++ ioc->diag_trigger_master.MasterData |= ++ (MASTER_TRIGGER_FW_FAULT + MASTER_TRIGGER_ADAPTER_RESET); ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return rc; ++} ++static DEVICE_ATTR(diag_trigger_master, S_IRUGO | S_IWUSR, ++ _ctl_diag_trigger_master_show, _ctl_diag_trigger_master_store); ++ ++ ++/** ++ * _ctl_diag_trigger_event_show - show the diag_trigger_event attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_event_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t rc; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ rc = sizeof(struct SL_WH_EVENT_TRIGGERS_T); ++ memcpy(buf, &ioc->diag_trigger_event, rc); ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return rc; ++} ++ ++/** ++ * _ctl_diag_trigger_event_store - store the diag_trigger_event attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_event_store(struct device *cdev, ++ struct device_attribute *attr, const char *buf, size_t count) ++ ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t sz; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ sz = min(sizeof(struct SL_WH_EVENT_TRIGGERS_T), count); ++ memset(&ioc->diag_trigger_event, 0, ++ sizeof(struct SL_WH_EVENT_TRIGGERS_T)); ++ memcpy(&ioc->diag_trigger_event, buf, sz); ++ if (ioc->diag_trigger_event.ValidEntries > NUM_VALID_ENTRIES) ++ ioc->diag_trigger_event.ValidEntries = NUM_VALID_ENTRIES; ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return sz; ++} ++static DEVICE_ATTR(diag_trigger_event, S_IRUGO | S_IWUSR, ++ _ctl_diag_trigger_event_show, _ctl_diag_trigger_event_store); ++ ++ ++/** ++ * _ctl_diag_trigger_scsi_show - show the diag_trigger_scsi attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_scsi_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t rc; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ rc = sizeof(struct SL_WH_SCSI_TRIGGERS_T); ++ memcpy(buf, &ioc->diag_trigger_scsi, rc); ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return rc; ++} ++ ++/** ++ * _ctl_diag_trigger_scsi_store - store the diag_trigger_scsi attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_scsi_store(struct device *cdev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t sz; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ sz = min(sizeof(struct SL_WH_SCSI_TRIGGERS_T), count); ++ memset(&ioc->diag_trigger_scsi, 0, ++ sizeof(struct SL_WH_EVENT_TRIGGERS_T)); ++ memcpy(&ioc->diag_trigger_scsi, buf, sz); ++ if (ioc->diag_trigger_scsi.ValidEntries > NUM_VALID_ENTRIES) ++ ioc->diag_trigger_scsi.ValidEntries = NUM_VALID_ENTRIES; ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return sz; ++} ++static DEVICE_ATTR(diag_trigger_scsi, S_IRUGO | S_IWUSR, ++ _ctl_diag_trigger_scsi_show, _ctl_diag_trigger_scsi_store); ++ ++ ++/** ++ * _ctl_diag_trigger_scsi_show - show the diag_trigger_mpi attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_mpi_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t rc; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ rc = sizeof(struct SL_WH_MPI_TRIGGERS_T); ++ memcpy(buf, &ioc->diag_trigger_mpi, rc); ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return rc; ++} ++ ++/** ++ * _ctl_diag_trigger_mpi_store - store the diag_trigger_mpi attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_mpi_store(struct device *cdev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t sz; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ sz = min(sizeof(struct SL_WH_MPI_TRIGGERS_T), count); ++ memset(&ioc->diag_trigger_mpi, 0, ++ sizeof(ioc->diag_trigger_mpi)); ++ memcpy(&ioc->diag_trigger_mpi, buf, sz); ++ if (ioc->diag_trigger_mpi.ValidEntries > NUM_VALID_ENTRIES) ++ ioc->diag_trigger_mpi.ValidEntries = NUM_VALID_ENTRIES; ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return sz; ++} ++ ++static DEVICE_ATTR(diag_trigger_mpi, S_IRUGO | S_IWUSR, ++ _ctl_diag_trigger_mpi_show, _ctl_diag_trigger_mpi_store); ++ ++/*********** diagnostic trigger suppport *** END ****************************/ ++ ++ ++ ++/*****************************************/ ++ ++struct device_attribute *mpt2sas_host_attrs[] = { ++ &dev_attr_version_fw, ++ &dev_attr_version_bios, ++ &dev_attr_version_mpi, ++ &dev_attr_version_product, ++ &dev_attr_version_nvdata_persistent, ++ &dev_attr_version_nvdata_default, ++ &dev_attr_board_name, ++ &dev_attr_board_assembly, ++ &dev_attr_board_tracer, ++ &dev_attr_io_delay, ++ &dev_attr_device_delay, ++ &dev_attr_logging_level, ++ &dev_attr_fwfault_debug, ++ &dev_attr_fw_queue_depth, ++ &dev_attr_host_sas_address, ++ &dev_attr_ioc_reset_count, ++ &dev_attr_host_trace_buffer_size, ++ &dev_attr_host_trace_buffer, ++ &dev_attr_host_trace_buffer_enable, ++ &dev_attr_reply_queue_count, ++ &dev_attr_diag_trigger_master, ++ &dev_attr_diag_trigger_event, ++ &dev_attr_diag_trigger_scsi, ++ &dev_attr_diag_trigger_mpi, ++ &dev_attr_BRM_status, ++ NULL, ++}; ++ ++/* device attributes */ ++ ++/** ++ * _ctl_device_sas_address_show - sas address ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is the sas address for the target ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_device_sas_address_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct scsi_device *sdev = to_scsi_device(dev); ++ struct MPT3SAS_DEVICE *sas_device_priv_data = sdev->hostdata; ++ ++ return snprintf(buf, PAGE_SIZE, "0x%016llx\n", ++ (unsigned long long)sas_device_priv_data->sas_target->sas_address); ++} ++static DEVICE_ATTR(sas_address, S_IRUGO, _ctl_device_sas_address_show, NULL); ++ ++/** ++ * _ctl_device_handle_show - device handle ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is the firmware assigned device handle ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_device_handle_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct scsi_device *sdev = to_scsi_device(dev); ++ struct MPT3SAS_DEVICE *sas_device_priv_data = sdev->hostdata; ++ ++ return snprintf(buf, PAGE_SIZE, "0x%04x\n", ++ sas_device_priv_data->sas_target->handle); ++} ++static DEVICE_ATTR(sas_device_handle, S_IRUGO, _ctl_device_handle_show, NULL); ++ ++struct device_attribute *mpt2sas_dev_attrs[] = { ++ &dev_attr_sas_address, ++ &dev_attr_sas_device_handle, ++ NULL, ++}; ++ ++/* file operations table for mpt3ctl device */ ++static const struct file_operations ctl_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = _ctl_ioctl_mpt2sas, ++ .poll = _ctl_poll_mpt2sas, ++ .fasync = _ctl_fasync_mpt2sas, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = _ctl_ioctl_compat_mpt2sas, ++#endif ++}; ++ ++/* file operations table for mpt2ctl device */ ++static const struct file_operations ctl_gen2_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = _ctl_mpt2_ioctl_mpt2sas, ++ .poll = _ctl_poll_mpt2sas, ++ .fasync = _ctl_fasync_mpt2sas, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = _ctl_mpt2_ioctl_compat_mpt2sas, ++#endif ++}; ++ ++static struct miscdevice ctl_dev = { ++ .minor = MPT3SAS_MINOR, ++ .name = MPT3SAS_DEV_NAME, ++ .fops = &ctl_fops, ++}; ++ ++static struct miscdevice gen2_ctl_dev = { ++ .minor = MPT2SAS_MINOR, ++ .name = MPT2SAS_DEV_NAME, ++ .fops = &ctl_gen2_fops, ++}; ++ ++/** ++ * mpt2sas_ctl_init - main entry point for ctl. ++ * ++ */ ++void ++mpt2sas_ctl_init(ushort hbas_to_enumerate) ++{ ++ async_queue = NULL; ++ ++ /* Don't register mpt3ctl ioctl device if ++ * hbas_to_enumarate is one. ++ */ ++ if (hbas_to_enumerate != 1) ++ if (misc_register(&ctl_dev) < 0) ++ pr_err("%s can't register misc device [minor=%d]\n", ++ MPT3SAS_DRIVER_NAME, MPT3SAS_MINOR); ++ ++ /* Don't register mpt3ctl ioctl device if ++ * hbas_to_enumarate is two. ++ */ ++ if (hbas_to_enumerate != 2) ++ if (misc_register(&gen2_ctl_dev) < 0) ++ pr_err("%s can't register misc device [minor=%d]\n", ++ MPT2SAS_DRIVER_NAME, MPT2SAS_MINOR); ++ ++ init_waitqueue_head(&ctl_poll_wait); ++} ++ ++/** ++ * mpt2sas_ctl_exit - exit point for ctl ++ * ++ */ ++void ++mpt2sas_ctl_exit(ushort hbas_to_enumerate) ++{ ++ struct MPT3SAS_ADAPTER *ioc; ++ int i; ++ ++ list_for_each_entry(ioc, &mpt2sas_ioc_list, list) { ++ ++ /* free memory associated to diag buffers */ ++ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) { ++ if (!ioc->diag_buffer[i]) ++ continue; ++ if (!(ioc->diag_buffer_status[i] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED)) ++ continue; ++ if ((ioc->diag_buffer_status[i] & ++ MPT3_DIAG_BUFFER_IS_RELEASED)) ++ continue; ++ pci_free_consistent(ioc->pdev, ioc->diag_buffer_sz[i], ++ ioc->diag_buffer[i], ioc->diag_buffer_dma[i]); ++ ioc->diag_buffer[i] = NULL; ++ ioc->diag_buffer_status[i] = 0; ++ } ++ ++ kfree(ioc->event_log); ++ } ++ if (hbas_to_enumerate != 1) ++ misc_deregister(&ctl_dev); ++ if (hbas_to_enumerate != 2) ++ misc_deregister(&gen2_ctl_dev); ++} +diff --git a/drivers/scsi/mpt2sas/mpt3sas_ctl.h b/drivers/scsi/mpt2sas/mpt3sas_ctl.h +new file mode 100644 +index 0000000..8940835 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_ctl.h +@@ -0,0 +1,423 @@ ++/* ++ * Management Module Support for MPT (Message Passing Technology) based ++ * controllers ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_ctl.h ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#ifndef MPT3SAS_CTL_H_INCLUDED ++#define MPT3SAS_CTL_H_INCLUDED ++ ++#ifdef __KERNEL__ ++#include ++#endif ++ ++#ifndef MPT2SAS_MINOR ++#define MPT2SAS_MINOR (MPT_MINOR + 1) ++#endif ++#ifndef MPT3SAS_MINOR ++#define MPT3SAS_MINOR (MPT_MINOR + 2) ++#endif ++#define MPT2SAS_DEV_NAME "mpt2ctl" ++#define MPT3SAS_DEV_NAME "mpt3ctl" ++#define MPT3_MAGIC_NUMBER 'L' ++#define MPT3_IOCTL_DEFAULT_TIMEOUT (10) /* in seconds */ ++ ++/** ++ * IOCTL opcodes ++ */ ++#define MPT3IOCINFO _IOWR(MPT3_MAGIC_NUMBER, 17, \ ++ struct mpt3_ioctl_iocinfo) ++#define MPT3COMMAND _IOWR(MPT3_MAGIC_NUMBER, 20, \ ++ struct mpt3_ioctl_command) ++#ifdef CONFIG_COMPAT ++#define MPT3COMMAND32 _IOWR(MPT3_MAGIC_NUMBER, 20, \ ++ struct mpt3_ioctl_command32) ++#endif ++#define MPT3EVENTQUERY _IOWR(MPT3_MAGIC_NUMBER, 21, \ ++ struct mpt3_ioctl_eventquery) ++#define MPT3EVENTENABLE _IOWR(MPT3_MAGIC_NUMBER, 22, \ ++ struct mpt3_ioctl_eventenable) ++#define MPT3EVENTREPORT _IOWR(MPT3_MAGIC_NUMBER, 23, \ ++ struct mpt3_ioctl_eventreport) ++#define MPT3HARDRESET _IOWR(MPT3_MAGIC_NUMBER, 24, \ ++ struct mpt3_ioctl_diag_reset) ++#define MPT3BTDHMAPPING _IOWR(MPT3_MAGIC_NUMBER, 31, \ ++ struct mpt3_ioctl_btdh_mapping) ++ ++/* diag buffer support */ ++#define MPT3DIAGREGISTER _IOWR(MPT3_MAGIC_NUMBER, 26, \ ++ struct mpt3_diag_register) ++#define MPT3DIAGRELEASE _IOWR(MPT3_MAGIC_NUMBER, 27, \ ++ struct mpt3_diag_release) ++#define MPT3DIAGUNREGISTER _IOWR(MPT3_MAGIC_NUMBER, 28, \ ++ struct mpt3_diag_unregister) ++#define MPT3DIAGQUERY _IOWR(MPT3_MAGIC_NUMBER, 29, \ ++ struct mpt3_diag_query) ++#define MPT3DIAGREADBUFFER _IOWR(MPT3_MAGIC_NUMBER, 30, \ ++ struct mpt3_diag_read_buffer) ++ ++/** ++ * struct mpt3_ioctl_header - main header structure ++ * @ioc_number - IOC unit number ++ * @port_number - IOC port number ++ * @max_data_size - maximum number bytes to transfer on read ++ */ ++struct mpt3_ioctl_header { ++ uint32_t ioc_number; ++ uint32_t port_number; ++ uint32_t max_data_size; ++}; ++ ++/** ++ * struct mpt3_ioctl_diag_reset - diagnostic reset ++ * @hdr - generic header ++ */ ++struct mpt3_ioctl_diag_reset { ++ struct mpt3_ioctl_header hdr; ++}; ++ ++ ++/** ++ * struct mpt3_ioctl_pci_info - pci device info ++ * @device - pci device id ++ * @function - pci function id ++ * @bus - pci bus id ++ * @segment_id - pci segment id ++ */ ++struct mpt3_ioctl_pci_info { ++ union { ++ struct { ++ uint32_t device:5; ++ uint32_t function:3; ++ uint32_t bus:24; ++ } bits; ++ uint32_t word; ++ } u; ++ uint32_t segment_id; ++}; ++ ++ ++#define MPT2_IOCTL_INTERFACE_SCSI (0x00) ++#define MPT2_IOCTL_INTERFACE_FC (0x01) ++#define MPT2_IOCTL_INTERFACE_FC_IP (0x02) ++#define MPT2_IOCTL_INTERFACE_SAS (0x03) ++#define MPT2_IOCTL_INTERFACE_SAS2 (0x04) ++#define MPT2_IOCTL_INTERFACE_SAS2_SSS6200 (0x05) ++#define MPT3_IOCTL_INTERFACE_SAS3 (0x06) ++#define MPT2_IOCTL_VERSION_LENGTH (32) ++ ++/** ++ * struct mpt3_ioctl_iocinfo - generic controller info ++ * @hdr - generic header ++ * @adapter_type - type of adapter (spi, fc, sas) ++ * @port_number - port number ++ * @pci_id - PCI Id ++ * @hw_rev - hardware revision ++ * @sub_system_device - PCI subsystem Device ID ++ * @sub_system_vendor - PCI subsystem Vendor ID ++ * @rsvd0 - reserved ++ * @firmware_version - firmware version ++ * @bios_version - BIOS version ++ * @driver_version - driver version - 32 ASCII characters ++ * @rsvd1 - reserved ++ * @scsi_id - scsi id of adapter 0 ++ * @rsvd2 - reserved ++ * @pci_information - pci info (2nd revision) ++ */ ++struct mpt3_ioctl_iocinfo { ++ struct mpt3_ioctl_header hdr; ++ uint32_t adapter_type; ++ uint32_t port_number; ++ uint32_t pci_id; ++ uint32_t hw_rev; ++ uint32_t subsystem_device; ++ uint32_t subsystem_vendor; ++ uint32_t rsvd0; ++ uint32_t firmware_version; ++ uint32_t bios_version; ++ uint8_t driver_version[MPT2_IOCTL_VERSION_LENGTH]; ++ uint8_t rsvd1; ++ uint8_t scsi_id; ++ uint16_t rsvd2; ++ struct mpt3_ioctl_pci_info pci_information; ++}; ++ ++ ++/* number of event log entries */ ++#define MPT3SAS_CTL_EVENT_LOG_SIZE (50) ++ ++/** ++ * struct mpt3_ioctl_eventquery - query event count and type ++ * @hdr - generic header ++ * @event_entries - number of events returned by get_event_report ++ * @rsvd - reserved ++ * @event_types - type of events currently being captured ++ */ ++struct mpt3_ioctl_eventquery { ++ struct mpt3_ioctl_header hdr; ++ uint16_t event_entries; ++ uint16_t rsvd; ++ uint32_t event_types[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; ++}; ++ ++/** ++ * struct mpt3_ioctl_eventenable - enable/disable event capturing ++ * @hdr - generic header ++ * @event_types - toggle off/on type of events to be captured ++ */ ++struct mpt3_ioctl_eventenable { ++ struct mpt3_ioctl_header hdr; ++ uint32_t event_types[4]; ++}; ++ ++#define MPT3_EVENT_DATA_SIZE (192) ++/** ++ * struct MPT3_IOCTL_EVENTS - ++ * @event - the event that was reported ++ * @context - unique value for each event assigned by driver ++ * @data - event data returned in fw reply message ++ */ ++struct MPT3_IOCTL_EVENTS { ++ uint32_t event; ++ uint32_t context; ++ uint8_t data[MPT3_EVENT_DATA_SIZE]; ++}; ++ ++/** ++ * struct mpt3_ioctl_eventreport - returing event log ++ * @hdr - generic header ++ * @event_data - (see struct MPT3_IOCTL_EVENTS) ++ */ ++struct mpt3_ioctl_eventreport { ++ struct mpt3_ioctl_header hdr; ++ struct MPT3_IOCTL_EVENTS event_data[1]; ++}; ++ ++/** ++ * struct mpt3_ioctl_command - generic mpt firmware passthru ioctl ++ * @hdr - generic header ++ * @timeout - command timeout in seconds. (if zero then use driver default ++ * value). ++ * @reply_frame_buf_ptr - reply location ++ * @data_in_buf_ptr - destination for read ++ * @data_out_buf_ptr - data source for write ++ * @sense_data_ptr - sense data location ++ * @max_reply_bytes - maximum number of reply bytes to be sent to app. ++ * @data_in_size - number bytes for data transfer in (read) ++ * @data_out_size - number bytes for data transfer out (write) ++ * @max_sense_bytes - maximum number of bytes for auto sense buffers ++ * @data_sge_offset - offset in words from the start of the request message to ++ * the first SGL ++ * @mf[1]; ++ */ ++struct mpt3_ioctl_command { ++ struct mpt3_ioctl_header hdr; ++ uint32_t timeout; ++ void __user *reply_frame_buf_ptr; ++ void __user *data_in_buf_ptr; ++ void __user *data_out_buf_ptr; ++ void __user *sense_data_ptr; ++ uint32_t max_reply_bytes; ++ uint32_t data_in_size; ++ uint32_t data_out_size; ++ uint32_t max_sense_bytes; ++ uint32_t data_sge_offset; ++ uint8_t mf[1]; ++}; ++ ++#ifdef CONFIG_COMPAT ++struct mpt3_ioctl_command32 { ++ struct mpt3_ioctl_header hdr; ++ uint32_t timeout; ++ uint32_t reply_frame_buf_ptr; ++ uint32_t data_in_buf_ptr; ++ uint32_t data_out_buf_ptr; ++ uint32_t sense_data_ptr; ++ uint32_t max_reply_bytes; ++ uint32_t data_in_size; ++ uint32_t data_out_size; ++ uint32_t max_sense_bytes; ++ uint32_t data_sge_offset; ++ uint8_t mf[1]; ++}; ++#endif ++ ++/** ++ * struct mpt3_ioctl_btdh_mapping - mapping info ++ * @hdr - generic header ++ * @id - target device identification number ++ * @bus - SCSI bus number that the target device exists on ++ * @handle - device handle for the target device ++ * @rsvd - reserved ++ * ++ * To obtain a bus/id the application sets ++ * handle to valid handle, and bus/id to 0xFFFF. ++ * ++ * To obtain the device handle the application sets ++ * bus/id valid value, and the handle to 0xFFFF. ++ */ ++struct mpt3_ioctl_btdh_mapping { ++ struct mpt3_ioctl_header hdr; ++ uint32_t id; ++ uint32_t bus; ++ uint16_t handle; ++ uint16_t rsvd; ++}; ++ ++ ++ ++/* application flags for mpt3_diag_register, mpt3_diag_query */ ++#define MPT3_APP_FLAGS_APP_OWNED (0x0001) ++#define MPT3_APP_FLAGS_BUFFER_VALID (0x0002) ++#define MPT3_APP_FLAGS_FW_BUFFER_ACCESS (0x0004) ++ ++/* flags for mpt3_diag_read_buffer */ ++#define MPT3_FLAGS_REREGISTER (0x0001) ++ ++#define MPT3_PRODUCT_SPECIFIC_DWORDS 23 ++ ++/** ++ * struct mpt3_diag_register - application register with driver ++ * @hdr - generic header ++ * @reserved - ++ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED ++ * @application_flags - misc flags ++ * @diagnostic_flags - specifies flags affecting command processing ++ * @product_specific - product specific information ++ * @requested_buffer_size - buffers size in bytes ++ * @unique_id - tag specified by application that is used to signal ownership ++ * of the buffer. ++ * ++ * This will allow the driver to setup any required buffers that will be ++ * needed by firmware to communicate with the driver. ++ */ ++struct mpt3_diag_register { ++ struct mpt3_ioctl_header hdr; ++ uint8_t reserved; ++ uint8_t buffer_type; ++ uint16_t application_flags; ++ uint32_t diagnostic_flags; ++ uint32_t product_specific[MPT3_PRODUCT_SPECIFIC_DWORDS]; ++ uint32_t requested_buffer_size; ++ uint32_t unique_id; ++}; ++ ++/** ++ * struct mpt3_diag_unregister - application unregister with driver ++ * @hdr - generic header ++ * @unique_id - tag uniquely identifies the buffer to be unregistered ++ * ++ * This will allow the driver to cleanup any memory allocated for diag ++ * messages and to free up any resources. ++ */ ++struct mpt3_diag_unregister { ++ struct mpt3_ioctl_header hdr; ++ uint32_t unique_id; ++}; ++ ++/** ++ * struct mpt3_diag_query - query relevant info associated with diag buffers ++ * @hdr - generic header ++ * @reserved - ++ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED ++ * @application_flags - misc flags ++ * @diagnostic_flags - specifies flags affecting command processing ++ * @product_specific - product specific information ++ * @total_buffer_size - diag buffer size in bytes ++ * @driver_added_buffer_size - size of extra space appended to end of buffer ++ * @unique_id - unique id associated with this buffer. ++ * ++ * The application will send only buffer_type and unique_id. Driver will ++ * inspect unique_id first, if valid, fill in all the info. If unique_id is ++ * 0x00, the driver will return info specified by Buffer Type. ++ */ ++struct mpt3_diag_query { ++ struct mpt3_ioctl_header hdr; ++ uint8_t reserved; ++ uint8_t buffer_type; ++ uint16_t application_flags; ++ uint32_t diagnostic_flags; ++ uint32_t product_specific[MPT3_PRODUCT_SPECIFIC_DWORDS]; ++ uint32_t total_buffer_size; ++ uint32_t driver_added_buffer_size; ++ uint32_t unique_id; ++}; ++ ++/** ++ * struct mpt3_diag_release - request to send Diag Release Message to firmware ++ * @hdr - generic header ++ * @unique_id - tag uniquely identifies the buffer to be released ++ * ++ * This allows ownership of the specified buffer to returned to the driver, ++ * allowing an application to read the buffer without fear that firmware is ++ * overwritting information in the buffer. ++ */ ++struct mpt3_diag_release { ++ struct mpt3_ioctl_header hdr; ++ uint32_t unique_id; ++}; ++ ++/** ++ * struct mpt3_diag_read_buffer - request for copy of the diag buffer ++ * @hdr - generic header ++ * @status - ++ * @reserved - ++ * @flags - misc flags ++ * @starting_offset - starting offset within drivers buffer where to start ++ * reading data at into the specified application buffer ++ * @bytes_to_read - number of bytes to copy from the drivers buffer into the ++ * application buffer starting at starting_offset. ++ * @unique_id - unique id associated with this buffer. ++ * @diagnostic_data - data payload ++ */ ++struct mpt3_diag_read_buffer { ++ struct mpt3_ioctl_header hdr; ++ uint8_t status; ++ uint8_t reserved; ++ uint16_t flags; ++ uint32_t starting_offset; ++ uint32_t bytes_to_read; ++ uint32_t unique_id; ++ uint32_t diagnostic_data[1]; ++}; ++ ++#endif /* MPT3SAS_CTL_H_INCLUDED */ +diff --git a/drivers/scsi/mpt2sas/mpt3sas_debug.h b/drivers/scsi/mpt2sas/mpt3sas_debug.h +new file mode 100644 +index 0000000..cceeb2c +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_debug.h +@@ -0,0 +1,206 @@ ++/* ++ * Logging Support for MPT (Message Passing Technology) based controllers ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_debug.c ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#ifndef MPT3SAS_DEBUG_H_INCLUDED ++#define MPT3SAS_DEBUG_H_INCLUDED ++ ++#define MPT_DEBUG 0x00000001 ++#define MPT_DEBUG_MSG_FRAME 0x00000002 ++#define MPT_DEBUG_SG 0x00000004 ++#define MPT_DEBUG_EVENTS 0x00000008 ++#define MPT_DEBUG_EVENT_WORK_TASK 0x00000010 ++#define MPT_DEBUG_INIT 0x00000020 ++#define MPT_DEBUG_EXIT 0x00000040 ++#define MPT_DEBUG_FAIL 0x00000080 ++#define MPT_DEBUG_TM 0x00000100 ++#define MPT_DEBUG_REPLY 0x00000200 ++#define MPT_DEBUG_HANDSHAKE 0x00000400 ++#define MPT_DEBUG_CONFIG 0x00000800 ++#define MPT_DEBUG_DL 0x00001000 ++#define MPT_DEBUG_RESET 0x00002000 ++#define MPT_DEBUG_SCSI 0x00004000 ++#define MPT_DEBUG_IOCTL 0x00008000 ++#define MPT_DEBUG_SAS 0x00020000 ++#define MPT_DEBUG_TRANSPORT 0x00040000 ++#define MPT_DEBUG_TASK_SET_FULL 0x00080000 ++ ++#define MPT_DEBUG_TRIGGER_DIAG 0x00200000 ++ ++ ++#define MPT_CHECK_LOGGING(IOC, CMD, BITS) \ ++{ \ ++ if (IOC->logging_level & BITS) \ ++ CMD; \ ++} ++ ++/* ++ * debug macros ++ */ ++ ++#define dprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG) ++ ++#define dsgprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SG) ++ ++#define devtprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EVENTS) ++ ++#define dewtprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EVENT_WORK_TASK) ++ ++#define dinitprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_INIT) ++ ++#define dexitprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EXIT) ++ ++#define dfailprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_FAIL) ++ ++#define dtmprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TM) ++ ++#define dreplyprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_REPLY) ++ ++#define dhsprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_HANDSHAKE) ++ ++#define dcprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_CONFIG) ++ ++#define ddlprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_DL) ++ ++#define drsprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_RESET) ++ ++#define dsprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SCSI) ++ ++#define dctlprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_IOCTL) ++ ++#define dsasprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SAS) ++ ++#define dsastransport(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SAS_WIDE) ++ ++#define dmfprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_MSG_FRAME) ++ ++#define dtsfprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TASK_SET_FULL) ++ ++#define dtransportprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TRANSPORT) ++ ++#define dTriggerDiagPrintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TRIGGER_DIAG) ++ ++ ++ ++/* inline functions for dumping debug data*/ ++ ++/** ++ * _debug_dump_mf - print message frame contents ++ * @mpi_request: pointer to message frame ++ * @sz: number of dwords ++ */ ++static inline void ++_debug_dump_mf(void *mpi_request, int sz) ++{ ++ int i; ++ __le32 *mfp = (__le32 *)mpi_request; ++ ++ pr_info("mf:\n\t"); ++ for (i = 0; i < sz; i++) { ++ if (i && ((i % 8) == 0)) ++ pr_info("\n\t"); ++ pr_info("%08x ", le32_to_cpu(mfp[i])); ++ } ++ pr_info("\n"); ++} ++/** ++ * _debug_dump_reply - print message frame contents ++ * @mpi_request: pointer to message frame ++ * @sz: number of dwords ++ */ ++static inline void ++_debug_dump_reply(void *mpi_request, int sz) ++{ ++ int i; ++ __le32 *mfp = (__le32 *)mpi_request; ++ ++ pr_info("reply:\n\t"); ++ for (i = 0; i < sz; i++) { ++ if (i && ((i % 8) == 0)) ++ pr_info("\n\t"); ++ pr_info("%08x ", le32_to_cpu(mfp[i])); ++ } ++ pr_info("\n"); ++} ++/** ++ * _debug_dump_config - print config page contents ++ * @mpi_request: pointer to message frame ++ * @sz: number of dwords ++ */ ++static inline void ++_debug_dump_config(void *mpi_request, int sz) ++{ ++ int i; ++ __le32 *mfp = (__le32 *)mpi_request; ++ ++ pr_info("config:\n\t"); ++ for (i = 0; i < sz; i++) { ++ if (i && ((i % 8) == 0)) ++ pr_info("\n\t"); ++ pr_info("%08x ", le32_to_cpu(mfp[i])); ++ } ++ pr_info("\n"); ++} ++ ++#endif /* MPT3SAS_DEBUG_H_INCLUDED */ +diff --git a/drivers/scsi/mpt2sas/mpt3sas_scsih.c b/drivers/scsi/mpt2sas/mpt3sas_scsih.c +new file mode 100644 +index 0000000..0a3ad2b +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_scsih.c +@@ -0,0 +1,9356 @@ ++/* ++ * Scsi Host Layer for MPT (Message Passing Technology) based controllers ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_scsih.c ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mpt3sas_base.h" ++ ++#define RAID_CHANNEL 1 ++/* forward proto's */ ++static void _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_node *sas_expander); ++static void _firmware_event_work(struct work_struct *work); ++ ++static void _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_device *sas_device); ++static int _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, ++ u8 retry_count, u8 is_pd); ++ ++static u8 _scsih_check_for_pending_tm(struct MPT3SAS_ADAPTER *ioc, u16 smid); ++ ++/* global parameters */ ++LIST_HEAD(mpt2sas_ioc_list); ++/* global ioc lock for list operations */ ++DEFINE_SPINLOCK(gioc_lock_mpt2sas); ++ ++MODULE_AUTHOR(MPT3SAS_AUTHOR); ++#ifdef MPT2SAS_SCSI ++MODULE_DESCRIPTION(MPT2SAS_DESCRIPTION); ++MODULE_VERSION(MPT2SAS_DRIVER_VERSION); ++#else ++MODULE_DESCRIPTION(MPT3SAS_DESCRIPTION); ++MODULE_VERSION(MPT3SAS_DRIVER_VERSION); ++#endif /* MPT2SAS_SCSI */ ++MODULE_LICENSE("GPL"); ++ ++/* local parameters */ ++static u8 scsi_io_cb_idx = -1; ++static u8 tm_cb_idx = -1; ++static u8 ctl_cb_idx = -1; ++static u8 base_cb_idx = -1; ++static u8 port_enable_cb_idx = -1; ++static u8 transport_cb_idx = -1; ++static u8 scsih_cb_idx = -1; ++static u8 config_cb_idx = -1; ++static int mpt2_ids; ++static int mpt3_ids; ++ ++static u8 tm_tr_cb_idx = -1 ; ++static u8 tm_tr_volume_cb_idx = -1 ; ++static u8 tm_sas_control_cb_idx = -1; ++ ++/* command line options */ ++static u32 logging_level; ++MODULE_PARM_DESC(logging_level, ++ " bits for enabling additional logging info (default=0)"); ++ ++ ++static ushort max_sectors = 0xFFFF; ++module_param(max_sectors, ushort, 0); ++MODULE_PARM_DESC(max_sectors, "max sectors, range 64 to 32767 default=32767"); ++ ++ ++static int missing_delay[2] = {-1, -1}; ++module_param_array(missing_delay, int, NULL, 0); ++MODULE_PARM_DESC(missing_delay, " device missing delay , io missing delay"); ++ ++/* scsi-mid layer global parmeter is max_report_luns, which is 511 */ ++#define MPT3SAS_MAX_LUN (16895) ++static int max_lun = MPT3SAS_MAX_LUN; ++module_param(max_lun, int, 0); ++MODULE_PARM_DESC(max_lun, " max lun, default=16895 "); ++ ++/* diag_buffer_enable is bitwise ++ * bit 0 set = TRACE ++ * bit 1 set = SNAPSHOT ++ * bit 2 set = EXTENDED ++ * ++ * Either bit can be set, or both ++ */ ++static int diag_buffer_enable = -1; ++module_param(diag_buffer_enable, int, 0); ++MODULE_PARM_DESC(diag_buffer_enable, ++ " post diag buffers (TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)"); ++static int disable_discovery = -1; ++module_param(disable_discovery, int, 0); ++MODULE_PARM_DESC(disable_discovery, " disable discovery "); ++ ++ ++/* permit overriding the host protection capabilities mask (EEDP/T10 PI) */ ++static int prot_mask = -1; ++module_param(prot_mask, int, 0); ++MODULE_PARM_DESC(prot_mask, " host protection capabilities mask, def=7 "); ++ ++ ++/* raid transport support */ ++struct raid_template *mpt3sas_raid_template_mpt2sas; ++struct raid_template *mpt2sas_raid_template_mpt2sas; ++ ++ ++/** ++ * struct sense_info - common structure for obtaining sense keys ++ * @skey: sense key ++ * @asc: additional sense code ++ * @ascq: additional sense code qualifier ++ */ ++struct sense_info { ++ u8 skey; ++ u8 asc; ++ u8 ascq; ++}; ++ ++#define MPT3SAS_PROCESS_TRIGGER_DIAG (0xFFFB) ++#define MPT3SAS_TURN_ON_PFA_LED (0xFFFC) ++#define MPT3SAS_PORT_ENABLE_COMPLETE (0xFFFD) ++#define MPT3SAS_ABRT_TASK_SET (0xFFFE) ++#define MPT3SAS_REMOVE_UNRESPONDING_DEVICES (0xFFFF) ++/** ++ * struct fw_event_work - firmware event struct ++ * @list: link list framework ++ * @work: work object (ioc->fault_reset_work_q) ++ * @ioc: per adapter object ++ * @device_handle: device handle ++ * @VF_ID: virtual function id ++ * @VP_ID: virtual port id ++ * @ignore: flag meaning this event has been marked to ignore ++ * @event: firmware event MPI2_EVENT_XXX defined in mpi2_ioc.h ++ * @refcount: kref for this event ++ * @event_data: reply event data payload follows ++ * ++ * This object stored on ioc->fw_event_list. ++ */ ++struct fw_event_work { ++ struct list_head list; ++ struct work_struct work; ++ ++ struct MPT3SAS_ADAPTER *ioc; ++ u16 device_handle; ++ u8 VF_ID; ++ u8 VP_ID; ++ u8 ignore; ++ u16 event; ++ struct kref refcount; ++ char event_data[0] __aligned(4); ++}; ++ ++static void fw_event_work_free(struct kref *r) ++{ ++ kfree(container_of(r, struct fw_event_work, refcount)); ++} ++ ++static void fw_event_work_get(struct fw_event_work *fw_work) ++{ ++ kref_get(&fw_work->refcount); ++} ++ ++static void fw_event_work_put(struct fw_event_work *fw_work) ++{ ++ kref_put(&fw_work->refcount, fw_event_work_free); ++} ++ ++static struct fw_event_work *alloc_fw_event_work(int len) ++{ ++ struct fw_event_work *fw_event; ++ ++ fw_event = kzalloc(sizeof(*fw_event) + len, GFP_ATOMIC); ++ if (!fw_event) ++ return NULL; ++ ++ kref_init(&fw_event->refcount); ++ return fw_event; ++} ++ ++/** ++ * struct _scsi_io_transfer - scsi io transfer ++ * @handle: sas device handle (assigned by firmware) ++ * @is_raid: flag set for hidden raid components ++ * @dir: DMA_TO_DEVICE, DMA_FROM_DEVICE, ++ * @data_length: data transfer length ++ * @data_dma: dma pointer to data ++ * @sense: sense data ++ * @lun: lun number ++ * @cdb_length: cdb length ++ * @cdb: cdb contents ++ * @timeout: timeout for this command ++ * @VF_ID: virtual function id ++ * @VP_ID: virtual port id ++ * @valid_reply: flag set for reply message ++ * @sense_length: sense length ++ * @ioc_status: ioc status ++ * @scsi_state: scsi state ++ * @scsi_status: scsi staus ++ * @log_info: log information ++ * @transfer_length: data length transfer when there is a reply message ++ * ++ * Used for sending internal scsi commands to devices within this module. ++ * Refer to _scsi_send_scsi_io(). ++ */ ++struct _scsi_io_transfer { ++ u16 handle; ++ u8 is_raid; ++ enum dma_data_direction dir; ++ u32 data_length; ++ dma_addr_t data_dma; ++ u8 sense[SCSI_SENSE_BUFFERSIZE]; ++ u32 lun; ++ u8 cdb_length; ++ u8 cdb[32]; ++ u8 timeout; ++ u8 VF_ID; ++ u8 VP_ID; ++ u8 valid_reply; ++ /* the following bits are only valid when 'valid_reply = 1' */ ++ u32 sense_length; ++ u16 ioc_status; ++ u8 scsi_state; ++ u8 scsi_status; ++ u32 log_info; ++ u32 transfer_length; ++}; ++ ++/** ++ * _scsih_set_debug_level - global setting of ioc->logging_level. ++ * ++ * Note: The logging levels are defined in mpt3sas_debug.h. ++ */ ++static int ++_scsih_set_debug_level(const char *val, struct kernel_param *kp) ++{ ++ int ret = param_set_int(val, kp); ++ struct MPT3SAS_ADAPTER *ioc; ++ ++ if (ret) ++ return ret; ++ ++ pr_info("setting logging_level(0x%08x)\n", logging_level); ++ spin_lock(&gioc_lock_mpt2sas); ++ list_for_each_entry(ioc, &mpt2sas_ioc_list, list) ++ ioc->logging_level = logging_level; ++ spin_unlock(&gioc_lock_mpt2sas); ++ return 0; ++} ++module_param_call(logging_level, _scsih_set_debug_level, param_get_int, ++ &logging_level, 0644); ++ ++/** ++ * _scsih_srch_boot_sas_address - search based on sas_address ++ * @sas_address: sas address ++ * @boot_device: boot device object from bios page 2 ++ * ++ * Returns 1 when there's a match, 0 means no match. ++ */ ++static inline int ++_scsih_srch_boot_sas_address(u64 sas_address, ++ Mpi2BootDeviceSasWwid_t *boot_device) ++{ ++ return (sas_address == le64_to_cpu(boot_device->SASAddress)) ? 1 : 0; ++} ++ ++/** ++ * _scsih_srch_boot_device_name - search based on device name ++ * @device_name: device name specified in INDENTIFY fram ++ * @boot_device: boot device object from bios page 2 ++ * ++ * Returns 1 when there's a match, 0 means no match. ++ */ ++static inline int ++_scsih_srch_boot_device_name(u64 device_name, ++ Mpi2BootDeviceDeviceName_t *boot_device) ++{ ++ return (device_name == le64_to_cpu(boot_device->DeviceName)) ? 1 : 0; ++} ++ ++/** ++ * _scsih_srch_boot_encl_slot - search based on enclosure_logical_id/slot ++ * @enclosure_logical_id: enclosure logical id ++ * @slot_number: slot number ++ * @boot_device: boot device object from bios page 2 ++ * ++ * Returns 1 when there's a match, 0 means no match. ++ */ ++static inline int ++_scsih_srch_boot_encl_slot(u64 enclosure_logical_id, u16 slot_number, ++ Mpi2BootDeviceEnclosureSlot_t *boot_device) ++{ ++ return (enclosure_logical_id == le64_to_cpu(boot_device-> ++ EnclosureLogicalID) && slot_number == le16_to_cpu(boot_device-> ++ SlotNumber)) ? 1 : 0; ++} ++ ++/** ++ * _scsih_is_boot_device - search for matching boot device. ++ * @sas_address: sas address ++ * @device_name: device name specified in INDENTIFY fram ++ * @enclosure_logical_id: enclosure logical id ++ * @slot_number: slot number ++ * @form: specifies boot device form ++ * @boot_device: boot device object from bios page 2 ++ * ++ * Returns 1 when there's a match, 0 means no match. ++ */ ++static int ++_scsih_is_boot_device(u64 sas_address, u64 device_name, ++ u64 enclosure_logical_id, u16 slot, u8 form, ++ Mpi2BiosPage2BootDevice_t *boot_device) ++{ ++ int rc = 0; ++ ++ switch (form) { ++ case MPI2_BIOSPAGE2_FORM_SAS_WWID: ++ if (!sas_address) ++ break; ++ rc = _scsih_srch_boot_sas_address( ++ sas_address, &boot_device->SasWwid); ++ break; ++ case MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT: ++ if (!enclosure_logical_id) ++ break; ++ rc = _scsih_srch_boot_encl_slot( ++ enclosure_logical_id, ++ slot, &boot_device->EnclosureSlot); ++ break; ++ case MPI2_BIOSPAGE2_FORM_DEVICE_NAME: ++ if (!device_name) ++ break; ++ rc = _scsih_srch_boot_device_name( ++ device_name, &boot_device->DeviceName); ++ break; ++ case MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED: ++ break; ++ } ++ ++ return rc; ++} ++ ++/** ++ * _scsih_get_sas_address - set the sas_address for given device handle ++ * @handle: device handle ++ * @sas_address: sas address ++ * ++ * Returns 0 success, non-zero when failure ++ */ ++static int ++_scsih_get_sas_address(struct MPT3SAS_ADAPTER *ioc, u16 handle, ++ u64 *sas_address) ++{ ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u32 ioc_status; ++ ++ *sas_address = 0; ++ ++ if (handle <= ioc->sas_hba.num_phys) { ++ *sas_address = ioc->sas_hba.sas_address; ++ return 0; ++ } ++ ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, ++ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__); ++ return -ENXIO; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; ++ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ++ *sas_address = le64_to_cpu(sas_device_pg0.SASAddress); ++ return 0; ++ } ++ ++ /* we hit this becuase the given parent handle doesn't exist */ ++ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) ++ return -ENXIO; ++ ++ /* else error case */ ++ pr_err(MPT3SAS_FMT ++ "handle(0x%04x), ioc_status(0x%04x), failure at %s:%d/%s()!\n", ++ ioc->name, handle, ioc_status, ++ __FILE__, __LINE__, __func__); ++ return -EIO; ++} ++ ++/** ++ * _scsih_determine_boot_device - determine boot device. ++ * @ioc: per adapter object ++ * @device: either sas_device or raid_device object ++ * @is_raid: [flag] 1 = raid object, 0 = sas object ++ * ++ * Determines whether this device should be first reported device to ++ * to scsi-ml or sas transport, this purpose is for persistent boot device. ++ * There are primary, alternate, and current entries in bios page 2. The order ++ * priority is primary, alternate, then current. This routine saves ++ * the corresponding device object and is_raid flag in the ioc object. ++ * The saved data to be used later in _scsih_probe_boot_devices(). ++ */ ++static void ++_scsih_determine_boot_device(struct MPT3SAS_ADAPTER *ioc, ++ void *device, u8 is_raid) ++{ ++ struct _sas_device *sas_device; ++ struct _raid_device *raid_device; ++ u64 sas_address; ++ u64 device_name; ++ u64 enclosure_logical_id; ++ u16 slot; ++ ++ /* only process this function when driver loads */ ++ if (!ioc->is_driver_loading) ++ return; ++ ++ /* no Bios, return immediately */ ++ if (!ioc->bios_pg3.BiosVersion) ++ return; ++ ++ if (!is_raid) { ++ sas_device = device; ++ sas_address = sas_device->sas_address; ++ device_name = sas_device->device_name; ++ enclosure_logical_id = sas_device->enclosure_logical_id; ++ slot = sas_device->slot; ++ } else { ++ raid_device = device; ++ sas_address = raid_device->wwid; ++ device_name = 0; ++ enclosure_logical_id = 0; ++ slot = 0; ++ } ++ ++ if (!ioc->req_boot_device.device) { ++ if (_scsih_is_boot_device(sas_address, device_name, ++ enclosure_logical_id, slot, ++ (ioc->bios_pg2.ReqBootDeviceForm & ++ MPI2_BIOSPAGE2_FORM_MASK), ++ &ioc->bios_pg2.RequestedBootDevice)) { ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: req_boot_device(0x%016llx)\n", ++ ioc->name, __func__, ++ (unsigned long long)sas_address)); ++ ioc->req_boot_device.device = device; ++ ioc->req_boot_device.is_raid = is_raid; ++ } ++ } ++ ++ if (!ioc->req_alt_boot_device.device) { ++ if (_scsih_is_boot_device(sas_address, device_name, ++ enclosure_logical_id, slot, ++ (ioc->bios_pg2.ReqAltBootDeviceForm & ++ MPI2_BIOSPAGE2_FORM_MASK), ++ &ioc->bios_pg2.RequestedAltBootDevice)) { ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: req_alt_boot_device(0x%016llx)\n", ++ ioc->name, __func__, ++ (unsigned long long)sas_address)); ++ ioc->req_alt_boot_device.device = device; ++ ioc->req_alt_boot_device.is_raid = is_raid; ++ } ++ } ++ ++ if (!ioc->current_boot_device.device) { ++ if (_scsih_is_boot_device(sas_address, device_name, ++ enclosure_logical_id, slot, ++ (ioc->bios_pg2.CurrentBootDeviceForm & ++ MPI2_BIOSPAGE2_FORM_MASK), ++ &ioc->bios_pg2.CurrentBootDevice)) { ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: current_boot_device(0x%016llx)\n", ++ ioc->name, __func__, ++ (unsigned long long)sas_address)); ++ ioc->current_boot_device.device = device; ++ ioc->current_boot_device.is_raid = is_raid; ++ } ++ } ++} ++ ++static struct _sas_device * ++__mpt2sas_get_sdev_from_target(struct MPT3SAS_ADAPTER *ioc, ++ struct MPT3SAS_TARGET *tgt_priv) ++{ ++ struct _sas_device *ret; ++ ++ assert_spin_locked(&ioc->sas_device_lock); ++ ++ ret = tgt_priv->sdev; ++ if (ret) ++ sas_device_get(ret); ++ ++ return ret; ++} ++ ++static struct _sas_device * ++mpt2sas_get_sdev_from_target(struct MPT3SAS_ADAPTER *ioc, ++ struct MPT3SAS_TARGET *tgt_priv) ++{ ++ struct _sas_device *ret; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ ret = __mpt2sas_get_sdev_from_target(ioc, tgt_priv); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ return ret; ++} ++ ++ ++struct _sas_device * ++__mpt2sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address) ++{ ++ struct _sas_device *sas_device; ++ ++ assert_spin_locked(&ioc->sas_device_lock); ++ ++ list_for_each_entry(sas_device, &ioc->sas_device_list, list) ++ if (sas_device->sas_address == sas_address) ++ goto found_device; ++ ++ list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) ++ if (sas_device->sas_address == sas_address) ++ goto found_device; ++ ++ return NULL; ++ ++found_device: ++ sas_device_get(sas_device); ++ return sas_device; ++} ++ ++/** ++ * mpt2sas_get_sdev_by_addr - sas device search ++ * @ioc: per adapter object ++ * @sas_address: sas address ++ * Context: Calling function should acquire ioc->sas_device_lock ++ * ++ * This searches for sas_device based on sas_address, then return sas_device ++ * object. ++ */ ++struct _sas_device * ++mpt2sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address) ++{ ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ sas_address); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ return sas_device; ++} ++ ++static struct _sas_device * ++__mpt2sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _sas_device *sas_device; ++ ++ assert_spin_locked(&ioc->sas_device_lock); ++ ++ list_for_each_entry(sas_device, &ioc->sas_device_list, list) ++ if (sas_device->handle == handle) ++ goto found_device; ++ ++ list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) ++ if (sas_device->handle == handle) ++ goto found_device; ++ ++ return NULL; ++ ++found_device: ++ sas_device_get(sas_device); ++ return sas_device; ++} ++ ++/** ++ * mpt2sas_get_sdev_by_handle - sas device search ++ * @ioc: per adapter object ++ * @handle: sas device handle (assigned by firmware) ++ * Context: Calling function should acquire ioc->sas_device_lock ++ * ++ * This searches for sas_device based on sas_address, then return sas_device ++ * object. ++ */ ++static struct _sas_device * ++mpt2sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ return sas_device; ++} ++ ++/** ++ * _scsih_sas_device_remove - remove sas_device from list. ++ * @ioc: per adapter object ++ * @sas_device: the sas_device object ++ * Context: This function will acquire ioc->sas_device_lock. ++ * ++ * If sas_device is on the list, remove it and decrement its reference count. ++ */ ++static void ++_scsih_sas_device_remove(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_device *sas_device) ++{ ++ unsigned long flags; ++ ++ if (!sas_device) ++ return; ++ pr_info(MPT3SAS_FMT ++ "removing handle(0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, sas_device->handle, ++ (unsigned long long) sas_device->sas_address); ++ ++ if (sas_device->enclosure_handle != 0) ++ pr_info(MPT3SAS_FMT ++ "removing enclosure logical id(0x%016llx), slot(%d)\n", ++ ioc->name, (unsigned long long) ++ sas_device->enclosure_logical_id, sas_device->slot); ++ ++ if (sas_device->connector_name[0] != '\0') ++ pr_info(MPT3SAS_FMT ++ "removing enclosure level(0x%04x), connector name( %s)\n", ++ ioc->name, sas_device->enclosure_level, ++ sas_device->connector_name); ++ ++ /* ++ * The lock serializes access to the list, but we still need to verify ++ * that nobody removed the entry while we were waiting on the lock. ++ */ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ if (!list_empty(&sas_device->list)) { ++ list_del_init(&sas_device->list); ++ sas_device_put(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++} ++ ++/** ++ * _scsih_device_remove_by_handle - removing device object by handle ++ * @ioc: per adapter object ++ * @handle: device handle ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ if (ioc->shost_recovery) ++ return; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (sas_device) { ++ list_del_init(&sas_device->list); ++ sas_device_put(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ if (sas_device) { ++ _scsih_remove_device(ioc, sas_device); ++ sas_device_put(sas_device); ++ } ++} ++ ++/** ++ * mpt2sas_device_remove_by_sas_address - removing device object by sas address ++ * @ioc: per adapter object ++ * @sas_address: device sas_address ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_device_remove_by_sas_address(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address) ++{ ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ if (ioc->shost_recovery) ++ return; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, sas_address); ++ if (sas_device) { ++ list_del_init(&sas_device->list); ++ sas_device_put(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ if (sas_device) { ++ _scsih_remove_device(ioc, sas_device); ++ sas_device_put(sas_device); ++ } ++} ++ ++/** ++ * _scsih_sas_device_add - insert sas_device to the list. ++ * @ioc: per adapter object ++ * @sas_device: the sas_device object ++ * Context: This function will acquire ioc->sas_device_lock. ++ * ++ * Adding new object to the ioc->sas_device_list. ++ */ ++static void ++_scsih_sas_device_add(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_device *sas_device) ++{ ++ unsigned long flags; ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: handle(0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, __func__, sas_device->handle, ++ (unsigned long long)sas_device->sas_address)); ++ ++ if (sas_device->enclosure_handle != 0) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enclosure logical id(0x%016llx), slot( %d)\n", ++ ioc->name, __func__, (unsigned long long) ++ sas_device->enclosure_logical_id, sas_device->slot)); ++ ++ if (sas_device->connector_name[0] != '\0') ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enclosure level(0x%04x), connector name( %s)\n", ++ ioc->name, __func__, ++ sas_device->enclosure_level, sas_device->connector_name)); ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device_get(sas_device); ++ list_add_tail(&sas_device->list, &ioc->sas_device_list); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ if (!mpt2sas_transport_port_add(ioc, sas_device->handle, ++ sas_device->sas_address_parent)) { ++ _scsih_sas_device_remove(ioc, sas_device); ++ } else if (!sas_device->starget) { ++ /* ++ * When asyn scanning is enabled, its not possible to remove ++ * devices while scanning is turned on due to an oops in ++ * scsi_sysfs_add_sdev()->add_device()->sysfs_addrm_start() ++ */ ++ if (!ioc->is_driver_loading) { ++ mpt2sas_transport_port_remove(ioc, ++ sas_device->sas_address, ++ sas_device->sas_address_parent); ++ _scsih_sas_device_remove(ioc, sas_device); ++ } ++ } ++} ++ ++/** ++ * _scsih_sas_device_init_add - insert sas_device to the list. ++ * @ioc: per adapter object ++ * @sas_device: the sas_device object ++ * Context: This function will acquire ioc->sas_device_lock. ++ * ++ * Adding new object at driver load time to the ioc->sas_device_init_list. ++ */ ++static void ++_scsih_sas_device_init_add(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_device *sas_device) ++{ ++ unsigned long flags; ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, ++ __func__, sas_device->handle, ++ (unsigned long long)sas_device->sas_address)); ++ ++ if (sas_device->enclosure_handle != 0) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enclosure logical id(0x%016llx), slot( %d)\n", ++ ioc->name, __func__, (unsigned long long) ++ sas_device->enclosure_logical_id, sas_device->slot)); ++ ++ if (sas_device->connector_name[0] != '\0') ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enclosure level(0x%04x), connector name( %s)\n", ++ ioc->name, __func__, sas_device->enclosure_level, ++ sas_device->connector_name)); ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device_get(sas_device); ++ list_add_tail(&sas_device->list, &ioc->sas_device_init_list); ++ _scsih_determine_boot_device(ioc, sas_device, 0); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++} ++ ++/** ++ * _scsih_raid_device_find_by_id - raid device search ++ * @ioc: per adapter object ++ * @id: sas device target id ++ * @channel: sas device channel ++ * Context: Calling function should acquire ioc->raid_device_lock ++ * ++ * This searches for raid_device based on target id, then return raid_device ++ * object. ++ */ ++static struct _raid_device * ++_scsih_raid_device_find_by_id(struct MPT3SAS_ADAPTER *ioc, int id, int channel) ++{ ++ struct _raid_device *raid_device, *r; ++ ++ r = NULL; ++ list_for_each_entry(raid_device, &ioc->raid_device_list, list) { ++ if (raid_device->id == id && raid_device->channel == channel) { ++ r = raid_device; ++ goto out; ++ } ++ } ++ ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_raid_device_find_by_handle - raid device search ++ * @ioc: per adapter object ++ * @handle: sas device handle (assigned by firmware) ++ * Context: Calling function should acquire ioc->raid_device_lock ++ * ++ * This searches for raid_device based on handle, then return raid_device ++ * object. ++ */ ++struct _raid_device * ++mpt2sas_raid_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _raid_device *raid_device, *r; ++ ++ r = NULL; ++ list_for_each_entry(raid_device, &ioc->raid_device_list, list) { ++ if (raid_device->handle != handle) ++ continue; ++ r = raid_device; ++ goto out; ++ } ++ ++ out: ++ return r; ++} ++ ++/** ++ * _scsih_raid_device_find_by_wwid - raid device search ++ * @ioc: per adapter object ++ * @handle: sas device handle (assigned by firmware) ++ * Context: Calling function should acquire ioc->raid_device_lock ++ * ++ * This searches for raid_device based on wwid, then return raid_device ++ * object. ++ */ ++static struct _raid_device * ++_scsih_raid_device_find_by_wwid(struct MPT3SAS_ADAPTER *ioc, u64 wwid) ++{ ++ struct _raid_device *raid_device, *r; ++ ++ r = NULL; ++ list_for_each_entry(raid_device, &ioc->raid_device_list, list) { ++ if (raid_device->wwid != wwid) ++ continue; ++ r = raid_device; ++ goto out; ++ } ++ ++ out: ++ return r; ++} ++ ++/** ++ * _scsih_raid_device_add - add raid_device object ++ * @ioc: per adapter object ++ * @raid_device: raid_device object ++ * ++ * This is added to the raid_device_list link list. ++ */ ++static void ++_scsih_raid_device_add(struct MPT3SAS_ADAPTER *ioc, ++ struct _raid_device *raid_device) ++{ ++ unsigned long flags; ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: handle(0x%04x), wwid(0x%016llx)\n", ioc->name, __func__, ++ raid_device->handle, (unsigned long long)raid_device->wwid)); ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ list_add_tail(&raid_device->list, &ioc->raid_device_list); ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++} ++ ++/** ++ * _scsih_raid_device_remove - delete raid_device object ++ * @ioc: per adapter object ++ * @raid_device: raid_device object ++ * ++ */ ++static void ++_scsih_raid_device_remove(struct MPT3SAS_ADAPTER *ioc, ++ struct _raid_device *raid_device) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ list_del(&raid_device->list); ++ kfree(raid_device); ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++} ++ ++/** ++ * mpt2sas_scsih_expander_find_by_handle - expander device search ++ * @ioc: per adapter object ++ * @handle: expander handle (assigned by firmware) ++ * Context: Calling function should acquire ioc->sas_device_lock ++ * ++ * This searches for expander device based on handle, then returns the ++ * sas_node object. ++ */ ++struct _sas_node * ++mpt2sas_scsih_expander_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _sas_node *sas_expander, *r; ++ ++ r = NULL; ++ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { ++ if (sas_expander->handle != handle) ++ continue; ++ r = sas_expander; ++ goto out; ++ } ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_scsih_expander_find_by_sas_address - expander device search ++ * @ioc: per adapter object ++ * @sas_address: sas address ++ * Context: Calling function should acquire ioc->sas_node_lock. ++ * ++ * This searches for expander device based on sas_address, then returns the ++ * sas_node object. ++ */ ++struct _sas_node * ++mpt2sas_scsih_expander_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address) ++{ ++ struct _sas_node *sas_expander, *r; ++ ++ r = NULL; ++ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { ++ if (sas_expander->sas_address != sas_address) ++ continue; ++ r = sas_expander; ++ goto out; ++ } ++ out: ++ return r; ++} ++ ++/** ++ * _scsih_expander_node_add - insert expander device to the list. ++ * @ioc: per adapter object ++ * @sas_expander: the sas_device object ++ * Context: This function will acquire ioc->sas_node_lock. ++ * ++ * Adding new object to the ioc->sas_expander_list. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_expander_node_add(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_node *sas_expander) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ list_add_tail(&sas_expander->list, &ioc->sas_expander_list); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++} ++ ++/** ++ * _scsih_is_end_device - determines if device is an end device ++ * @device_info: bitfield providing information about the device. ++ * Context: none ++ * ++ * Returns 1 if end device. ++ */ ++static int ++_scsih_is_end_device(u32 device_info) ++{ ++ if (device_info & MPI2_SAS_DEVICE_INFO_END_DEVICE && ++ ((device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) | ++ (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) | ++ (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE))) ++ return 1; ++ else ++ return 0; ++} ++ ++/** ++ * _scsih_scsi_lookup_get - returns scmd entry ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Returns the smid stored scmd pointer. ++ */ ++static struct scsi_cmnd * ++_scsih_scsi_lookup_get(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ return ioc->scsi_lookup[smid - 1].scmd; ++} ++ ++/** ++ * _scsih_scsi_lookup_get_clear - returns scmd entry ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Returns the smid stored scmd pointer. ++ * Then will derefrence the stored scmd pointer. ++ */ ++static inline struct scsi_cmnd * ++_scsih_scsi_lookup_get_clear(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ unsigned long flags; ++ struct scsi_cmnd *scmd; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ scmd = ioc->scsi_lookup[smid - 1].scmd; ++ ioc->scsi_lookup[smid - 1].scmd = NULL; ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ ++ return scmd; ++} ++ ++/** ++ * _scsih_scsi_lookup_find_by_scmd - scmd lookup ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @scmd: pointer to scsi command object ++ * Context: This function will acquire ioc->scsi_lookup_lock. ++ * ++ * This will search for a scmd pointer in the scsi_lookup array, ++ * returning the revelent smid. A returned value of zero means invalid. ++ */ ++static u16 ++_scsih_scsi_lookup_find_by_scmd(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd ++ *scmd) ++{ ++ u16 smid; ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ smid = 0; ++ for (i = 0; i < ioc->scsiio_depth; i++) { ++ if (ioc->scsi_lookup[i].scmd == scmd) { ++ smid = ioc->scsi_lookup[i].smid; ++ goto out; ++ } ++ } ++ out: ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return smid; ++} ++ ++/** ++ * _scsih_scsi_lookup_find_by_target - search for matching channel:id ++ * @ioc: per adapter object ++ * @id: target id ++ * @channel: channel ++ * Context: This function will acquire ioc->scsi_lookup_lock. ++ * ++ * This will search for a matching channel:id in the scsi_lookup array, ++ * returning 1 if found. ++ */ ++static u8 ++_scsih_scsi_lookup_find_by_target(struct MPT3SAS_ADAPTER *ioc, int id, ++ int channel) ++{ ++ u8 found; ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ found = 0; ++ for (i = 0 ; i < ioc->scsiio_depth; i++) { ++ if (ioc->scsi_lookup[i].scmd && ++ (ioc->scsi_lookup[i].scmd->device->id == id && ++ ioc->scsi_lookup[i].scmd->device->channel == channel)) { ++ found = 1; ++ goto out; ++ } ++ } ++ out: ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return found; ++} ++ ++/** ++ * _scsih_scsi_lookup_find_by_lun - search for matching channel:id:lun ++ * @ioc: per adapter object ++ * @id: target id ++ * @lun: lun number ++ * @channel: channel ++ * Context: This function will acquire ioc->scsi_lookup_lock. ++ * ++ * This will search for a matching channel:id:lun in the scsi_lookup array, ++ * returning 1 if found. ++ */ ++static u8 ++_scsih_scsi_lookup_find_by_lun(struct MPT3SAS_ADAPTER *ioc, int id, ++ unsigned int lun, int channel) ++{ ++ u8 found; ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ found = 0; ++ for (i = 0 ; i < ioc->scsiio_depth; i++) { ++ if (ioc->scsi_lookup[i].scmd && ++ (ioc->scsi_lookup[i].scmd->device->id == id && ++ ioc->scsi_lookup[i].scmd->device->channel == channel && ++ ioc->scsi_lookup[i].scmd->device->lun == lun)) { ++ found = 1; ++ goto out; ++ } ++ } ++ out: ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return found; ++} ++ ++static void ++_scsih_adjust_queue_depth(struct scsi_device *sdev, int qdepth) ++{ ++ struct Scsi_Host *shost = sdev->host; ++ int max_depth; ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ max_depth = shost->can_queue; ++ ++ /* limit max device queue for SATA to 32 */ ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data) ++ goto not_sata; ++ sas_target_priv_data = sas_device_priv_data->sas_target; ++ if (!sas_target_priv_data) ++ goto not_sata; ++ if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) ++ goto not_sata; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_from_target(ioc, sas_target_priv_data); ++ if (sas_device) { ++ if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE) ++ max_depth = MPT3SAS_SATA_QUEUE_DEPTH; ++ ++ sas_device_put(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ not_sata: ++ ++ if (!sdev->tagged_supported) ++ max_depth = 1; ++ if (qdepth > max_depth) ++ qdepth = max_depth; ++ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); ++} ++ ++/** ++ * scsih_change_queue_depth_mpt2sas - setting device queue depth ++ * @sdev: scsi device struct ++ * @qdepth: requested queue depth ++ * @reason: SCSI_QDEPTH_DEFAULT/SCSI_QDEPTH_QFULL/SCSI_QDEPTH_RAMP_UP ++ * (see include/scsi/scsi_host.h for definition) ++ * ++ * Returns queue depth. ++ */ ++int ++scsih_change_queue_depth_mpt2sas(struct scsi_device *sdev, int qdepth, int reason) ++{ ++ if (reason == SCSI_QDEPTH_DEFAULT || reason == SCSI_QDEPTH_RAMP_UP) ++ _scsih_adjust_queue_depth(sdev, qdepth); ++ else if (reason == SCSI_QDEPTH_QFULL) ++ scsi_track_queue_full(sdev, qdepth); ++ else ++ return -EOPNOTSUPP; ++ ++ if (sdev->inquiry_len > 7) ++ sdev_printk(KERN_INFO, sdev, "qdepth(%d), tagged(%d), " \ ++ "simple(%d), ordered(%d), scsi_level(%d), cmd_que(%d)\n", ++ sdev->queue_depth, sdev->tagged_supported, sdev->simple_tags, ++ sdev->ordered_tags, sdev->scsi_level, ++ (sdev->inquiry[7] & 2) >> 1); ++ ++ return sdev->queue_depth; ++} ++ ++/** ++ * _scsih_change_queue_type_mpt2sas - changing device queue tag type ++ * @sdev: scsi device struct ++ * @tag_type: requested tag type ++ * ++ * Returns queue tag type. ++ */ ++int ++_scsih_change_queue_type_mpt2sas(struct scsi_device *sdev, int tag_type) ++{ ++ if (sdev->tagged_supported) { ++ scsi_set_tag_type(sdev, tag_type); ++ if (tag_type) ++ scsi_activate_tcq(sdev, sdev->queue_depth); ++ else ++ scsi_deactivate_tcq(sdev, sdev->queue_depth); ++ } else ++ tag_type = 0; ++ ++ return tag_type; ++} ++ ++ ++/** ++ * scsih_target_alloc_mpt2sas - target add routine ++ * @starget: scsi target struct ++ * ++ * Returns 0 if ok. Any other return is assumed to be an error and ++ * the device is ignored. ++ */ ++int ++scsih_target_alloc_mpt2sas(struct scsi_target *starget) ++{ ++ struct Scsi_Host *shost = dev_to_shost(&starget->dev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct _sas_device *sas_device; ++ struct _raid_device *raid_device; ++ unsigned long flags; ++ struct sas_rphy *rphy; ++ ++ sas_target_priv_data = kzalloc(sizeof(*sas_target_priv_data), ++ GFP_KERNEL); ++ if (!sas_target_priv_data) ++ return -ENOMEM; ++ ++ starget->hostdata = sas_target_priv_data; ++ sas_target_priv_data->starget = starget; ++ sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE; ++ ++ /* RAID volumes */ ++ if (starget->channel == RAID_CHANNEL) { ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = _scsih_raid_device_find_by_id(ioc, starget->id, ++ starget->channel); ++ if (raid_device) { ++ sas_target_priv_data->handle = raid_device->handle; ++ sas_target_priv_data->sas_address = raid_device->wwid; ++ sas_target_priv_data->flags |= MPT_TARGET_FLAGS_VOLUME; ++ sas_target_priv_data->raid_device = raid_device; ++ if (ioc->is_warpdrive) ++ raid_device->starget = starget; ++ } ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ return 0; ++ } ++ ++ /* sas/sata devices */ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ rphy = dev_to_rphy(starget->dev.parent); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ rphy->identify.sas_address); ++ ++ if (sas_device) { ++ sas_target_priv_data->handle = sas_device->handle; ++ sas_target_priv_data->sas_address = sas_device->sas_address; ++ sas_target_priv_data->sdev = sas_device; ++ sas_device->starget = starget; ++ sas_device->id = starget->id; ++ sas_device->channel = starget->channel; ++ if (test_bit(sas_device->handle, ioc->pd_handles)) ++ sas_target_priv_data->flags |= ++ MPT_TARGET_FLAGS_RAID_COMPONENT; ++ if (sas_device->fast_path) ++ sas_target_priv_data->flags |= MPT_TARGET_FASTPATH_IO; ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ return 0; ++} ++ ++/** ++ * scsih_target_destroy_mpt2sas - target destroy routine ++ * @starget: scsi target struct ++ * ++ * Returns nothing. ++ */ ++void ++scsih_target_destroy_mpt2sas(struct scsi_target *starget) ++{ ++ struct Scsi_Host *shost = dev_to_shost(&starget->dev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct _sas_device *sas_device; ++ struct _raid_device *raid_device; ++ unsigned long flags; ++ struct sas_rphy *rphy; ++ ++ sas_target_priv_data = starget->hostdata; ++ if (!sas_target_priv_data) ++ return; ++ ++ if (starget->channel == RAID_CHANNEL) { ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = _scsih_raid_device_find_by_id(ioc, starget->id, ++ starget->channel); ++ if (raid_device) { ++ raid_device->starget = NULL; ++ raid_device->sdev = NULL; ++ } ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ goto out; ++ } ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ rphy = dev_to_rphy(starget->dev.parent); ++ sas_device = __mpt2sas_get_sdev_from_target(ioc, sas_target_priv_data); ++ if (sas_device && (sas_device->starget == starget) && ++ (sas_device->id == starget->id) && ++ (sas_device->channel == starget->channel)) ++ sas_device->starget = NULL; ++ ++ if (sas_device) { ++ /* ++ * Corresponding get() is in _scsih_target_alloc_mpt2sas() ++ */ ++ sas_target_priv_data->sdev = NULL; ++ sas_device_put(sas_device); ++ ++ sas_device_put(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ out: ++ kfree(sas_target_priv_data); ++ starget->hostdata = NULL; ++} ++ ++/** ++ * scsih_slave_alloc_mpt2sas - device add routine ++ * @sdev: scsi device struct ++ * ++ * Returns 0 if ok. Any other return is assumed to be an error and ++ * the device is ignored. ++ */ ++int ++scsih_slave_alloc_mpt2sas(struct scsi_device *sdev) ++{ ++ struct Scsi_Host *shost; ++ struct MPT3SAS_ADAPTER *ioc; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_target *starget; ++ struct _raid_device *raid_device; ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ sas_device_priv_data = kzalloc(sizeof(*sas_device_priv_data), ++ GFP_KERNEL); ++ if (!sas_device_priv_data) ++ return -ENOMEM; ++ ++ sas_device_priv_data->lun = sdev->lun; ++ sas_device_priv_data->flags = MPT_DEVICE_FLAGS_INIT; ++ ++ starget = scsi_target(sdev); ++ sas_target_priv_data = starget->hostdata; ++ sas_target_priv_data->num_luns++; ++ sas_device_priv_data->sas_target = sas_target_priv_data; ++ sdev->hostdata = sas_device_priv_data; ++ if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT)) ++ sdev->no_uld_attach = 1; ++ ++ shost = dev_to_shost(&starget->dev); ++ ioc = shost_priv(shost); ++ if (starget->channel == RAID_CHANNEL) { ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = _scsih_raid_device_find_by_id(ioc, ++ starget->id, starget->channel); ++ if (raid_device) ++ raid_device->sdev = sdev; /* raid is single lun */ ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ } ++ ++ if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ sas_target_priv_data->sas_address); ++ if (sas_device && (sas_device->starget == NULL)) { ++ sdev_printk(KERN_INFO, sdev, ++ "%s : sas_device->starget set to starget @ %d\n", ++ __func__, __LINE__); ++ sas_device->starget = starget; ++ } ++ ++ if (sas_device) ++ sas_device_put(sas_device); ++ ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ } ++ ++ return 0; ++} ++ ++/** ++ * scsih_slave_destroy_mpt2sas - device destroy routine ++ * @sdev: scsi device struct ++ * ++ * Returns nothing. ++ */ ++void ++scsih_slave_destroy_mpt2sas(struct scsi_device *sdev) ++{ ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct scsi_target *starget; ++ struct Scsi_Host *shost; ++ struct MPT3SAS_ADAPTER *ioc; ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ if (!sdev->hostdata) ++ return; ++ ++ starget = scsi_target(sdev); ++ sas_target_priv_data = starget->hostdata; ++ sas_target_priv_data->num_luns--; ++ ++ shost = dev_to_shost(&starget->dev); ++ ioc = shost_priv(shost); ++ ++ if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_from_target(ioc, ++ sas_target_priv_data); ++ if (sas_device && !sas_target_priv_data->num_luns) ++ sas_device->starget = NULL; ++ ++ if (sas_device) ++ sas_device_put(sas_device); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ } ++ ++ kfree(sdev->hostdata); ++ sdev->hostdata = NULL; ++} ++ ++/** ++ * _scsih_display_sata_capabilities - sata capabilities ++ * @ioc: per adapter object ++ * @handle: device handle ++ * @sdev: scsi device struct ++ */ ++static void ++_scsih_display_sata_capabilities(struct MPT3SAS_ADAPTER *ioc, ++ u16 handle, struct scsi_device *sdev) ++{ ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ u32 ioc_status; ++ u16 flags; ++ u32 device_info; ++ ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, ++ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ flags = le16_to_cpu(sas_device_pg0.Flags); ++ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); ++ ++ sdev_printk(KERN_INFO, sdev, ++ "atapi(%s), ncq(%s), asyn_notify(%s), smart(%s), fua(%s), " ++ "sw_preserve(%s)\n", ++ (device_info & MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE) ? "y" : "n", ++ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED) ? "y" : "n", ++ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY) ? "y" : ++ "n", ++ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED) ? "y" : "n", ++ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED) ? "y" : "n", ++ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE) ? "y" : "n"); ++} ++ ++/* ++ * raid transport support - ++ * Enabled for SLES11 and newer, in older kernels the driver will panic when ++ * unloading the driver followed by a load - I beleive that the subroutine ++ * raid_class_release() is not cleaning up properly. ++ */ ++ ++/** ++ * scsih_is_raid_mpt2sas - return boolean indicating device is raid volume ++ * @dev the device struct object ++ */ ++int ++scsih_is_raid_mpt2sas(struct device *dev) ++{ ++ struct scsi_device *sdev = to_scsi_device(dev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host); ++ ++ if (ioc->is_warpdrive) ++ return 0; ++ return (sdev->channel == RAID_CHANNEL) ? 1 : 0; ++} ++ ++/** ++ * scsih_get_resync_mpt2sas - get raid volume resync percent complete ++ * @dev the device struct object ++ */ ++void ++scsih_get_resync_mpt2sas(struct device *dev) ++{ ++ struct scsi_device *sdev = to_scsi_device(dev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host); ++ static struct _raid_device *raid_device; ++ unsigned long flags; ++ Mpi2RaidVolPage0_t vol_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u32 volume_status_flags; ++ u8 percent_complete; ++ u16 handle; ++ ++ percent_complete = 0; ++ handle = 0; ++ if (ioc->is_warpdrive) ++ goto out; ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id, ++ sdev->channel); ++ if (raid_device) { ++ handle = raid_device->handle; ++ percent_complete = raid_device->percent_complete; ++ } ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ ++ if (!handle) ++ goto out; ++ ++ if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0, ++ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, ++ sizeof(Mpi2RaidVolPage0_t))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ percent_complete = 0; ++ goto out; ++ } ++ ++ volume_status_flags = le32_to_cpu(vol_pg0.VolumeStatusFlags); ++ if (!(volume_status_flags & ++ MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS)) ++ percent_complete = 0; ++ ++ out: ++ ++ switch (ioc->hba_mpi_version_belonged) { ++ case MPI2_VERSION: ++ raid_set_resync(mpt2sas_raid_template_mpt2sas, dev, percent_complete); ++ break; ++ case MPI25_VERSION: ++ case MPI26_VERSION: ++ raid_set_resync(mpt3sas_raid_template_mpt2sas, dev, percent_complete); ++ break; ++ } ++} ++ ++/** ++ * scsih_get_state_mpt2sas - get raid volume level ++ * @dev the device struct object ++ */ ++void ++scsih_get_state_mpt2sas(struct device *dev) ++{ ++ struct scsi_device *sdev = to_scsi_device(dev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host); ++ static struct _raid_device *raid_device; ++ unsigned long flags; ++ Mpi2RaidVolPage0_t vol_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u32 volstate; ++ enum raid_state state = RAID_STATE_UNKNOWN; ++ u16 handle = 0; ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id, ++ sdev->channel); ++ if (raid_device) ++ handle = raid_device->handle; ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ ++ if (!raid_device) ++ goto out; ++ ++ if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0, ++ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, ++ sizeof(Mpi2RaidVolPage0_t))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ++ volstate = le32_to_cpu(vol_pg0.VolumeStatusFlags); ++ if (volstate & MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) { ++ state = RAID_STATE_RESYNCING; ++ goto out; ++ } ++ ++ switch (vol_pg0.VolumeState) { ++ case MPI2_RAID_VOL_STATE_OPTIMAL: ++ case MPI2_RAID_VOL_STATE_ONLINE: ++ state = RAID_STATE_ACTIVE; ++ break; ++ case MPI2_RAID_VOL_STATE_DEGRADED: ++ state = RAID_STATE_DEGRADED; ++ break; ++ case MPI2_RAID_VOL_STATE_FAILED: ++ case MPI2_RAID_VOL_STATE_MISSING: ++ state = RAID_STATE_OFFLINE; ++ break; ++ } ++ out: ++ switch (ioc->hba_mpi_version_belonged) { ++ case MPI2_VERSION: ++ raid_set_state(mpt2sas_raid_template_mpt2sas, dev, state); ++ break; ++ case MPI25_VERSION: ++ case MPI26_VERSION: ++ raid_set_state(mpt3sas_raid_template_mpt2sas, dev, state); ++ break; ++ } ++} ++ ++/** ++ * _scsih_set_level - set raid level ++ * @sdev: scsi device struct ++ * @volume_type: volume type ++ */ ++static void ++_scsih_set_level(struct MPT3SAS_ADAPTER *ioc, ++ struct scsi_device *sdev, u8 volume_type) ++{ ++ enum raid_level level = RAID_LEVEL_UNKNOWN; ++ ++ switch (volume_type) { ++ case MPI2_RAID_VOL_TYPE_RAID0: ++ level = RAID_LEVEL_0; ++ break; ++ case MPI2_RAID_VOL_TYPE_RAID10: ++ level = RAID_LEVEL_10; ++ break; ++ case MPI2_RAID_VOL_TYPE_RAID1E: ++ level = RAID_LEVEL_1E; ++ break; ++ case MPI2_RAID_VOL_TYPE_RAID1: ++ level = RAID_LEVEL_1; ++ break; ++ } ++ ++ switch (ioc->hba_mpi_version_belonged) { ++ case MPI2_VERSION: ++ raid_set_level(mpt2sas_raid_template_mpt2sas, ++ &sdev->sdev_gendev, level); ++ break; ++ case MPI25_VERSION: ++ case MPI26_VERSION: ++ raid_set_level(mpt3sas_raid_template_mpt2sas, ++ &sdev->sdev_gendev, level); ++ break; ++ } ++} ++ ++ ++/** ++ * _scsih_get_volume_capabilities - volume capabilities ++ * @ioc: per adapter object ++ * @sas_device: the raid_device object ++ * ++ * Returns 0 for success, else 1 ++ */ ++static int ++_scsih_get_volume_capabilities(struct MPT3SAS_ADAPTER *ioc, ++ struct _raid_device *raid_device) ++{ ++ Mpi2RaidVolPage0_t *vol_pg0; ++ Mpi2RaidPhysDiskPage0_t pd_pg0; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 sz; ++ u8 num_pds; ++ ++ if ((mpt2sas_config_get_number_pds(ioc, raid_device->handle, ++ &num_pds)) || !num_pds) { ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, ++ __func__)); ++ return 1; ++ } ++ ++ raid_device->num_pds = num_pds; ++ sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds * ++ sizeof(Mpi2RaidVol0PhysDisk_t)); ++ vol_pg0 = kzalloc(sz, GFP_KERNEL); ++ if (!vol_pg0) { ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, ++ __func__)); ++ return 1; ++ } ++ ++ if ((mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0, ++ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) { ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, ++ __func__)); ++ kfree(vol_pg0); ++ return 1; ++ } ++ ++ raid_device->volume_type = vol_pg0->VolumeType; ++ ++ /* figure out what the underlying devices are by ++ * obtaining the device_info bits for the 1st device ++ */ ++ if (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, ++ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM, ++ vol_pg0->PhysDisk[0].PhysDiskNum))) { ++ if (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, ++ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ++ le16_to_cpu(pd_pg0.DevHandle)))) { ++ raid_device->device_info = ++ le32_to_cpu(sas_device_pg0.DeviceInfo); ++ } ++ } ++ ++ kfree(vol_pg0); ++ return 0; ++} ++ ++/** ++ * _scsih_enable_tlr - setting TLR flags ++ * @ioc: per adapter object ++ * @sdev: scsi device struct ++ * ++ * Enabling Transaction Layer Retries for tape devices when ++ * vpd page 0x90 is present ++ * ++ */ ++static void ++_scsih_enable_tlr(struct MPT3SAS_ADAPTER *ioc, struct scsi_device *sdev) ++{ ++ ++ /* only for TAPE */ ++ if (sdev->type != TYPE_TAPE) ++ return; ++ ++ if (!(ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR)) ++ return; ++ ++ sas_enable_tlr(sdev); ++ sdev_printk(KERN_INFO, sdev, "TLR %s\n", ++ sas_is_tlr_enabled(sdev) ? "Enabled" : "Disabled"); ++ return; ++ ++} ++ ++/** ++ * scsih_slave_configure_mpt2sas - device configure routine. ++ * @sdev: scsi device struct ++ * ++ * Returns 0 if ok. Any other return is assumed to be an error and ++ * the device is ignored. ++ */ ++int ++scsih_slave_configure_mpt2sas(struct scsi_device *sdev) ++{ ++ struct Scsi_Host *shost = sdev->host; ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct _sas_device *sas_device; ++ struct _raid_device *raid_device; ++ unsigned long flags; ++ int qdepth; ++ u8 ssp_target = 0; ++ char *ds = ""; ++ char *r_level = ""; ++ u16 handle, volume_handle = 0; ++ u64 volume_wwid = 0; ++ ++ qdepth = 1; ++ sas_device_priv_data = sdev->hostdata; ++ sas_device_priv_data->configured_lun = 1; ++ sas_device_priv_data->flags &= ~MPT_DEVICE_FLAGS_INIT; ++ sas_target_priv_data = sas_device_priv_data->sas_target; ++ handle = sas_target_priv_data->handle; ++ ++ /* raid volume handling */ ++ if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME) { ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = mpt2sas_raid_device_find_by_handle(ioc, handle); ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ if (!raid_device) { ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, ++ __LINE__, __func__)); ++ return 1; ++ } ++ ++ if (_scsih_get_volume_capabilities(ioc, raid_device)) { ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, ++ __LINE__, __func__)); ++ return 1; ++ } ++ ++ /* ++ * WARPDRIVE: Initialize the required data for Direct IO ++ */ ++ mpt2sas_init_warpdrive_properties(ioc, raid_device); ++ ++ /* RAID Queue Depth Support ++ * IS volume = underlying qdepth of drive type, either ++ * MPT3SAS_SAS_QUEUE_DEPTH or MPT3SAS_SATA_QUEUE_DEPTH ++ * IM/IME/R10 = 128 (MPT3SAS_RAID_QUEUE_DEPTH) ++ */ ++ if (raid_device->device_info & ++ MPI2_SAS_DEVICE_INFO_SSP_TARGET) { ++ qdepth = MPT3SAS_SAS_QUEUE_DEPTH; ++ ds = "SSP"; ++ } else { ++ qdepth = MPT3SAS_SATA_QUEUE_DEPTH; ++ if (raid_device->device_info & ++ MPI2_SAS_DEVICE_INFO_SATA_DEVICE) ++ ds = "SATA"; ++ else ++ ds = "STP"; ++ } ++ ++ switch (raid_device->volume_type) { ++ case MPI2_RAID_VOL_TYPE_RAID0: ++ r_level = "RAID0"; ++ break; ++ case MPI2_RAID_VOL_TYPE_RAID1E: ++ qdepth = MPT3SAS_RAID_QUEUE_DEPTH; ++ if (ioc->manu_pg10.OEMIdentifier && ++ (le32_to_cpu(ioc->manu_pg10.GenericFlags0) & ++ MFG10_GF0_R10_DISPLAY) && ++ !(raid_device->num_pds % 2)) ++ r_level = "RAID10"; ++ else ++ r_level = "RAID1E"; ++ break; ++ case MPI2_RAID_VOL_TYPE_RAID1: ++ qdepth = MPT3SAS_RAID_QUEUE_DEPTH; ++ r_level = "RAID1"; ++ break; ++ case MPI2_RAID_VOL_TYPE_RAID10: ++ qdepth = MPT3SAS_RAID_QUEUE_DEPTH; ++ r_level = "RAID10"; ++ break; ++ case MPI2_RAID_VOL_TYPE_UNKNOWN: ++ default: ++ qdepth = MPT3SAS_RAID_QUEUE_DEPTH; ++ r_level = "RAIDX"; ++ break; ++ } ++ ++ if (!ioc->hide_ir_msg) ++ sdev_printk(KERN_INFO, sdev, ++ "%s: handle(0x%04x), wwid(0x%016llx)," ++ " pd_count(%d), type(%s)\n", ++ r_level, raid_device->handle, ++ (unsigned long long)raid_device->wwid, ++ raid_device->num_pds, ds); ++ ++ if (shost->max_sectors > MPT3SAS_RAID_MAX_SECTORS) { ++ blk_queue_max_hw_sectors(sdev->request_queue, ++ MPT3SAS_RAID_MAX_SECTORS); ++ sdev_printk(KERN_INFO, sdev, ++ "Set queue's max_sector to: %u\n", ++ MPT3SAS_RAID_MAX_SECTORS); ++ } ++ ++ scsih_change_queue_depth_mpt2sas(sdev, qdepth, SCSI_QDEPTH_DEFAULT); ++ ++ /* raid transport support */ ++ if (!ioc->is_warpdrive) ++ _scsih_set_level(ioc, sdev, raid_device->volume_type); ++ return 0; ++ } ++ ++ /* non-raid handling */ ++ if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) { ++ if (mpt2sas_config_get_volume_handle(ioc, handle, ++ &volume_handle)) { ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__)); ++ return 1; ++ } ++ if (volume_handle && mpt2sas_config_get_volume_wwid(ioc, ++ volume_handle, &volume_wwid)) { ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__)); ++ return 1; ++ } ++ } ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ sas_device_priv_data->sas_target->sas_address); ++ if (!sas_device) { ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, ++ __func__)); ++ return 1; ++ } ++ ++ sas_device->volume_handle = volume_handle; ++ sas_device->volume_wwid = volume_wwid; ++ if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) { ++ qdepth = MPT3SAS_SAS_QUEUE_DEPTH; ++ ssp_target = 1; ++ if (sas_device->device_info & ++ MPI2_SAS_DEVICE_INFO_SEP) { ++ sdev_printk(KERN_WARNING, sdev, ++ "set ignore_delay_remove for handle(0x%04x)\n", ++ sas_device_priv_data->sas_target->handle); ++ sas_device_priv_data->ignore_delay_remove = 1; ++ ds = "SES"; ++ } else ++ ds = "SSP"; ++ } else { ++ qdepth = MPT3SAS_SATA_QUEUE_DEPTH; ++ if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) ++ ds = "STP"; ++ else if (sas_device->device_info & ++ MPI2_SAS_DEVICE_INFO_SATA_DEVICE) ++ ds = "SATA"; ++ } ++ ++ sdev_printk(KERN_INFO, sdev, "%s: handle(0x%04x), " \ ++ "sas_addr(0x%016llx), phy(%d), device_name(0x%016llx)\n", ++ ds, handle, (unsigned long long)sas_device->sas_address, ++ sas_device->phy, (unsigned long long)sas_device->device_name); ++ if (sas_device->enclosure_handle != 0) ++ sdev_printk(KERN_INFO, sdev, ++ "%s: enclosure_logical_id(0x%016llx), slot(%d)\n", ++ ds, (unsigned long long) ++ sas_device->enclosure_logical_id, sas_device->slot); ++ if (sas_device->connector_name[0] != '\0') ++ sdev_printk(KERN_INFO, sdev, ++ "%s: enclosure level(0x%04x), connector name( %s)\n", ++ ds, sas_device->enclosure_level, ++ sas_device->connector_name); ++ ++ sas_device_put(sas_device); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ if (!ssp_target) ++ _scsih_display_sata_capabilities(ioc, handle, sdev); ++ ++ ++ scsih_change_queue_depth_mpt2sas(sdev, qdepth, SCSI_QDEPTH_DEFAULT); ++ ++ if (ssp_target) { ++ sas_read_port_mode_page(sdev); ++ _scsih_enable_tlr(ioc, sdev); ++ } ++ ++ return 0; ++} ++ ++/** ++ * scsih_bios_param_mpt2sas - fetch head, sector, cylinder info for a disk ++ * @sdev: scsi device struct ++ * @bdev: pointer to block device context ++ * @capacity: device size (in 512 byte sectors) ++ * @params: three element array to place output: ++ * params[0] number of heads (max 255) ++ * params[1] number of sectors (max 63) ++ * params[2] number of cylinders ++ * ++ * Return nothing. ++ */ ++int ++scsih_bios_param_mpt2sas(struct scsi_device *sdev, struct block_device *bdev, ++ sector_t capacity, int params[]) ++{ ++ int heads; ++ int sectors; ++ sector_t cylinders; ++ ulong dummy; ++ ++ heads = 64; ++ sectors = 32; ++ ++ dummy = heads * sectors; ++ cylinders = capacity; ++ sector_div(cylinders, dummy); ++ ++ /* ++ * Handle extended translation size for logical drives ++ * > 1Gb ++ */ ++ if ((ulong)capacity >= 0x200000) { ++ heads = 255; ++ sectors = 63; ++ dummy = heads * sectors; ++ cylinders = capacity; ++ sector_div(cylinders, dummy); ++ } ++ ++ /* return result */ ++ params[0] = heads; ++ params[1] = sectors; ++ params[2] = cylinders; ++ ++ return 0; ++} ++ ++/** ++ * _scsih_response_code - translation of device response code ++ * @ioc: per adapter object ++ * @response_code: response code returned by the device ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_response_code(struct MPT3SAS_ADAPTER *ioc, u8 response_code) ++{ ++ char *desc; ++ ++ switch (response_code) { ++ case MPI2_SCSITASKMGMT_RSP_TM_COMPLETE: ++ desc = "task management request completed"; ++ break; ++ case MPI2_SCSITASKMGMT_RSP_INVALID_FRAME: ++ desc = "invalid frame"; ++ break; ++ case MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED: ++ desc = "task management request not supported"; ++ break; ++ case MPI2_SCSITASKMGMT_RSP_TM_FAILED: ++ desc = "task management request failed"; ++ break; ++ case MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED: ++ desc = "task management request succeeded"; ++ break; ++ case MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN: ++ desc = "invalid lun"; ++ break; ++ case 0xA: ++ desc = "overlapped tag attempted"; ++ break; ++ case MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC: ++ desc = "task queued, however not sent to target"; ++ break; ++ default: ++ desc = "unknown"; ++ break; ++ } ++ pr_warn(MPT3SAS_FMT "response_code(0x%01x): %s\n", ++ ioc->name, response_code, desc); ++} ++ ++/** ++ * _scsih_tm_done - tm completion routine ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: none. ++ * ++ * The callback handler when using scsih_issue_tm. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_scsih_tm_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ ++ if (ioc->tm_cmds.status == MPT3_CMD_NOT_USED) ++ return 1; ++ if (ioc->tm_cmds.smid != smid) ++ return 1; ++ ioc->tm_cmds.status |= MPT3_CMD_COMPLETE; ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (mpi_reply) { ++ memcpy(ioc->tm_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); ++ ioc->tm_cmds.status |= MPT3_CMD_REPLY_VALID; ++ } ++ ioc->tm_cmds.status &= ~MPT3_CMD_PENDING; ++ complete(&ioc->tm_cmds.done); ++ return 1; ++} ++ ++/** ++ * mpt2sas_scsih_set_tm_flag - set per target tm_busy ++ * @ioc: per adapter object ++ * @handle: device handle ++ * ++ * During taskmangement request, we need to freeze the device queue. ++ */ ++void ++mpt2sas_scsih_set_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_device *sdev; ++ u8 skip = 0; ++ ++ shost_for_each_device(sdev, ioc->shost) { ++ if (skip) ++ continue; ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data) ++ continue; ++ if (sas_device_priv_data->sas_target->handle == handle) { ++ sas_device_priv_data->sas_target->tm_busy = 1; ++ skip = 1; ++ ioc->ignore_loginfos = 1; ++ } ++ } ++} ++ ++/** ++ * mpt2sas_scsih_clear_tm_flag - clear per target tm_busy ++ * @ioc: per adapter object ++ * @handle: device handle ++ * ++ * During taskmangement request, we need to freeze the device queue. ++ */ ++void ++mpt2sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_device *sdev; ++ u8 skip = 0; ++ ++ shost_for_each_device(sdev, ioc->shost) { ++ if (skip) ++ continue; ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data) ++ continue; ++ if (sas_device_priv_data->sas_target->handle == handle) { ++ sas_device_priv_data->sas_target->tm_busy = 0; ++ skip = 1; ++ ioc->ignore_loginfos = 0; ++ } ++ } ++} ++ ++/** ++ * mpt2sas_scsih_issue_tm - main routine for sending tm requests ++ * @ioc: per adapter struct ++ * @device_handle: device handle ++ * @channel: the channel assigned by the OS ++ * @id: the id assigned by the OS ++ * @lun: lun number ++ * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h) ++ * @smid_task: smid assigned to the task ++ * @timeout: timeout in seconds ++ * @m_type: TM_MUTEX_ON or TM_MUTEX_OFF ++ * Context: user ++ * ++ * A generic API for sending task management requests to firmware. ++ * ++ * The callback index is set inside `ioc->tm_cb_idx`. ++ * ++ * Return SUCCESS or FAILED. ++ */ ++int ++mpt2sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, ++ uint id, uint lun, u8 type, u16 smid_task, ulong timeout, ++ enum mutex_type m_type) ++{ ++ Mpi2SCSITaskManagementRequest_t *mpi_request; ++ Mpi2SCSITaskManagementReply_t *mpi_reply; ++ u16 smid = 0; ++ u32 ioc_state; ++ unsigned long timeleft; ++ struct scsiio_tracker *scsi_lookup = NULL; ++ int rc; ++ u16 msix_task = 0; ++ ++ if (m_type == TM_MUTEX_ON) ++ mutex_lock(&ioc->tm_cmds.mutex); ++ if (ioc->tm_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_info(MPT3SAS_FMT "%s: tm_cmd busy!!!\n", ++ __func__, ioc->name); ++ rc = FAILED; ++ goto err_out; ++ } ++ ++ if (ioc->shost_recovery || ioc->remove_host || ++ ioc->pci_error_recovery) { ++ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", ++ __func__, ioc->name); ++ rc = FAILED; ++ goto err_out; ++ } ++ ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 0); ++ if (ioc_state & MPI2_DOORBELL_USED) { ++ dhsprintk(ioc, pr_info(MPT3SAS_FMT ++ "unexpected doorbell active!\n", ioc->name)); ++ rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ rc = (!rc) ? SUCCESS : FAILED; ++ goto err_out; ++ } ++ ++ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { ++ mpt2sas_base_fault_info(ioc, ioc_state & ++ MPI2_DOORBELL_DATA_MASK); ++ rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ rc = (!rc) ? SUCCESS : FAILED; ++ goto err_out; ++ } ++ ++ smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = FAILED; ++ goto err_out; ++ } ++ ++ if (type == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK) ++ scsi_lookup = &ioc->scsi_lookup[smid_task - 1]; ++ ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "sending tm: handle(0x%04x), task_type(0x%02x), smid(%d)\n", ++ ioc->name, handle, type, smid_task)); ++ ioc->tm_cmds.status = MPT3_CMD_PENDING; ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->tm_cmds.smid = smid; ++ memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); ++ memset(ioc->tm_cmds.reply, 0, sizeof(Mpi2SCSITaskManagementReply_t)); ++ mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; ++ mpi_request->DevHandle = cpu_to_le16(handle); ++ mpi_request->TaskType = type; ++ mpi_request->TaskMID = cpu_to_le16(smid_task); ++ int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN); ++ mpt2sas_scsih_set_tm_flag(ioc, handle); ++ init_completion(&ioc->tm_cmds.done); ++ if ((type == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK) && ++ (scsi_lookup->msix_io < ioc->reply_queue_count)) ++ msix_task = scsi_lookup->msix_io; ++ else ++ msix_task = 0; ++ mpt2sas_base_put_smid_hi_priority(ioc, smid, msix_task); ++ timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ); ++ if (!(ioc->tm_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SCSITaskManagementRequest_t)/4); ++ if (!(ioc->tm_cmds.status & MPT3_CMD_RESET)) { ++ rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ rc = (!rc) ? SUCCESS : FAILED; ++ ioc->tm_cmds.status = MPT3_CMD_NOT_USED; ++ mpt2sas_scsih_clear_tm_flag(ioc, handle); ++ goto err_out; ++ } ++ } ++ ++ /* sync IRQs in case those were busy during flush. */ ++ mpt2sas_base_sync_reply_irqs(ioc); ++ ++ if (ioc->tm_cmds.status & MPT3_CMD_REPLY_VALID) { ++ mpt2sas_trigger_master(ioc, MASTER_TRIGGER_TASK_MANAGMENT); ++ mpi_reply = ioc->tm_cmds.reply; ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT "complete tm: " \ ++ "ioc_status(0x%04x), loginfo(0x%08x), term_count(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply->IOCStatus), ++ le32_to_cpu(mpi_reply->IOCLogInfo), ++ le32_to_cpu(mpi_reply->TerminationCount))); ++ if (ioc->logging_level & MPT_DEBUG_TM) { ++ _scsih_response_code(ioc, mpi_reply->ResponseCode); ++ if (mpi_reply->IOCStatus) ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SCSITaskManagementRequest_t)/4); ++ } ++ } ++ ++ switch (type) { ++ case MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK: ++ rc = SUCCESS; ++ if (scsi_lookup->scmd == NULL) ++ break; ++ rc = FAILED; ++ break; ++ ++ case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET: ++ if (_scsih_scsi_lookup_find_by_target(ioc, id, channel)) ++ rc = FAILED; ++ else ++ rc = SUCCESS; ++ break; ++ case MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET: ++ case MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET: ++ if (_scsih_scsi_lookup_find_by_lun(ioc, id, lun, channel)) ++ rc = FAILED; ++ else ++ rc = SUCCESS; ++ break; ++ case MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK: ++ rc = SUCCESS; ++ break; ++ default: ++ rc = FAILED; ++ break; ++ } ++ ++ mpt2sas_scsih_clear_tm_flag(ioc, handle); ++ ioc->tm_cmds.status = MPT3_CMD_NOT_USED; ++ if (m_type == TM_MUTEX_ON) ++ mutex_unlock(&ioc->tm_cmds.mutex); ++ ++ return rc; ++ ++ err_out: ++ if (m_type == TM_MUTEX_ON) ++ mutex_unlock(&ioc->tm_cmds.mutex); ++ return rc; ++} ++ ++/** ++ * _scsih_tm_display_info - displays info about the device ++ * @ioc: per adapter struct ++ * @scmd: pointer to scsi command object ++ * ++ * Called by task management callback handlers. ++ */ ++static void ++_scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd) ++{ ++ struct scsi_target *starget = scmd->device->sdev_target; ++ struct MPT3SAS_TARGET *priv_target = starget->hostdata; ++ struct _sas_device *sas_device = NULL; ++ unsigned long flags; ++ char *device_str = NULL; ++ ++ if (!priv_target) ++ return; ++ if (ioc->hide_ir_msg) ++ device_str = "WarpDrive"; ++ else ++ device_str = "volume"; ++ ++ scsi_print_command(scmd); ++ if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) { ++ starget_printk(KERN_INFO, starget, ++ "%s handle(0x%04x), %s wwid(0x%016llx)\n", ++ device_str, priv_target->handle, ++ device_str, (unsigned long long)priv_target->sas_address); ++ } else { ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_from_target(ioc, priv_target); ++ if (sas_device) { ++ if (priv_target->flags & ++ MPT_TARGET_FLAGS_RAID_COMPONENT) { ++ starget_printk(KERN_INFO, starget, ++ "volume handle(0x%04x), " ++ "volume wwid(0x%016llx)\n", ++ sas_device->volume_handle, ++ (unsigned long long)sas_device->volume_wwid); ++ } ++ starget_printk(KERN_INFO, starget, ++ "handle(0x%04x), sas_address(0x%016llx), phy(%d)\n", ++ sas_device->handle, ++ (unsigned long long)sas_device->sas_address, ++ sas_device->phy); ++ if (sas_device->enclosure_handle != 0) ++ starget_printk(KERN_INFO, starget, ++ "enclosure_logical_id(0x%016llx), slot(%d)\n", ++ (unsigned long long) ++ sas_device->enclosure_logical_id, ++ sas_device->slot); ++ if (sas_device->connector_name[0] != '\0') ++ starget_printk(KERN_INFO, starget, ++ "enclosure level(0x%04x),connector name(%s)\n", ++ sas_device->enclosure_level, ++ sas_device->connector_name); ++ ++ sas_device_put(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ } ++} ++ ++/** ++ * scsih_abort_mpt2sas - eh threads main abort routine ++ * @scmd: pointer to scsi command object ++ * ++ * Returns SUCCESS if command aborted else FAILED ++ */ ++int ++scsih_abort_mpt2sas(struct scsi_cmnd *scmd) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ u16 smid; ++ u16 handle; ++ int r; ++ ++ sdev_printk(KERN_INFO, scmd->device, ++ "attempting task abort! scmd(%p)\n", scmd); ++ _scsih_tm_display_info(ioc, scmd); ++ ++ sas_device_priv_data = scmd->device->hostdata; ++ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { ++ sdev_printk(KERN_INFO, scmd->device, ++ "device been deleted! scmd(%p)\n", scmd); ++ scmd->result = DID_NO_CONNECT << 16; ++ scmd->scsi_done(scmd); ++ r = SUCCESS; ++ goto out; ++ } ++ ++ /* search for the command */ ++ smid = _scsih_scsi_lookup_find_by_scmd(ioc, scmd); ++ if (!smid) { ++ scmd->result = DID_RESET << 16; ++ r = SUCCESS; ++ goto out; ++ } ++ ++ /* for hidden raid components and volumes this is not supported */ ++ if (sas_device_priv_data->sas_target->flags & ++ MPT_TARGET_FLAGS_RAID_COMPONENT || ++ sas_device_priv_data->sas_target->flags & MPT_TARGET_FLAGS_VOLUME) { ++ scmd->result = DID_RESET << 16; ++ r = FAILED; ++ goto out; ++ } ++ ++ mpt2sas_halt_firmware(ioc); ++ ++ handle = sas_device_priv_data->sas_target->handle; ++ r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, ++ scmd->device->id, scmd->device->lun, ++ MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, TM_MUTEX_ON); ++ ++ out: ++ sdev_printk(KERN_INFO, scmd->device, "task abort: %s scmd(%p)\n", ++ ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); ++ return r; ++} ++ ++/** ++ * scsih_dev_reset_mpt2sas - eh threads main device reset routine ++ * @scmd: pointer to scsi command object ++ * ++ * Returns SUCCESS if command aborted else FAILED ++ */ ++int ++scsih_dev_reset_mpt2sas(struct scsi_cmnd *scmd) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct _sas_device *sas_device = NULL; ++ u16 handle; ++ int r; ++ ++ struct scsi_target *starget = scmd->device->sdev_target; ++ struct MPT3SAS_TARGET *target_priv_data = starget->hostdata; ++ ++ sdev_printk(KERN_INFO, scmd->device, ++ "attempting device reset! scmd(%p)\n", scmd); ++ _scsih_tm_display_info(ioc, scmd); ++ ++ sas_device_priv_data = scmd->device->hostdata; ++ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { ++ sdev_printk(KERN_INFO, scmd->device, ++ "device been deleted! scmd(%p)\n", scmd); ++ scmd->result = DID_NO_CONNECT << 16; ++ scmd->scsi_done(scmd); ++ r = SUCCESS; ++ goto out; ++ } ++ ++ /* for hidden raid components obtain the volume_handle */ ++ handle = 0; ++ if (sas_device_priv_data->sas_target->flags & ++ MPT_TARGET_FLAGS_RAID_COMPONENT) { ++ sas_device = mpt2sas_get_sdev_from_target(ioc, ++ target_priv_data); ++ if (sas_device) ++ handle = sas_device->volume_handle; ++ } else ++ handle = sas_device_priv_data->sas_target->handle; ++ ++ if (!handle) { ++ scmd->result = DID_RESET << 16; ++ r = FAILED; ++ goto out; ++ } ++ ++ r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, ++ scmd->device->id, scmd->device->lun, ++ MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 30, TM_MUTEX_ON); ++ ++ out: ++ sdev_printk(KERN_INFO, scmd->device, "device reset: %s scmd(%p)\n", ++ ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); ++ ++ if (sas_device) ++ sas_device_put(sas_device); ++ ++ return r; ++} ++ ++/** ++ * scsih_target_reset_mpt2sas - eh threads main target reset routine ++ * @scmd: pointer to scsi command object ++ * ++ * Returns SUCCESS if command aborted else FAILED ++ */ ++int ++scsih_target_reset_mpt2sas(struct scsi_cmnd *scmd) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct _sas_device *sas_device = NULL; ++ u16 handle; ++ int r; ++ struct scsi_target *starget = scmd->device->sdev_target; ++ struct MPT3SAS_TARGET *target_priv_data = starget->hostdata; ++ ++ starget_printk(KERN_INFO, starget, "attempting target reset! scmd(%p)\n", ++ scmd); ++ _scsih_tm_display_info(ioc, scmd); ++ ++ sas_device_priv_data = scmd->device->hostdata; ++ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { ++ starget_printk(KERN_INFO, starget, "target been deleted! scmd(%p)\n", ++ scmd); ++ scmd->result = DID_NO_CONNECT << 16; ++ scmd->scsi_done(scmd); ++ r = SUCCESS; ++ goto out; ++ } ++ ++ /* for hidden raid components obtain the volume_handle */ ++ handle = 0; ++ if (sas_device_priv_data->sas_target->flags & ++ MPT_TARGET_FLAGS_RAID_COMPONENT) { ++ sas_device = mpt2sas_get_sdev_from_target(ioc, ++ target_priv_data); ++ if (sas_device) ++ handle = sas_device->volume_handle; ++ } else ++ handle = sas_device_priv_data->sas_target->handle; ++ ++ if (!handle) { ++ scmd->result = DID_RESET << 16; ++ r = FAILED; ++ goto out; ++ } ++ ++ r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, ++ scmd->device->id, 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, ++ 30, TM_MUTEX_ON); ++ ++ out: ++ starget_printk(KERN_INFO, starget, "target reset: %s scmd(%p)\n", ++ ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); ++ ++ if (sas_device) ++ sas_device_put(sas_device); ++ ++ return r; ++} ++ ++ ++/** ++ * scsih_host_reset_mpt2sas - eh threads main host reset routine ++ * @scmd: pointer to scsi command object ++ * ++ * Returns SUCCESS if command aborted else FAILED ++ */ ++int ++scsih_host_reset_mpt2sas(struct scsi_cmnd *scmd) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); ++ int r, retval; ++ ++ pr_info(MPT3SAS_FMT "attempting host reset! scmd(%p)\n", ++ ioc->name, scmd); ++ scsi_print_command(scmd); ++ ++ if (ioc->is_driver_loading) { ++ pr_info(MPT3SAS_FMT "Blocking the host reset\n", ++ ioc->name); ++ r = FAILED; ++ goto out; ++ } ++ ++ retval = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ r = (retval < 0) ? FAILED : SUCCESS; ++out: ++ pr_info(MPT3SAS_FMT "host reset: %s scmd(%p)\n", ++ ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); ++ ++ return r; ++} ++ ++/** ++ * _scsih_fw_event_add - insert and queue up fw_event ++ * @ioc: per adapter object ++ * @fw_event: object describing the event ++ * Context: This function will acquire ioc->fw_event_lock. ++ * ++ * This adds the firmware event object into link list, then queues it up to ++ * be processed from user context. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_fw_event_add(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) ++{ ++ unsigned long flags; ++ ++ if (ioc->firmware_event_thread == NULL) ++ return; ++ ++ spin_lock_irqsave(&ioc->fw_event_lock, flags); ++ fw_event_work_get(fw_event); ++ INIT_LIST_HEAD(&fw_event->list); ++ list_add_tail(&fw_event->list, &ioc->fw_event_list); ++ INIT_WORK(&fw_event->work, _firmware_event_work); ++ fw_event_work_get(fw_event); ++ queue_work(ioc->firmware_event_thread, &fw_event->work); ++ spin_unlock_irqrestore(&ioc->fw_event_lock, flags); ++} ++ ++/** ++ * _scsih_fw_event_del_from_list - delete fw_event from the list ++ * @ioc: per adapter object ++ * @fw_event: object describing the event ++ * Context: This function will acquire ioc->fw_event_lock. ++ * ++ * If the fw_event is on the fw_event_list, remove it and do a put. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_fw_event_del_from_list(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work ++ *fw_event) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->fw_event_lock, flags); ++ if (!list_empty(&fw_event->list)) { ++ list_del_init(&fw_event->list); ++ fw_event_work_put(fw_event); ++ } ++ spin_unlock_irqrestore(&ioc->fw_event_lock, flags); ++} ++ ++ ++ /** ++ * mpt2sas_send_trigger_data_event - send event for processing trigger data ++ * @ioc: per adapter object ++ * @event_data: trigger event data ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_send_trigger_data_event(struct MPT3SAS_ADAPTER *ioc, ++ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data) ++{ ++ struct fw_event_work *fw_event; ++ u16 sz; ++ ++ if (ioc->is_driver_loading) ++ return; ++ sz = sizeof(*event_data); ++ fw_event = alloc_fw_event_work(sz); ++ if (!fw_event) ++ return; ++ fw_event->event = MPT3SAS_PROCESS_TRIGGER_DIAG; ++ fw_event->ioc = ioc; ++ memcpy(fw_event->event_data, event_data, sizeof(*event_data)); ++ _scsih_fw_event_add(ioc, fw_event); ++ fw_event_work_put(fw_event); ++} ++ ++/** ++ * _scsih_error_recovery_delete_devices - remove devices not responding ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_error_recovery_delete_devices(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct fw_event_work *fw_event; ++ ++ if (ioc->is_driver_loading) ++ return; ++ fw_event = alloc_fw_event_work(0); ++ if (!fw_event) ++ return; ++ fw_event->event = MPT3SAS_REMOVE_UNRESPONDING_DEVICES; ++ fw_event->ioc = ioc; ++ _scsih_fw_event_add(ioc, fw_event); ++ fw_event_work_put(fw_event); ++} ++ ++/** ++ * mpt2sas_port_enable_complete - port enable completed (fake event) ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct fw_event_work *fw_event; ++ ++ fw_event = alloc_fw_event_work(0); ++ if (!fw_event) ++ return; ++ fw_event->event = MPT3SAS_PORT_ENABLE_COMPLETE; ++ fw_event->ioc = ioc; ++ _scsih_fw_event_add(ioc, fw_event); ++ fw_event_work_put(fw_event); ++} ++ ++static struct fw_event_work *dequeue_next_fw_event(struct MPT3SAS_ADAPTER *ioc) ++{ ++ unsigned long flags; ++ struct fw_event_work *fw_event = NULL; ++ ++ spin_lock_irqsave(&ioc->fw_event_lock, flags); ++ if (!list_empty(&ioc->fw_event_list)) { ++ fw_event = list_first_entry(&ioc->fw_event_list, ++ struct fw_event_work, list); ++ list_del_init(&fw_event->list); ++ } ++ spin_unlock_irqrestore(&ioc->fw_event_lock, flags); ++ ++ return fw_event; ++} ++ ++/** ++ * _scsih_fw_event_cleanup_queue - cleanup event queue ++ * @ioc: per adapter object ++ * ++ * Walk the firmware event queue, either killing timers, or waiting ++ * for outstanding events to complete ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_fw_event_cleanup_queue(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct fw_event_work *fw_event; ++ ++ if (list_empty(&ioc->fw_event_list) || ++ !ioc->firmware_event_thread || in_interrupt()) ++ return; ++ ++ while ((fw_event = dequeue_next_fw_event(ioc))) { ++ /* ++ * Wait on the fw_event to complete. If this returns 1, then ++ * the event was never executed, and we need a put for the ++ * reference the work had on the fw_event. ++ * ++ * If it did execute, we wait for it to finish, and the put will ++ * happen from _firmware_event_work() ++ */ ++ if (cancel_work_sync(&fw_event->work)) ++ fw_event_work_put(fw_event); ++ ++ fw_event_work_put(fw_event); ++ } ++} ++ ++/** ++ * _scsih_internal_device_block - block the sdev device ++ * @sdev: per device object ++ * @sas_device_priv_data : per device driver private data ++ * ++ * make sure device is blocked without error, if not ++ * print an error ++ */ ++static void ++_scsih_internal_device_block(struct scsi_device *sdev, ++ struct MPT3SAS_DEVICE *sas_device_priv_data) ++{ ++ int r = 0; ++ ++ sdev_printk(KERN_INFO, sdev, "device_block, handle(0x%04x)\n", ++ sas_device_priv_data->sas_target->handle); ++ sas_device_priv_data->block = 1; ++ ++ r = scsi_internal_device_block(sdev); ++ if (r == -EINVAL) ++ sdev_printk(KERN_WARNING, sdev, ++ "device_block failed with return(%d) for handle(0x%04x)\n", ++ sas_device_priv_data->sas_target->handle, r); ++} ++ ++/** ++ * _scsih_internal_device_unblock - unblock the sdev device ++ * @sdev: per device object ++ * @sas_device_priv_data : per device driver private data ++ * make sure device is unblocked without error, if not retry ++ * by blocking and then unblocking ++ */ ++ ++static void ++_scsih_internal_device_unblock(struct scsi_device *sdev, ++ struct MPT3SAS_DEVICE *sas_device_priv_data) ++{ ++ int r = 0; ++ ++ sdev_printk(KERN_WARNING, sdev, "device_unblock and setting to running, " ++ "handle(0x%04x)\n", sas_device_priv_data->sas_target->handle); ++ sas_device_priv_data->block = 0; ++ r = scsi_internal_device_unblock(sdev, SDEV_RUNNING); ++ if (r == -EINVAL) { ++ /* The device has been set to SDEV_RUNNING by SD layer during ++ * device addition but the request queue is still stopped by ++ * our earlier block call. We need to perform a block again ++ * to get the device to SDEV_BLOCK and then to SDEV_RUNNING */ ++ ++ sdev_printk(KERN_WARNING, sdev, ++ "device_unblock failed with return(%d) for handle(0x%04x) " ++ "performing a block followed by an unblock\n", ++ sas_device_priv_data->sas_target->handle, r); ++ sas_device_priv_data->block = 1; ++ r = scsi_internal_device_block(sdev); ++ if (r) ++ sdev_printk(KERN_WARNING, sdev, "retried device_block " ++ "failed with return(%d) for handle(0x%04x)\n", ++ sas_device_priv_data->sas_target->handle, r); ++ ++ sas_device_priv_data->block = 0; ++ r = scsi_internal_device_unblock(sdev, SDEV_RUNNING); ++ if (r) ++ sdev_printk(KERN_WARNING, sdev, "retried device_unblock" ++ " failed with return(%d) for handle(0x%04x)\n", ++ sas_device_priv_data->sas_target->handle, r); ++ } ++} ++ ++/** ++ * _scsih_ublock_io_all_device - unblock every device ++ * @ioc: per adapter object ++ * ++ * change the device state from block to running ++ */ ++static void ++_scsih_ublock_io_all_device(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_device *sdev; ++ ++ shost_for_each_device(sdev, ioc->shost) { ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data) ++ continue; ++ if (!sas_device_priv_data->block) ++ continue; ++ ++ dewtprintk(ioc, sdev_printk(KERN_INFO, sdev, ++ "device_running, handle(0x%04x)\n", ++ sas_device_priv_data->sas_target->handle)); ++ _scsih_internal_device_unblock(sdev, sas_device_priv_data); ++ } ++} ++ ++ ++/** ++ * _scsih_ublock_io_device - prepare device to be deleted ++ * @ioc: per adapter object ++ * @sas_addr: sas address ++ * ++ * unblock then put device in offline state ++ */ ++static void ++_scsih_ublock_io_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address) ++{ ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_device *sdev; ++ ++ shost_for_each_device(sdev, ioc->shost) { ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data) ++ continue; ++ if (sas_device_priv_data->sas_target->sas_address ++ != sas_address) ++ continue; ++ if (sas_device_priv_data->block) ++ _scsih_internal_device_unblock(sdev, ++ sas_device_priv_data); ++ } ++} ++ ++/** ++ * _scsih_block_io_all_device - set the device state to SDEV_BLOCK ++ * @ioc: per adapter object ++ * @handle: device handle ++ * ++ * During device pull we need to appropiately set the sdev state. ++ */ ++static void ++_scsih_block_io_all_device(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_device *sdev; ++ ++ shost_for_each_device(sdev, ioc->shost) { ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data) ++ continue; ++ if (sas_device_priv_data->block) ++ continue; ++ if (sas_device_priv_data->ignore_delay_remove) { ++ sdev_printk(KERN_INFO, sdev, ++ "%s skip device_block for SES handle(0x%04x)\n", ++ __func__, sas_device_priv_data->sas_target->handle); ++ continue; ++ } ++ _scsih_internal_device_block(sdev, sas_device_priv_data); ++ } ++} ++ ++/** ++ * _scsih_block_io_device - set the device state to SDEV_BLOCK ++ * @ioc: per adapter object ++ * @handle: device handle ++ * ++ * During device pull we need to appropiately set the sdev state. ++ */ ++static void ++_scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_device *sdev; ++ struct _sas_device *sas_device; ++ ++ sas_device = mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (!sas_device) ++ return; ++ ++ shost_for_each_device(sdev, ioc->shost) { ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data) ++ continue; ++ if (sas_device_priv_data->sas_target->handle != handle) ++ continue; ++ if (sas_device_priv_data->block) ++ continue; ++ if (sas_device->pend_sas_rphy_add) ++ continue; ++ if (sas_device_priv_data->ignore_delay_remove) { ++ sdev_printk(KERN_INFO, sdev, ++ "%s skip device_block for SES handle(0x%04x)\n", ++ __func__, sas_device_priv_data->sas_target->handle); ++ continue; ++ } ++ _scsih_internal_device_block(sdev, sas_device_priv_data); ++ } ++ ++ sas_device_put(sas_device); ++} ++ ++/** ++ * _scsih_block_io_to_children_attached_to_ex ++ * @ioc: per adapter object ++ * @sas_expander: the sas_device object ++ * ++ * This routine set sdev state to SDEV_BLOCK for all devices ++ * attached to this expander. This function called when expander is ++ * pulled. ++ */ ++static void ++_scsih_block_io_to_children_attached_to_ex(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_node *sas_expander) ++{ ++ struct _sas_port *mpt2sas_port; ++ struct _sas_device *sas_device; ++ struct _sas_node *expander_sibling; ++ unsigned long flags; ++ ++ if (!sas_expander) ++ return; ++ ++ list_for_each_entry(mpt2sas_port, ++ &sas_expander->sas_port_list, port_list) { ++ if (mpt2sas_port->remote_identify.device_type == ++ SAS_END_DEVICE) { ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ mpt2sas_port->remote_identify.sas_address); ++ if (sas_device) { ++ set_bit(sas_device->handle, ++ ioc->blocking_handles); ++ sas_device_put(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ } ++ } ++ ++ list_for_each_entry(mpt2sas_port, ++ &sas_expander->sas_port_list, port_list) { ++ ++ if (mpt2sas_port->remote_identify.device_type == ++ SAS_EDGE_EXPANDER_DEVICE || ++ mpt2sas_port->remote_identify.device_type == ++ SAS_FANOUT_EXPANDER_DEVICE) { ++ expander_sibling = ++ mpt2sas_scsih_expander_find_by_sas_address( ++ ioc, mpt2sas_port->remote_identify.sas_address); ++ _scsih_block_io_to_children_attached_to_ex(ioc, ++ expander_sibling); ++ } ++ } ++} ++ ++/** ++ * _scsih_block_io_to_children_attached_directly ++ * @ioc: per adapter object ++ * @event_data: topology change event data ++ * ++ * This routine set sdev state to SDEV_BLOCK for all devices ++ * direct attached during device pull. ++ */ ++static void ++_scsih_block_io_to_children_attached_directly(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataSasTopologyChangeList_t *event_data) ++{ ++ int i; ++ u16 handle; ++ u16 reason_code; ++ ++ for (i = 0; i < event_data->NumEntries; i++) { ++ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); ++ if (!handle) ++ continue; ++ reason_code = event_data->PHY[i].PhyStatus & ++ MPI2_EVENT_SAS_TOPO_RC_MASK; ++ if (reason_code == MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING) ++ _scsih_block_io_device(ioc, handle); ++ } ++} ++ ++/** ++ * _scsih_tm_tr_send - send task management request ++ * @ioc: per adapter object ++ * @handle: device handle ++ * Context: interrupt time. ++ * ++ * This code is to initiate the device removal handshake protocol ++ * with controller firmware. This function will issue target reset ++ * using high priority request queue. It will send a sas iounit ++ * control request (MPI2_SAS_OP_REMOVE_DEVICE) from this completion. ++ * ++ * This is designed to send muliple task management request at the same ++ * time to the fifo. If the fifo is full, we will append the request, ++ * and process it in a future completion. ++ */ ++static void ++_scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ Mpi2SCSITaskManagementRequest_t *mpi_request; ++ u16 smid; ++ struct _sas_device *sas_device = NULL; ++ struct MPT3SAS_TARGET *sas_target_priv_data = NULL; ++ u64 sas_address = 0; ++ unsigned long flags; ++ struct _tr_list *delayed_tr; ++ u32 ioc_state; ++ ++ if (ioc->remove_host) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host has been removed: handle(0x%04x)\n", ++ __func__, ioc->name, handle)); ++ return; ++ } else if (ioc->pci_error_recovery) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host in pci error recovery: handle(0x%04x)\n", ++ __func__, ioc->name, ++ handle)); ++ return; ++ } ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host is not operational: handle(0x%04x)\n", ++ __func__, ioc->name, ++ handle)); ++ return; ++ } ++ ++ /* if PD, then return */ ++ if (test_bit(handle, ioc->pd_handles)) ++ return; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (sas_device && sas_device->starget && ++ sas_device->starget->hostdata) { ++ sas_target_priv_data = sas_device->starget->hostdata; ++ sas_target_priv_data->deleted = 1; ++ sas_address = sas_device->sas_address; ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ if (sas_target_priv_data) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "setting delete flag: handle(0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, handle, ++ (unsigned long long)sas_address)); ++ if (sas_device->enclosure_handle != 0) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "setting delete flag:enclosure logical id(0x%016llx)," ++ " slot(%d)\n", ioc->name, (unsigned long long) ++ sas_device->enclosure_logical_id, ++ sas_device->slot)); ++ if (sas_device->connector_name[0] != '\0') ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "setting delete flag: enclosure level(0x%04x)," ++ " connector name( %s)\n", ioc->name, ++ sas_device->enclosure_level, ++ sas_device->connector_name)); ++ _scsih_ublock_io_device(ioc, sas_address); ++ sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE; ++ } ++ ++ smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx); ++ if (!smid) { ++ delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); ++ if (!delayed_tr) ++ goto out; ++ INIT_LIST_HEAD(&delayed_tr->list); ++ delayed_tr->handle = handle; ++ list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list); ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "DELAYED:tr:handle(0x%04x), (open)\n", ++ ioc->name, handle)); ++ goto out; ++ } ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "tr_send:handle(0x%04x), (open), smid(%d), cb(%d)\n", ++ ioc->name, handle, smid, ++ ioc->tm_tr_cb_idx)); ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; ++ mpi_request->DevHandle = cpu_to_le16(handle); ++ mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; ++ mpt2sas_base_put_smid_hi_priority(ioc, smid, 0); ++ mpt2sas_trigger_master(ioc, MASTER_TRIGGER_DEVICE_REMOVAL); ++ ++out: ++ if (sas_device) ++ sas_device_put(sas_device); ++} ++ ++/** ++ * _scsih_tm_tr_complete - ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: interrupt time. ++ * ++ * This is the target reset completion routine. ++ * This code is part of the code to initiate the device removal ++ * handshake protocol with controller firmware. ++ * It will send a sas iounit control request (MPI2_SAS_OP_REMOVE_DEVICE) ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_scsih_tm_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply) ++{ ++ u16 handle; ++ Mpi2SCSITaskManagementRequest_t *mpi_request_tm; ++ Mpi2SCSITaskManagementReply_t *mpi_reply = ++ mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ Mpi2SasIoUnitControlRequest_t *mpi_request; ++ u16 smid_sas_ctrl; ++ u32 ioc_state; ++ struct _sc_list *delayed_sc; ++ ++ if (ioc->remove_host) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host has been removed\n", __func__, ioc->name)); ++ return 1; ++ } else if (ioc->pci_error_recovery) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host in pci error recovery\n", __func__, ++ ioc->name)); ++ return 1; ++ } ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host is not operational\n", __func__, ioc->name)); ++ return 1; ++ } ++ if (unlikely(!mpi_reply)) { ++ pr_err(MPT3SAS_FMT "mpi_reply not valid at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return 1; ++ } ++ mpi_request_tm = mpt2sas_base_get_msg_frame(ioc, smid); ++ handle = le16_to_cpu(mpi_request_tm->DevHandle); ++ if (handle != le16_to_cpu(mpi_reply->DevHandle)) { ++ dewtprintk(ioc, pr_err(MPT3SAS_FMT ++ "spurious interrupt: handle(0x%04x:0x%04x), smid(%d)!!!\n", ++ ioc->name, handle, ++ le16_to_cpu(mpi_reply->DevHandle), smid)); ++ return 0; ++ } ++ ++ mpt2sas_trigger_master(ioc, MASTER_TRIGGER_TASK_MANAGMENT); ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), " ++ "loginfo(0x%08x), completed(%d)\n", ioc->name, ++ handle, smid, le16_to_cpu(mpi_reply->IOCStatus), ++ le32_to_cpu(mpi_reply->IOCLogInfo), ++ le32_to_cpu(mpi_reply->TerminationCount))); ++ ++ smid_sas_ctrl = mpt2sas_base_get_smid(ioc, ioc->tm_sas_control_cb_idx); ++ if (!smid_sas_ctrl) { ++ delayed_sc = kzalloc(sizeof(*delayed_sc), GFP_ATOMIC); ++ if (!delayed_sc) ++ return _scsih_check_for_pending_tm(ioc, smid); ++ INIT_LIST_HEAD(&delayed_sc->list); ++ delayed_sc->handle = mpi_request_tm->DevHandle; ++ list_add_tail(&delayed_sc->list, &ioc->delayed_sc_list); ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "DELAYED:sc:handle(0x%04x), (open)\n", ++ ioc->name, handle)); ++ return _scsih_check_for_pending_tm(ioc, smid); ++ } ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "sc_send:handle(0x%04x), (open), smid(%d), cb(%d)\n", ++ ioc->name, handle, smid_sas_ctrl, ++ ioc->tm_sas_control_cb_idx)); ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid_sas_ctrl); ++ memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; ++ mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE; ++ mpi_request->DevHandle = mpi_request_tm->DevHandle; ++ mpt2sas_base_put_smid_default(ioc, smid_sas_ctrl); ++ ++ return _scsih_check_for_pending_tm(ioc, smid); ++} ++ ++ ++/** ++ * _scsih_sas_control_complete - completion routine ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: interrupt time. ++ * ++ * This is the sas iounit control completion routine. ++ * This code is part of the code to initiate the device removal ++ * handshake protocol with controller firmware. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_scsih_sas_control_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ u8 msix_index, u32 reply) ++{ ++ Mpi2SasIoUnitControlReply_t *mpi_reply = ++ mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ ++ if (likely(mpi_reply)) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "sc_complete:handle(0x%04x), (open) " ++ "smid(%d), ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply->DevHandle), smid, ++ le16_to_cpu(mpi_reply->IOCStatus), ++ le32_to_cpu(mpi_reply->IOCLogInfo))); ++ } else { ++ pr_err(MPT3SAS_FMT "mpi_reply not valid at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ } ++ return mpt2sas_check_for_pending_internal_cmds(ioc, smid); ++} ++ ++/** ++ * _scsih_tm_tr_volume_send - send target reset request for volumes ++ * @ioc: per adapter object ++ * @handle: device handle ++ * Context: interrupt time. ++ * ++ * This is designed to send muliple task management request at the same ++ * time to the fifo. If the fifo is full, we will append the request, ++ * and process it in a future completion. ++ */ ++static void ++_scsih_tm_tr_volume_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ Mpi2SCSITaskManagementRequest_t *mpi_request; ++ u16 smid; ++ struct _tr_list *delayed_tr; ++ ++ if (ioc->shost_recovery || ioc->remove_host || ++ ioc->pci_error_recovery) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host reset in progress!\n", ++ __func__, ioc->name)); ++ return; ++ } ++ ++ smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_volume_cb_idx); ++ if (!smid) { ++ delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); ++ if (!delayed_tr) ++ return; ++ INIT_LIST_HEAD(&delayed_tr->list); ++ delayed_tr->handle = handle; ++ list_add_tail(&delayed_tr->list, &ioc->delayed_tr_volume_list); ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "DELAYED:tr:handle(0x%04x), (open)\n", ++ ioc->name, handle)); ++ return; ++ } ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "tr_send:handle(0x%04x), (open), smid(%d), cb(%d)\n", ++ ioc->name, handle, smid, ++ ioc->tm_tr_volume_cb_idx)); ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; ++ mpi_request->DevHandle = cpu_to_le16(handle); ++ mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; ++ mpt2sas_base_put_smid_hi_priority(ioc, smid, 0); ++} ++ ++/** ++ * _scsih_tm_volume_tr_complete - target reset completion ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: interrupt time. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_scsih_tm_volume_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ u8 msix_index, u32 reply) ++{ ++ u16 handle; ++ Mpi2SCSITaskManagementRequest_t *mpi_request_tm; ++ Mpi2SCSITaskManagementReply_t *mpi_reply = ++ mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ ++ if (ioc->shost_recovery || ioc->remove_host || ++ ioc->pci_error_recovery) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host reset in progress!\n", ++ __func__, ioc->name)); ++ return 1; ++ } ++ if (unlikely(!mpi_reply)) { ++ pr_err(MPT3SAS_FMT "mpi_reply not valid at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return 1; ++ } ++ ++ mpi_request_tm = mpt2sas_base_get_msg_frame(ioc, smid); ++ handle = le16_to_cpu(mpi_request_tm->DevHandle); ++ if (handle != le16_to_cpu(mpi_reply->DevHandle)) { ++ dewtprintk(ioc, pr_err(MPT3SAS_FMT ++ "spurious interrupt: handle(0x%04x:0x%04x), smid(%d)!!!\n", ++ ioc->name, handle, ++ le16_to_cpu(mpi_reply->DevHandle), smid)); ++ return 0; ++ } ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), " ++ "loginfo(0x%08x), completed(%d)\n", ioc->name, ++ handle, smid, le16_to_cpu(mpi_reply->IOCStatus), ++ le32_to_cpu(mpi_reply->IOCLogInfo), ++ le32_to_cpu(mpi_reply->TerminationCount))); ++ ++ return _scsih_check_for_pending_tm(ioc, smid); ++} ++ ++/** ++ * _scsih_issue_delayed_event_ack_mpt2sas - issue delayed Event ACK messages ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @event: Event ID ++ * @event_context: used to track events uniquely ++ * ++ * Context - processed in interrupt context. ++ */ ++void ++_scsih_issue_delayed_event_ack_mpt2sas(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 event, ++ u32 event_context) ++{ ++ Mpi2EventAckRequest_t *ack_request; ++ int i = smid - ioc->internal_smid; ++ unsigned long flags; ++ ++ /* Without releasing the smid just update the ++ * call back index and reuse the same smid for ++ * processing this delayed request ++ */ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ ioc->internal_lookup[i].cb_idx = ioc->base_cb_idx; ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "EVENT ACK: event(0x%04x), smid(%d), cb(%d)\n", ++ ioc->name, le16_to_cpu(event), smid, ++ ioc->base_cb_idx)); ++ ack_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ memset(ack_request, 0, sizeof(Mpi2EventAckRequest_t)); ++ ack_request->Function = MPI2_FUNCTION_EVENT_ACK; ++ ack_request->Event = event; ++ ack_request->EventContext = event_context; ++ ack_request->VF_ID = 0; /* TODO */ ++ ack_request->VP_ID = 0; ++ mpt2sas_base_put_smid_default(ioc, smid); ++} ++ ++/** ++ * _scsih_issue_delayed_sas_io_unit_ctrl_mpt2sas - issue delayed ++ * sas_io_unit_ctrl messages ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @handle: device handle ++ * ++ * Context - processed in interrupt context. ++ */ ++void ++_scsih_issue_delayed_sas_io_unit_ctrl_mpt2sas(struct MPT3SAS_ADAPTER *ioc, ++ u16 smid, u16 handle) ++ { ++ Mpi2SasIoUnitControlRequest_t *mpi_request; ++ u32 ioc_state; ++ int i = smid - ioc->internal_smid; ++ unsigned long flags; ++ ++ if (ioc->remove_host) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host has been removed\n", ++ __func__, ioc->name)); ++ return; ++ } else if (ioc->pci_error_recovery) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host in pci error recovery\n", ++ __func__, ioc->name)); ++ return; ++ } ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host is not operational\n", ++ __func__, ioc->name)); ++ return; ++ } ++ ++ /* Without releasing the smid just update the ++ * call back index and reuse the same smid for ++ * processing this delayed request ++ */ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ ioc->internal_lookup[i].cb_idx = ioc->tm_sas_control_cb_idx; ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "sc_send:handle(0x%04x), (open), smid(%d), cb(%d)\n", ++ ioc->name, le16_to_cpu(handle), smid, ++ ioc->tm_sas_control_cb_idx)); ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; ++ mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE; ++ mpi_request->DevHandle = handle; ++ mpt2sas_base_put_smid_default(ioc, smid); ++} ++ ++/** ++ * _scsih_check_for_pending_internal_cmds - check for pending internal messages ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Context: Executed in interrupt context ++ * ++ * This will check delayed internal messages list, and process the ++ * next request. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_check_for_pending_internal_cmds(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ struct _sc_list *delayed_sc; ++ struct _event_ack_list *delayed_event_ack; ++ ++ if (!list_empty(&ioc->delayed_event_ack_list)) { ++ delayed_event_ack = list_entry(ioc->delayed_event_ack_list.next, ++ struct _event_ack_list, list); ++ _scsih_issue_delayed_event_ack_mpt2sas(ioc, smid, ++ delayed_event_ack->Event, delayed_event_ack->EventContext); ++ list_del(&delayed_event_ack->list); ++ kfree(delayed_event_ack); ++ return 0; ++ } ++ ++ if (!list_empty(&ioc->delayed_sc_list)) { ++ delayed_sc = list_entry(ioc->delayed_sc_list.next, ++ struct _sc_list, list); ++ _scsih_issue_delayed_sas_io_unit_ctrl_mpt2sas(ioc, smid, ++ delayed_sc->handle); ++ list_del(&delayed_sc->list); ++ kfree(delayed_sc); ++ return 0; ++ } ++ return 1; ++} ++ ++/** ++ * _scsih_check_for_pending_tm - check for pending task management ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * This will check delayed target reset list, and feed the ++ * next reqeust. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_scsih_check_for_pending_tm(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ struct _tr_list *delayed_tr; ++ ++ if (!list_empty(&ioc->delayed_tr_volume_list)) { ++ delayed_tr = list_entry(ioc->delayed_tr_volume_list.next, ++ struct _tr_list, list); ++ mpt2sas_base_free_smid(ioc, smid); ++ _scsih_tm_tr_volume_send(ioc, delayed_tr->handle); ++ list_del(&delayed_tr->list); ++ kfree(delayed_tr); ++ return 0; ++ } ++ ++ if (!list_empty(&ioc->delayed_tr_list)) { ++ delayed_tr = list_entry(ioc->delayed_tr_list.next, ++ struct _tr_list, list); ++ mpt2sas_base_free_smid(ioc, smid); ++ _scsih_tm_tr_send(ioc, delayed_tr->handle); ++ list_del(&delayed_tr->list); ++ kfree(delayed_tr); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++/** ++ * _scsih_check_topo_delete_events - sanity check on topo events ++ * @ioc: per adapter object ++ * @event_data: the event data payload ++ * ++ * This routine added to better handle cable breaker. ++ * ++ * This handles the case where driver receives multiple expander ++ * add and delete events in a single shot. When there is a delete event ++ * the routine will void any pending add events waiting in the event queue. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_check_topo_delete_events(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataSasTopologyChangeList_t *event_data) ++{ ++ struct fw_event_work *fw_event; ++ Mpi2EventDataSasTopologyChangeList_t *local_event_data; ++ u16 expander_handle; ++ struct _sas_node *sas_expander; ++ unsigned long flags; ++ int i, reason_code; ++ u16 handle; ++ ++ for (i = 0 ; i < event_data->NumEntries; i++) { ++ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); ++ if (!handle) ++ continue; ++ reason_code = event_data->PHY[i].PhyStatus & ++ MPI2_EVENT_SAS_TOPO_RC_MASK; ++ if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING) ++ _scsih_tm_tr_send(ioc, handle); ++ } ++ ++ expander_handle = le16_to_cpu(event_data->ExpanderDevHandle); ++ if (expander_handle < ioc->sas_hba.num_phys) { ++ _scsih_block_io_to_children_attached_directly(ioc, event_data); ++ return; ++ } ++ if (event_data->ExpStatus == ++ MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING) { ++ /* put expander attached devices into blocking state */ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, ++ expander_handle); ++ _scsih_block_io_to_children_attached_to_ex(ioc, sas_expander); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ do { ++ handle = find_first_bit(ioc->blocking_handles, ++ ioc->facts.MaxDevHandle); ++ if (handle < ioc->facts.MaxDevHandle) ++ _scsih_block_io_device(ioc, handle); ++ } while (test_and_clear_bit(handle, ioc->blocking_handles)); ++ } else if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_RESPONDING) ++ _scsih_block_io_to_children_attached_directly(ioc, event_data); ++ ++ if (event_data->ExpStatus != MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING) ++ return; ++ ++ /* mark ignore flag for pending events */ ++ spin_lock_irqsave(&ioc->fw_event_lock, flags); ++ list_for_each_entry(fw_event, &ioc->fw_event_list, list) { ++ if (fw_event->event != MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST || ++ fw_event->ignore) ++ continue; ++ local_event_data = (Mpi2EventDataSasTopologyChangeList_t *) ++ fw_event->event_data; ++ if (local_event_data->ExpStatus == ++ MPI2_EVENT_SAS_TOPO_ES_ADDED || ++ local_event_data->ExpStatus == ++ MPI2_EVENT_SAS_TOPO_ES_RESPONDING) { ++ if (le16_to_cpu(local_event_data->ExpanderDevHandle) == ++ expander_handle) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "setting ignoring flag\n", ioc->name)); ++ fw_event->ignore = 1; ++ } ++ } ++ } ++ spin_unlock_irqrestore(&ioc->fw_event_lock, flags); ++} ++ ++/** ++ * _scsih_set_volume_delete_flag - setting volume delete flag ++ * @ioc: per adapter object ++ * @handle: device handle ++ * ++ * This returns nothing. ++ */ ++static void ++_scsih_set_volume_delete_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _raid_device *raid_device; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = mpt2sas_raid_device_find_by_handle(ioc, handle); ++ if (raid_device && raid_device->starget && ++ raid_device->starget->hostdata) { ++ sas_target_priv_data = ++ raid_device->starget->hostdata; ++ sas_target_priv_data->deleted = 1; ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "setting delete flag: handle(0x%04x), " ++ "wwid(0x%016llx)\n", ioc->name, handle, ++ (unsigned long long) raid_device->wwid)); ++ } ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++} ++ ++/** ++ * _scsih_set_volume_handle_for_tr - set handle for target reset to volume ++ * @handle: input handle ++ * @a: handle for volume a ++ * @b: handle for volume b ++ * ++ * IR firmware only supports two raid volumes. The purpose of this ++ * routine is to set the volume handle in either a or b. When the given ++ * input handle is non-zero, or when a and b have not been set before. ++ */ ++static void ++_scsih_set_volume_handle_for_tr(u16 handle, u16 *a, u16 *b) ++{ ++ if (!handle || handle == *a || handle == *b) ++ return; ++ if (!*a) ++ *a = handle; ++ else if (!*b) ++ *b = handle; ++} ++ ++/** ++ * _scsih_check_ir_config_unhide_events - check for UNHIDE events ++ * @ioc: per adapter object ++ * @event_data: the event data payload ++ * Context: interrupt time. ++ * ++ * This routine will send target reset to volume, followed by target ++ * resets to the PDs. This is called when a PD has been removed, or ++ * volume has been deleted or removed. When the target reset is sent ++ * to volume, the PD target resets need to be queued to start upon ++ * completion of the volume target reset. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_check_ir_config_unhide_events(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataIrConfigChangeList_t *event_data) ++{ ++ Mpi2EventIrConfigElement_t *element; ++ int i; ++ u16 handle, volume_handle, a, b; ++ struct _tr_list *delayed_tr; ++ ++ a = 0; ++ b = 0; ++ ++ if (ioc->is_warpdrive) ++ return; ++ ++ /* Volume Resets for Deleted or Removed */ ++ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; ++ for (i = 0; i < event_data->NumElements; i++, element++) { ++ if (le32_to_cpu(event_data->Flags) & ++ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ++ continue; ++ if (element->ReasonCode == ++ MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED || ++ element->ReasonCode == ++ MPI2_EVENT_IR_CHANGE_RC_REMOVED) { ++ volume_handle = le16_to_cpu(element->VolDevHandle); ++ _scsih_set_volume_delete_flag(ioc, volume_handle); ++ _scsih_set_volume_handle_for_tr(volume_handle, &a, &b); ++ } ++ } ++ ++ /* Volume Resets for UNHIDE events */ ++ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; ++ for (i = 0; i < event_data->NumElements; i++, element++) { ++ if (le32_to_cpu(event_data->Flags) & ++ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ++ continue; ++ if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_UNHIDE) { ++ volume_handle = le16_to_cpu(element->VolDevHandle); ++ _scsih_set_volume_handle_for_tr(volume_handle, &a, &b); ++ } ++ } ++ ++ if (a) ++ _scsih_tm_tr_volume_send(ioc, a); ++ if (b) ++ _scsih_tm_tr_volume_send(ioc, b); ++ ++ /* PD target resets */ ++ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; ++ for (i = 0; i < event_data->NumElements; i++, element++) { ++ if (element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_UNHIDE) ++ continue; ++ handle = le16_to_cpu(element->PhysDiskDevHandle); ++ volume_handle = le16_to_cpu(element->VolDevHandle); ++ clear_bit(handle, ioc->pd_handles); ++ if (!volume_handle) ++ _scsih_tm_tr_send(ioc, handle); ++ else if (volume_handle == a || volume_handle == b) { ++ delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); ++ BUG_ON(!delayed_tr); ++ INIT_LIST_HEAD(&delayed_tr->list); ++ delayed_tr->handle = handle; ++ list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list); ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "DELAYED:tr:handle(0x%04x), (open)\n", ioc->name, ++ handle)); ++ } else ++ _scsih_tm_tr_send(ioc, handle); ++ } ++} ++ ++ ++/** ++ * _scsih_check_volume_delete_events - set delete flag for volumes ++ * @ioc: per adapter object ++ * @event_data: the event data payload ++ * Context: interrupt time. ++ * ++ * This will handle the case when the cable connected to entire volume is ++ * pulled. We will take care of setting the deleted flag so normal IO will ++ * not be sent. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_check_volume_delete_events(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataIrVolume_t *event_data) ++{ ++ u32 state; ++ ++ if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED) ++ return; ++ state = le32_to_cpu(event_data->NewValue); ++ if (state == MPI2_RAID_VOL_STATE_MISSING || state == ++ MPI2_RAID_VOL_STATE_FAILED) ++ _scsih_set_volume_delete_flag(ioc, ++ le16_to_cpu(event_data->VolDevHandle)); ++} ++ ++/** ++ * _scsih_temp_threshold_events - display temperature threshold exceeded events ++ * @ioc: per adapter object ++ * @event_data: the temp threshold event data ++ * Context: interrupt time. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_temp_threshold_events(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataTemperature_t *event_data) ++{ ++ if (ioc->temp_sensors_count >= event_data->SensorNum) { ++ pr_err(MPT3SAS_FMT "Temperature Threshold flags %s%s%s%s" ++ " exceeded for Sensor: %d !!!\n", ioc->name, ++ ((le16_to_cpu(event_data->Status) & 0x1) == 1) ? "0 " : " ", ++ ((le16_to_cpu(event_data->Status) & 0x2) == 2) ? "1 " : " ", ++ ((le16_to_cpu(event_data->Status) & 0x4) == 4) ? "2 " : " ", ++ ((le16_to_cpu(event_data->Status) & 0x8) == 8) ? "3 " : " ", ++ event_data->SensorNum); ++ pr_err(MPT3SAS_FMT "Current Temp In Celsius: %d\n", ++ ioc->name, event_data->CurrentTemperature); ++ } ++} ++ ++/** ++ * _scsih_flush_running_cmds - completing outstanding commands. ++ * @ioc: per adapter object ++ * ++ * The flushing out of all pending scmd commands following host reset, ++ * where all IO is dropped to the floor. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_flush_running_cmds(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct scsi_cmnd *scmd; ++ u16 smid; ++ u16 count = 0; ++ ++ for (smid = 1; smid <= ioc->scsiio_depth; smid++) { ++ scmd = _scsih_scsi_lookup_get_clear(ioc, smid); ++ if (!scmd) ++ continue; ++ count++; ++ mpt2sas_base_free_smid(ioc, smid); ++ scsi_dma_unmap(scmd); ++ if (ioc->pci_error_recovery) ++ scmd->result = DID_NO_CONNECT << 16; ++ else ++ scmd->result = DID_RESET << 16; ++ scmd->scsi_done(scmd); ++ } ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT "completing %d cmds\n", ++ ioc->name, count)); ++} ++ ++/** ++ * _scsih_setup_eedp - setup MPI request for EEDP transfer ++ * @ioc: per adapter object ++ * @scmd: pointer to scsi command object ++ * @mpi_request: pointer to the SCSI_IO reqest message frame ++ * ++ * Supporting protection 1 and 3. ++ * ++ * Returns nothing ++ */ ++static void ++_scsih_setup_eedp(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, ++ Mpi2SCSIIORequest_t *mpi_request) ++{ ++ u16 eedp_flags; ++ unsigned char prot_op = scsi_get_prot_op(scmd); ++ unsigned char prot_type = scsi_get_prot_type(scmd); ++ Mpi25SCSIIORequest_t *mpi_request_3v = ++ (Mpi25SCSIIORequest_t *)mpi_request; ++ ++ if (prot_type == SCSI_PROT_DIF_TYPE0 || prot_op == SCSI_PROT_NORMAL) ++ return; ++ ++ if (prot_op == SCSI_PROT_READ_STRIP) ++ eedp_flags = MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP; ++ else if (prot_op == SCSI_PROT_WRITE_INSERT) ++ eedp_flags = MPI2_SCSIIO_EEDPFLAGS_INSERT_OP; ++ else ++ return; ++ ++ switch (prot_type) { ++ case SCSI_PROT_DIF_TYPE1: ++ case SCSI_PROT_DIF_TYPE2: ++ ++ /* ++ * enable ref/guard checking ++ * auto increment ref tag ++ */ ++ eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG | ++ MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG | ++ MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; ++ mpi_request->CDB.EEDP32.PrimaryReferenceTag = ++ cpu_to_be32(scsi_get_lba(scmd)); ++ break; ++ ++ case SCSI_PROT_DIF_TYPE3: ++ ++ /* ++ * enable guard checking ++ */ ++ eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; ++ ++ break; ++ } ++ ++ mpi_request_3v->EEDPBlockSize = ++ cpu_to_le16(scmd->device->sector_size); ++ mpi_request->EEDPFlags = cpu_to_le16(eedp_flags); ++} ++ ++/** ++ * _scsih_eedp_error_handling - return sense code for EEDP errors ++ * @scmd: pointer to scsi command object ++ * @ioc_status: ioc status ++ * ++ * Returns nothing ++ */ ++static void ++_scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status) ++{ ++ u8 ascq; ++ ++ switch (ioc_status) { ++ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: ++ ascq = 0x01; ++ break; ++ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR: ++ ascq = 0x02; ++ break; ++ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR: ++ ascq = 0x03; ++ break; ++ default: ++ ascq = 0x00; ++ break; ++ } ++ scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST, 0x10, ++ ascq); ++ scmd->result = DRIVER_SENSE << 24 | (DID_ABORT << 16) | ++ SAM_STAT_CHECK_CONDITION; ++} ++ ++ ++ ++/** ++ * scsih_qcmd_mpt2sas - main scsi request entry point ++ * @scmd: pointer to scsi command object ++ * @done: function pointer to be invoked on completion ++ * ++ * The callback index is set inside `ioc->scsi_io_cb_idx`. ++ * ++ * Returns 0 on success. If there's a failure, return either: ++ * SCSI_MLQUEUE_DEVICE_BUSY if the device queue is full, or ++ * SCSI_MLQUEUE_HOST_BUSY if the entire host queue is full ++ */ ++int ++scsih_qcmd_mpt2sas(struct Scsi_Host *shost, struct scsi_cmnd *scmd) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct _raid_device *raid_device; ++ Mpi2SCSIIORequest_t *mpi_request; ++ u32 mpi_control; ++ u16 smid; ++ u16 handle; ++ ++ if (ioc->logging_level & MPT_DEBUG_SCSI) ++ scsi_print_command(scmd); ++ ++ sas_device_priv_data = scmd->device->hostdata; ++ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { ++ scmd->result = DID_NO_CONNECT << 16; ++ scmd->scsi_done(scmd); ++ return 0; ++ } ++ ++ if (ioc->pci_error_recovery || ioc->remove_host) { ++ scmd->result = DID_NO_CONNECT << 16; ++ scmd->scsi_done(scmd); ++ return 0; ++ } ++ ++ sas_target_priv_data = sas_device_priv_data->sas_target; ++ ++ /* invalid device handle */ ++ handle = sas_target_priv_data->handle; ++ if (handle == MPT3SAS_INVALID_DEVICE_HANDLE) { ++ scmd->result = DID_NO_CONNECT << 16; ++ scmd->scsi_done(scmd); ++ return 0; ++ } ++ ++ ++ /* host recovery or link resets sent via IOCTLs */ ++ if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress) ++ return SCSI_MLQUEUE_HOST_BUSY; ++ ++ /* device has been deleted */ ++ else if (sas_target_priv_data->deleted) { ++ scmd->result = DID_NO_CONNECT << 16; ++ scmd->scsi_done(scmd); ++ return 0; ++ /* device busy with task managment */ ++ } else if (sas_target_priv_data->tm_busy || ++ sas_device_priv_data->block) ++ return SCSI_MLQUEUE_DEVICE_BUSY; ++ ++ if (scmd->sc_data_direction == DMA_FROM_DEVICE) ++ mpi_control = MPI2_SCSIIO_CONTROL_READ; ++ else if (scmd->sc_data_direction == DMA_TO_DEVICE) ++ mpi_control = MPI2_SCSIIO_CONTROL_WRITE; ++ else ++ mpi_control = MPI2_SCSIIO_CONTROL_NODATATRANSFER; ++ ++ /* set tags */ ++ if (!(sas_device_priv_data->flags & MPT_DEVICE_FLAGS_INIT)) { ++ if (scmd->device->tagged_supported) { ++ if (scmd->device->ordered_tags) ++ mpi_control |= MPI2_SCSIIO_CONTROL_ORDEREDQ; ++ else ++ mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; ++ } else ++ mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; ++ } else ++ mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; ++ ++ /* Make sure Device is not raid volume. ++ * We do not expose raid functionality to upper layer for warpdrive. ++ */ ++ if (!ioc->is_warpdrive && !scsih_is_raid_mpt2sas(&scmd->device->sdev_gendev) ++ && sas_is_tlr_enabled(scmd->device) && scmd->cmd_len != 32) ++ mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON; ++ ++ smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->scsi_io_cb_idx, scmd); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ goto out; ++ } ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ memset(mpi_request, 0, sizeof(Mpi2SCSIIORequest_t)); ++ _scsih_setup_eedp(ioc, scmd, mpi_request); ++ ++ if (scmd->cmd_len == 32) ++ mpi_control |= 4 << MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT; ++ mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; ++ if (sas_device_priv_data->sas_target->flags & ++ MPT_TARGET_FLAGS_RAID_COMPONENT) ++ mpi_request->Function = MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH; ++ else ++ mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; ++ mpi_request->DevHandle = cpu_to_le16(handle); ++ mpi_request->DataLength = cpu_to_le32(scsi_bufflen(scmd)); ++ mpi_request->Control = cpu_to_le32(mpi_control); ++ mpi_request->IoFlags = cpu_to_le16(scmd->cmd_len); ++ mpi_request->MsgFlags = MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR; ++ mpi_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE; ++ mpi_request->SenseBufferLowAddress = ++ mpt2sas_base_get_sense_buffer_dma(ioc, smid); ++ mpi_request->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4; ++ int_to_scsilun(sas_device_priv_data->lun, (struct scsi_lun *) ++ mpi_request->LUN); ++ memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len); ++ ++ if (mpi_request->DataLength) { ++ if (ioc->build_sg_scmd(ioc, scmd, smid)) { ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ } else ++ ioc->build_zero_len_sge(ioc, &mpi_request->SGL); ++ ++ raid_device = sas_target_priv_data->raid_device; ++ if (raid_device && raid_device->direct_io_enabled) ++ mpt2sas_setup_direct_io(ioc, scmd, raid_device, mpi_request, ++ smid); ++ ++ if (likely(mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)) { ++ if (sas_target_priv_data->flags & MPT_TARGET_FASTPATH_IO) { ++ mpi_request->IoFlags = cpu_to_le16(scmd->cmd_len | ++ MPI25_SCSIIO_IOFLAGS_FAST_PATH); ++ mpt2sas_base_put_smid_fast_path(ioc, smid, handle); ++ } else ++ mpt2sas_base_put_smid_scsi_io(ioc, smid, ++ le16_to_cpu(mpi_request->DevHandle)); ++ } else ++ mpt2sas_base_put_smid_default(ioc, smid); ++ return 0; ++ ++ out: ++ return SCSI_MLQUEUE_HOST_BUSY; ++} ++ ++/** ++ * _scsih_normalize_sense - normalize descriptor and fixed format sense data ++ * @sense_buffer: sense data returned by target ++ * @data: normalized skey/asc/ascq ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_normalize_sense(char *sense_buffer, struct sense_info *data) ++{ ++ if ((sense_buffer[0] & 0x7F) >= 0x72) { ++ /* descriptor format */ ++ data->skey = sense_buffer[1] & 0x0F; ++ data->asc = sense_buffer[2]; ++ data->ascq = sense_buffer[3]; ++ } else { ++ /* fixed format */ ++ data->skey = sense_buffer[2] & 0x0F; ++ data->asc = sense_buffer[12]; ++ data->ascq = sense_buffer[13]; ++ } ++} ++ ++/** ++ * _scsih_scsi_ioc_info - translated non-succesfull SCSI_IO request ++ * @ioc: per adapter object ++ * @scmd: pointer to scsi command object ++ * @mpi_reply: reply mf payload returned from firmware ++ * ++ * scsi_status - SCSI Status code returned from target device ++ * scsi_state - state info associated with SCSI_IO determined by ioc ++ * ioc_status - ioc supplied status info ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, ++ Mpi2SCSIIOReply_t *mpi_reply, u16 smid) ++{ ++ u32 response_info; ++ u8 *response_bytes; ++ u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ u8 scsi_state = mpi_reply->SCSIState; ++ u8 scsi_status = mpi_reply->SCSIStatus; ++ char *desc_ioc_state = NULL; ++ char *desc_scsi_status = NULL; ++ char *desc_scsi_state = ioc->tmp_string; ++ u32 log_info = le32_to_cpu(mpi_reply->IOCLogInfo); ++ struct _sas_device *sas_device = NULL; ++ struct scsi_target *starget = scmd->device->sdev_target; ++ struct MPT3SAS_TARGET *priv_target = starget->hostdata; ++ char *device_str = NULL; ++ ++ if (!priv_target) ++ return; ++ if (ioc->hide_ir_msg) ++ device_str = "WarpDrive"; ++ else ++ device_str = "volume"; ++ ++ if (log_info == 0x31170000) ++ return; ++ ++ switch (ioc_status) { ++ case MPI2_IOCSTATUS_SUCCESS: ++ desc_ioc_state = "success"; ++ break; ++ case MPI2_IOCSTATUS_INVALID_FUNCTION: ++ desc_ioc_state = "invalid function"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: ++ desc_ioc_state = "scsi recovered error"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE: ++ desc_ioc_state = "scsi invalid dev handle"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: ++ desc_ioc_state = "scsi device not there"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: ++ desc_ioc_state = "scsi data overrun"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: ++ desc_ioc_state = "scsi data underrun"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: ++ desc_ioc_state = "scsi io data error"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: ++ desc_ioc_state = "scsi protocol error"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: ++ desc_ioc_state = "scsi task terminated"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: ++ desc_ioc_state = "scsi residual mismatch"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: ++ desc_ioc_state = "scsi task mgmt failed"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: ++ desc_ioc_state = "scsi ioc terminated"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: ++ desc_ioc_state = "scsi ext terminated"; ++ break; ++ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: ++ desc_ioc_state = "eedp guard error"; ++ break; ++ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR: ++ desc_ioc_state = "eedp ref tag error"; ++ break; ++ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR: ++ desc_ioc_state = "eedp app tag error"; ++ break; ++ case MPI2_IOCSTATUS_INSUFFICIENT_POWER: ++ desc_ioc_state = "insufficient power"; ++ break; ++ default: ++ desc_ioc_state = "unknown"; ++ break; ++ } ++ ++ switch (scsi_status) { ++ case MPI2_SCSI_STATUS_GOOD: ++ desc_scsi_status = "good"; ++ break; ++ case MPI2_SCSI_STATUS_CHECK_CONDITION: ++ desc_scsi_status = "check condition"; ++ break; ++ case MPI2_SCSI_STATUS_CONDITION_MET: ++ desc_scsi_status = "condition met"; ++ break; ++ case MPI2_SCSI_STATUS_BUSY: ++ desc_scsi_status = "busy"; ++ break; ++ case MPI2_SCSI_STATUS_INTERMEDIATE: ++ desc_scsi_status = "intermediate"; ++ break; ++ case MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET: ++ desc_scsi_status = "intermediate condmet"; ++ break; ++ case MPI2_SCSI_STATUS_RESERVATION_CONFLICT: ++ desc_scsi_status = "reservation conflict"; ++ break; ++ case MPI2_SCSI_STATUS_COMMAND_TERMINATED: ++ desc_scsi_status = "command terminated"; ++ break; ++ case MPI2_SCSI_STATUS_TASK_SET_FULL: ++ desc_scsi_status = "task set full"; ++ break; ++ case MPI2_SCSI_STATUS_ACA_ACTIVE: ++ desc_scsi_status = "aca active"; ++ break; ++ case MPI2_SCSI_STATUS_TASK_ABORTED: ++ desc_scsi_status = "task aborted"; ++ break; ++ default: ++ desc_scsi_status = "unknown"; ++ break; ++ } ++ ++ desc_scsi_state[0] = '\0'; ++ if (!scsi_state) ++ desc_scsi_state = " "; ++ if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) ++ strcat(desc_scsi_state, "response info "); ++ if (scsi_state & MPI2_SCSI_STATE_TERMINATED) ++ strcat(desc_scsi_state, "state terminated "); ++ if (scsi_state & MPI2_SCSI_STATE_NO_SCSI_STATUS) ++ strcat(desc_scsi_state, "no status "); ++ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_FAILED) ++ strcat(desc_scsi_state, "autosense failed "); ++ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) ++ strcat(desc_scsi_state, "autosense valid "); ++ ++ scsi_print_command(scmd); ++ ++ if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) { ++ pr_warn(MPT3SAS_FMT "\t%s wwid(0x%016llx)\n", ioc->name, ++ device_str, (unsigned long long)priv_target->sas_address); ++ } else { ++ sas_device = mpt2sas_get_sdev_from_target(ioc, priv_target); ++ if (sas_device) { ++ pr_warn(MPT3SAS_FMT ++ "\tsas_address(0x%016llx), phy(%d)\n", ++ ioc->name, (unsigned long long) ++ sas_device->sas_address, sas_device->phy); ++ if (sas_device->enclosure_handle != 0) ++ pr_warn(MPT3SAS_FMT ++ "\tenclosure_logical_id(0x%016llx)," ++ "slot(%d)\n", ioc->name, ++ (unsigned long long) ++ sas_device->enclosure_logical_id, ++ sas_device->slot); ++ if (sas_device->connector_name[0]) ++ pr_warn(MPT3SAS_FMT ++ "\tenclosure level(0x%04x)," ++ " connector name( %s)\n", ioc->name, ++ sas_device->enclosure_level, ++ sas_device->connector_name); ++ ++ sas_device_put(sas_device); ++ } ++ } ++ ++ pr_warn(MPT3SAS_FMT ++ "\thandle(0x%04x), ioc_status(%s)(0x%04x), smid(%d)\n", ++ ioc->name, le16_to_cpu(mpi_reply->DevHandle), ++ desc_ioc_state, ioc_status, smid); ++ pr_warn(MPT3SAS_FMT ++ "\trequest_len(%d), underflow(%d), resid(%d)\n", ++ ioc->name, scsi_bufflen(scmd), scmd->underflow, ++ scsi_get_resid(scmd)); ++ pr_warn(MPT3SAS_FMT ++ "\ttag(%d), transfer_count(%d), sc->result(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply->TaskTag), ++ le32_to_cpu(mpi_reply->TransferCount), scmd->result); ++ pr_warn(MPT3SAS_FMT ++ "\tscsi_status(%s)(0x%02x), scsi_state(%s)(0x%02x)\n", ++ ioc->name, desc_scsi_status, ++ scsi_status, desc_scsi_state, scsi_state); ++ ++ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) { ++ struct sense_info data; ++ _scsih_normalize_sense(scmd->sense_buffer, &data); ++ pr_warn(MPT3SAS_FMT ++ "\t[sense_key,asc,ascq]: [0x%02x,0x%02x,0x%02x], count(%d)\n", ++ ioc->name, data.skey, ++ data.asc, data.ascq, le32_to_cpu(mpi_reply->SenseCount)); ++ } ++ ++ if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) { ++ response_info = le32_to_cpu(mpi_reply->ResponseInfo); ++ response_bytes = (u8 *)&response_info; ++ _scsih_response_code(ioc, response_bytes[0]); ++ } ++} ++ ++/** ++ * _scsih_turn_on_pfa_led - illuminate PFA LED ++ * @ioc: per adapter object ++ * @handle: device handle ++ * Context: process ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_turn_on_pfa_led(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ Mpi2SepReply_t mpi_reply; ++ Mpi2SepRequest_t mpi_request; ++ struct _sas_device *sas_device; ++ ++ sas_device = mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (!sas_device) ++ return; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; ++ mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS; ++ mpi_request.SlotStatus = ++ cpu_to_le32(MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT); ++ mpi_request.DevHandle = cpu_to_le16(handle); ++ mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS; ++ if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, ++ &mpi_request)) != 0) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ sas_device->pfa_led_on = 1; ++ ++ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "enclosure_processor: ioc_status (0x%04x), loginfo(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply.IOCStatus), ++ le32_to_cpu(mpi_reply.IOCLogInfo))); ++ goto out; ++ } ++out: ++ sas_device_put(sas_device); ++} ++ ++/** ++ * _scsih_turn_off_pfa_led - turn off Fault LED ++ * @ioc: per adapter object ++ * @sas_device: sas device whose PFA LED has to turned off ++ * Context: process ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_turn_off_pfa_led(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_device *sas_device) ++{ ++ Mpi2SepReply_t mpi_reply; ++ Mpi2SepRequest_t mpi_request; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; ++ mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS; ++ mpi_request.SlotStatus = 0; ++ mpi_request.Slot = cpu_to_le16(sas_device->slot); ++ mpi_request.DevHandle = 0; ++ mpi_request.EnclosureHandle = cpu_to_le16(sas_device->enclosure_handle); ++ mpi_request.Flags = MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS; ++ if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, ++ &mpi_request)) != 0) { ++ printk(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) { ++ dewtprintk(ioc, printk(MPT3SAS_FMT ++ "enclosure_processor: ioc_status (0x%04x), loginfo(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply.IOCStatus), ++ le32_to_cpu(mpi_reply.IOCLogInfo))); ++ return; ++ } ++} ++ ++/** ++ * _scsih_send_event_to_turn_on_pfa_led - fire delayed event ++ * @ioc: per adapter object ++ * @handle: device handle ++ * Context: interrupt. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_send_event_to_turn_on_pfa_led(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct fw_event_work *fw_event; ++ ++ fw_event = alloc_fw_event_work(0); ++ if (!fw_event) ++ return; ++ fw_event->event = MPT3SAS_TURN_ON_PFA_LED; ++ fw_event->device_handle = handle; ++ fw_event->ioc = ioc; ++ _scsih_fw_event_add(ioc, fw_event); ++ fw_event_work_put(fw_event); ++} ++ ++/** ++ * _scsih_smart_predicted_fault - process smart errors ++ * @ioc: per adapter object ++ * @handle: device handle ++ * Context: interrupt. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_smart_predicted_fault(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct scsi_target *starget; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ Mpi2EventNotificationReply_t *event_reply; ++ Mpi2EventDataSasDeviceStatusChange_t *event_data; ++ struct _sas_device *sas_device; ++ ssize_t sz; ++ unsigned long flags; ++ ++ /* only handle non-raid devices */ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (!sas_device) ++ goto out_unlock; ++ ++ starget = sas_device->starget; ++ sas_target_priv_data = starget->hostdata; ++ ++ if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) || ++ ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))) ++ goto out_unlock; ++ ++ if (sas_device->enclosure_handle != 0) ++ starget_printk(KERN_INFO, starget, "predicted fault, " ++ "enclosure logical id(0x%016llx), slot(%d)\n", ++ (unsigned long long)sas_device->enclosure_logical_id, ++ sas_device->slot); ++ if (sas_device->connector_name[0] != '\0') ++ starget_printk(KERN_WARNING, starget, "predicted fault, " ++ "enclosure level(0x%04x), connector name( %s)\n", ++ sas_device->enclosure_level, ++ sas_device->connector_name); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) ++ _scsih_send_event_to_turn_on_pfa_led(ioc, handle); ++ ++ /* insert into event log */ ++ sz = offsetof(Mpi2EventNotificationReply_t, EventData) + ++ sizeof(Mpi2EventDataSasDeviceStatusChange_t); ++ event_reply = kzalloc(sz, GFP_KERNEL); ++ if (!event_reply) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ++ event_reply->Function = MPI2_FUNCTION_EVENT_NOTIFICATION; ++ event_reply->Event = ++ cpu_to_le16(MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE); ++ event_reply->MsgLength = sz/4; ++ event_reply->EventDataLength = ++ cpu_to_le16(sizeof(Mpi2EventDataSasDeviceStatusChange_t)/4); ++ event_data = (Mpi2EventDataSasDeviceStatusChange_t *) ++ event_reply->EventData; ++ event_data->ReasonCode = MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA; ++ event_data->ASC = 0x5D; ++ event_data->DevHandle = cpu_to_le16(handle); ++ event_data->SASAddress = cpu_to_le64(sas_target_priv_data->sas_address); ++ mpt2sas_ctl_add_to_event_log(ioc, event_reply); ++ kfree(event_reply); ++out: ++ if (sas_device) ++ sas_device_put(sas_device); ++ return; ++ ++out_unlock: ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ goto out; ++} ++ ++/** ++ * _scsih_io_done - scsi request callback ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * ++ * Callback handler when using _scsih_qcmd_mpt2sas. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) ++{ ++ Mpi2SCSIIORequest_t *mpi_request; ++ Mpi2SCSIIOReply_t *mpi_reply; ++ struct scsi_cmnd *scmd; ++ u16 ioc_status; ++ u32 xfer_cnt; ++ u8 scsi_state; ++ u8 scsi_status; ++ u32 log_info; ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ u32 response_code = 0; ++ unsigned long flags; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ scmd = _scsih_scsi_lookup_get_clear(ioc, smid); ++ if (scmd == NULL) ++ return 1; ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ++ if (mpi_reply == NULL) { ++ scmd->result = DID_OK << 16; ++ goto out; ++ } ++ ++ sas_device_priv_data = scmd->device->hostdata; ++ if (!sas_device_priv_data || !sas_device_priv_data->sas_target || ++ sas_device_priv_data->sas_target->deleted) { ++ scmd->result = DID_NO_CONNECT << 16; ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus); ++ ++ /* ++ * WARPDRIVE: If direct_io is set then it is directIO, ++ * the failed direct I/O should be redirected to volume ++ */ ++ if (mpt2sas_scsi_direct_io_get(ioc, smid) && ++ ((ioc_status & MPI2_IOCSTATUS_MASK) ++ != MPI2_IOCSTATUS_SCSI_TASK_TERMINATED)) { ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ ioc->scsi_lookup[smid - 1].scmd = scmd; ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ mpt2sas_scsi_direct_io_set(ioc, smid, 0); ++ memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len); ++ mpi_request->DevHandle = ++ cpu_to_le16(sas_device_priv_data->sas_target->handle); ++ mpt2sas_base_put_smid_scsi_io(ioc, smid, ++ sas_device_priv_data->sas_target->handle); ++ return 0; ++ } ++ /* turning off TLR */ ++ scsi_state = mpi_reply->SCSIState; ++ if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) ++ response_code = ++ le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF; ++ if (!sas_device_priv_data->tlr_snoop_check) { ++ sas_device_priv_data->tlr_snoop_check++; ++ if (!ioc->is_warpdrive && ++ !scsih_is_raid_mpt2sas(&scmd->device->sdev_gendev) && ++ sas_is_tlr_enabled(scmd->device) && ++ response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) { ++ sas_disable_tlr(scmd->device); ++ sdev_printk(KERN_INFO, scmd->device, "TLR disabled\n"); ++ } ++ } ++ ++ xfer_cnt = le32_to_cpu(mpi_reply->TransferCount); ++ scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_cnt); ++ if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) ++ log_info = le32_to_cpu(mpi_reply->IOCLogInfo); ++ else ++ log_info = 0; ++ ioc_status &= MPI2_IOCSTATUS_MASK; ++ scsi_status = mpi_reply->SCSIStatus; ++ ++ if (ioc_status == MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 && ++ (scsi_status == MPI2_SCSI_STATUS_BUSY || ++ scsi_status == MPI2_SCSI_STATUS_RESERVATION_CONFLICT || ++ scsi_status == MPI2_SCSI_STATUS_TASK_SET_FULL)) { ++ ioc_status = MPI2_IOCSTATUS_SUCCESS; ++ } ++ ++ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) { ++ struct sense_info data; ++ const void *sense_data = mpt2sas_base_get_sense_buffer(ioc, ++ smid); ++ u32 sz = min_t(u32, SCSI_SENSE_BUFFERSIZE, ++ le32_to_cpu(mpi_reply->SenseCount)); ++ memcpy(scmd->sense_buffer, sense_data, sz); ++ _scsih_normalize_sense(scmd->sense_buffer, &data); ++ /* failure prediction threshold exceeded */ ++ if (data.asc == 0x5D) ++ _scsih_smart_predicted_fault(ioc, ++ le16_to_cpu(mpi_reply->DevHandle)); ++ mpt2sas_trigger_scsi(ioc, data.skey, data.asc, data.ascq); ++ ++ if (!(ioc->logging_level & MPT_DEBUG_REPLY) && ++ ((scmd->sense_buffer[2] == UNIT_ATTENTION) || ++ (scmd->sense_buffer[2] == MEDIUM_ERROR) || ++ (scmd->sense_buffer[2] == HARDWARE_ERROR))) ++ _scsih_scsi_ioc_info(ioc, scmd, mpi_reply, smid); ++ } ++ switch (ioc_status) { ++ case MPI2_IOCSTATUS_BUSY: ++ case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES: ++ scmd->result = SAM_STAT_BUSY; ++ break; ++ ++ case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: ++ scmd->result = DID_NO_CONNECT << 16; ++ break; ++ ++ case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: ++ if (sas_device_priv_data->block) { ++ scmd->result = DID_TRANSPORT_DISRUPTED << 16; ++ goto out; ++ } ++ if (log_info == 0x31110630) { ++ if (scmd->retries > 2) { ++ scmd->result = DID_NO_CONNECT << 16; ++ scsi_device_set_state(scmd->device, ++ SDEV_OFFLINE); ++ } else { ++ scmd->result = DID_SOFT_ERROR << 16; ++ scmd->device->expecting_cc_ua = 1; ++ } ++ break; ++ } else if (log_info == VIRTUAL_IO_FAILED_RETRY) { ++ scmd->result = DID_RESET << 16; ++ break; ++ } ++ scmd->result = DID_SOFT_ERROR << 16; ++ break; ++ case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: ++ case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: ++ scmd->result = DID_RESET << 16; ++ break; ++ ++ case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: ++ if ((xfer_cnt == 0) || (scmd->underflow > xfer_cnt)) ++ scmd->result = DID_SOFT_ERROR << 16; ++ else ++ scmd->result = (DID_OK << 16) | scsi_status; ++ break; ++ ++ case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: ++ scmd->result = (DID_OK << 16) | scsi_status; ++ ++ if ((scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID)) ++ break; ++ ++ if (xfer_cnt < scmd->underflow) { ++ if (scsi_status == SAM_STAT_BUSY) ++ scmd->result = SAM_STAT_BUSY; ++ else ++ scmd->result = DID_SOFT_ERROR << 16; ++ } else if (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED | ++ MPI2_SCSI_STATE_NO_SCSI_STATUS)) ++ scmd->result = DID_SOFT_ERROR << 16; ++ else if (scsi_state & MPI2_SCSI_STATE_TERMINATED) ++ scmd->result = DID_RESET << 16; ++ else if (!xfer_cnt && scmd->cmnd[0] == REPORT_LUNS) { ++ mpi_reply->SCSIState = MPI2_SCSI_STATE_AUTOSENSE_VALID; ++ mpi_reply->SCSIStatus = SAM_STAT_CHECK_CONDITION; ++ scmd->result = (DRIVER_SENSE << 24) | ++ SAM_STAT_CHECK_CONDITION; ++ scmd->sense_buffer[0] = 0x70; ++ scmd->sense_buffer[2] = ILLEGAL_REQUEST; ++ scmd->sense_buffer[12] = 0x20; ++ scmd->sense_buffer[13] = 0; ++ } ++ break; ++ ++ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: ++ scsi_set_resid(scmd, 0); ++ case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: ++ case MPI2_IOCSTATUS_SUCCESS: ++ scmd->result = (DID_OK << 16) | scsi_status; ++ if (response_code == ++ MPI2_SCSITASKMGMT_RSP_INVALID_FRAME || ++ (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED | ++ MPI2_SCSI_STATE_NO_SCSI_STATUS))) ++ scmd->result = DID_SOFT_ERROR << 16; ++ else if (scsi_state & MPI2_SCSI_STATE_TERMINATED) ++ scmd->result = DID_RESET << 16; ++ break; ++ ++ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: ++ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR: ++ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR: ++ _scsih_eedp_error_handling(scmd, ioc_status); ++ break; ++ ++ case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: ++ case MPI2_IOCSTATUS_INVALID_FUNCTION: ++ case MPI2_IOCSTATUS_INVALID_SGL: ++ case MPI2_IOCSTATUS_INTERNAL_ERROR: ++ case MPI2_IOCSTATUS_INVALID_FIELD: ++ case MPI2_IOCSTATUS_INVALID_STATE: ++ case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: ++ case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: ++ case MPI2_IOCSTATUS_INSUFFICIENT_POWER: ++ default: ++ scmd->result = DID_SOFT_ERROR << 16; ++ break; ++ ++ } ++ ++ if (scmd->result && (ioc->logging_level & MPT_DEBUG_REPLY)) ++ _scsih_scsi_ioc_info(ioc , scmd, mpi_reply, smid); ++ ++ out: ++ ++ scsi_dma_unmap(scmd); ++ ++ scmd->scsi_done(scmd); ++ return 1; ++} ++ ++/** ++ * _scsih_sas_host_refresh - refreshing sas host object contents ++ * @ioc: per adapter object ++ * Context: user ++ * ++ * During port enable, fw will send topology events for every device. Its ++ * possible that the handles may change from the previous setting, so this ++ * code keeping handles updating if changed. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_host_refresh(struct MPT3SAS_ADAPTER *ioc) ++{ ++ u16 sz; ++ u16 ioc_status; ++ int i; ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; ++ u16 attached_handle; ++ u8 link_rate; ++ ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "updating handles for sas_host(0x%016llx)\n", ++ ioc->name, (unsigned long long)ioc->sas_hba.sas_address)); ++ ++ sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys ++ * sizeof(Mpi2SasIOUnit0PhyData_t)); ++ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); ++ if (!sas_iounit_pg0) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, ++ sas_iounit_pg0, sz)) != 0) ++ goto out; ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ goto out; ++ for (i = 0; i < ioc->sas_hba.num_phys ; i++) { ++ link_rate = sas_iounit_pg0->PhyData[i].NegotiatedLinkRate >> 4; ++ if (i == 0) ++ ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0-> ++ PhyData[0].ControllerDevHandle); ++ ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle; ++ attached_handle = le16_to_cpu(sas_iounit_pg0->PhyData[i]. ++ AttachedDevHandle); ++ if (attached_handle && link_rate < MPI2_SAS_NEG_LINK_RATE_1_5) ++ link_rate = MPI2_SAS_NEG_LINK_RATE_1_5; ++ mpt2sas_transport_update_links(ioc, ioc->sas_hba.sas_address, ++ attached_handle, i, link_rate); ++ } ++ out: ++ kfree(sas_iounit_pg0); ++} ++ ++/** ++ * _scsih_sas_host_add - create sas host object ++ * @ioc: per adapter object ++ * ++ * Creating host side data object, stored in ioc->sas_hba ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_host_add(struct MPT3SAS_ADAPTER *ioc) ++{ ++ int i; ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; ++ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; ++ Mpi2SasPhyPage0_t phy_pg0; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ Mpi2SasEnclosurePage0_t enclosure_pg0; ++ u16 ioc_status; ++ u16 sz; ++ u8 device_missing_delay; ++ ++ mpt2sas_config_get_number_hba_phys(ioc, &ioc->sas_hba.num_phys); ++ if (!ioc->sas_hba.num_phys) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ /* sas_iounit page 0 */ ++ sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys * ++ sizeof(Mpi2SasIOUnit0PhyData_t)); ++ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); ++ if (!sas_iounit_pg0) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, ++ sas_iounit_pg0, sz))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ++ /* sas_iounit page 1 */ ++ sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * ++ sizeof(Mpi2SasIOUnit1PhyData_t)); ++ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); ++ if (!sas_iounit_pg1) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, ++ sas_iounit_pg1, sz))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ++ ioc->io_missing_delay = ++ sas_iounit_pg1->IODeviceMissingDelay; ++ device_missing_delay = ++ sas_iounit_pg1->ReportDeviceMissingDelay; ++ if (device_missing_delay & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16) ++ ioc->device_missing_delay = (device_missing_delay & ++ MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16; ++ else ++ ioc->device_missing_delay = device_missing_delay & ++ MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK; ++ ++ ioc->sas_hba.parent_dev = &ioc->shost->shost_gendev; ++ ioc->sas_hba.phy = kcalloc(ioc->sas_hba.num_phys, ++ sizeof(struct _sas_phy), GFP_KERNEL); ++ if (!ioc->sas_hba.phy) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ for (i = 0; i < ioc->sas_hba.num_phys ; i++) { ++ if ((mpt2sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0, ++ i))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ++ if (i == 0) ++ ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0-> ++ PhyData[0].ControllerDevHandle); ++ ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle; ++ ioc->sas_hba.phy[i].phy_id = i; ++ mpt2sas_transport_add_host_phy(ioc, &ioc->sas_hba.phy[i], ++ phy_pg0, ioc->sas_hba.parent_dev); ++ } ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, ++ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ioc->sas_hba.enclosure_handle = ++ le16_to_cpu(sas_device_pg0.EnclosureHandle); ++ ioc->sas_hba.sas_address = le64_to_cpu(sas_device_pg0.SASAddress); ++ pr_info(MPT3SAS_FMT ++ "host_add: handle(0x%04x), sas_addr(0x%016llx), phys(%d)\n", ++ ioc->name, ioc->sas_hba.handle, ++ (unsigned long long) ioc->sas_hba.sas_address, ++ ioc->sas_hba.num_phys) ; ++ ++ if (ioc->sas_hba.enclosure_handle) { ++ if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply, ++ &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, ++ ioc->sas_hba.enclosure_handle))) ++ ioc->sas_hba.enclosure_logical_id = ++ le64_to_cpu(enclosure_pg0.EnclosureLogicalID); ++ } ++ ++ out: ++ kfree(sas_iounit_pg1); ++ kfree(sas_iounit_pg0); ++} ++ ++/** ++ * _scsih_expander_add - creating expander object ++ * @ioc: per adapter object ++ * @handle: expander handle ++ * ++ * Creating expander object, stored in ioc->sas_expander_list. ++ * ++ * Return 0 for success, else error. ++ */ ++static int ++_scsih_expander_add(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _sas_node *sas_expander; ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2ExpanderPage0_t expander_pg0; ++ Mpi2ExpanderPage1_t expander_pg1; ++ Mpi2SasEnclosurePage0_t enclosure_pg0; ++ u32 ioc_status; ++ u16 parent_handle; ++ u64 sas_address, sas_address_parent = 0; ++ int i; ++ unsigned long flags; ++ struct _sas_port *mpt2sas_port = NULL; ++ ++ int rc = 0; ++ ++ if (!handle) ++ return -1; ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery) ++ return -1; ++ ++ if ((mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, ++ MPI2_SAS_EXPAND_PGAD_FORM_HNDL, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ ++ /* handle out of order topology events */ ++ parent_handle = le16_to_cpu(expander_pg0.ParentDevHandle); ++ if (_scsih_get_sas_address(ioc, parent_handle, &sas_address_parent) ++ != 0) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ if (sas_address_parent != ioc->sas_hba.sas_address) { ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, ++ sas_address_parent); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ if (!sas_expander) { ++ rc = _scsih_expander_add(ioc, parent_handle); ++ if (rc != 0) ++ return rc; ++ } ++ } ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_address = le64_to_cpu(expander_pg0.SASAddress); ++ sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, ++ sas_address); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ if (sas_expander) ++ return 0; ++ ++ sas_expander = kzalloc(sizeof(struct _sas_node), ++ GFP_KERNEL); ++ if (!sas_expander) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ ++ sas_expander->handle = handle; ++ sas_expander->num_phys = expander_pg0.NumPhys; ++ sas_expander->sas_address_parent = sas_address_parent; ++ sas_expander->sas_address = sas_address; ++ ++ pr_info(MPT3SAS_FMT "expander_add: handle(0x%04x)," \ ++ " parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n", ioc->name, ++ handle, parent_handle, (unsigned long long) ++ sas_expander->sas_address, sas_expander->num_phys); ++ ++ if (!sas_expander->num_phys) ++ goto out_fail; ++ sas_expander->phy = kcalloc(sas_expander->num_phys, ++ sizeof(struct _sas_phy), GFP_KERNEL); ++ if (!sas_expander->phy) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -1; ++ goto out_fail; ++ } ++ ++ INIT_LIST_HEAD(&sas_expander->sas_port_list); ++ mpt2sas_port = mpt2sas_transport_port_add(ioc, handle, ++ sas_address_parent); ++ if (!mpt2sas_port) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -1; ++ goto out_fail; ++ } ++ sas_expander->parent_dev = &mpt2sas_port->rphy->dev; ++ ++ for (i = 0 ; i < sas_expander->num_phys ; i++) { ++ if ((mpt2sas_config_get_expander_pg1(ioc, &mpi_reply, ++ &expander_pg1, i, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -1; ++ goto out_fail; ++ } ++ sas_expander->phy[i].handle = handle; ++ sas_expander->phy[i].phy_id = i; ++ ++ if ((mpt2sas_transport_add_expander_phy(ioc, ++ &sas_expander->phy[i], expander_pg1, ++ sas_expander->parent_dev))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -1; ++ goto out_fail; ++ } ++ } ++ ++ if (sas_expander->enclosure_handle) { ++ if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply, ++ &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, ++ sas_expander->enclosure_handle))) ++ sas_expander->enclosure_logical_id = ++ le64_to_cpu(enclosure_pg0.EnclosureLogicalID); ++ } ++ ++ _scsih_expander_node_add(ioc, sas_expander); ++ return 0; ++ ++ out_fail: ++ ++ if (mpt2sas_port) ++ mpt2sas_transport_port_remove(ioc, sas_expander->sas_address, ++ sas_address_parent); ++ kfree(sas_expander); ++ return rc; ++} ++ ++/** ++ * mpt2sas_expander_remove - removing expander object ++ * @ioc: per adapter object ++ * @sas_address: expander sas_address ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address) ++{ ++ struct _sas_node *sas_expander; ++ unsigned long flags; ++ ++ if (ioc->shost_recovery) ++ return; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, ++ sas_address); ++ if (sas_expander) ++ list_del(&sas_expander->list); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ if (sas_expander) ++ _scsih_expander_node_remove(ioc, sas_expander); ++} ++ ++/** ++ * _scsih_done - internal SCSI_IO callback handler. ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * ++ * Callback handler when sending internal generated SCSI_IO. ++ * The callback index passed is `ioc->scsih_cb_idx` ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_scsih_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (ioc->scsih_cmds.status == MPT3_CMD_NOT_USED) ++ return 1; ++ if (ioc->scsih_cmds.smid != smid) ++ return 1; ++ ioc->scsih_cmds.status |= MPT3_CMD_COMPLETE; ++ if (mpi_reply) { ++ memcpy(ioc->scsih_cmds.reply, mpi_reply, ++ mpi_reply->MsgLength*4); ++ ioc->scsih_cmds.status |= MPT3_CMD_REPLY_VALID; ++ } ++ ioc->scsih_cmds.status &= ~MPT3_CMD_PENDING; ++ complete(&ioc->scsih_cmds.done); ++ return 1; ++} ++ ++ ++ ++ ++#define MPT3_MAX_LUNS (255) ++ ++ ++/** ++ * _scsih_check_access_status - check access flags ++ * @ioc: per adapter object ++ * @sas_address: sas address ++ * @handle: sas device handle ++ * @access_flags: errors returned during discovery of the device ++ * ++ * Return 0 for success, else failure ++ */ ++static u8 ++_scsih_check_access_status(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, ++ u16 handle, u8 access_status) ++{ ++ u8 rc = 1; ++ char *desc = NULL; ++ ++ switch (access_status) { ++ case MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS: ++ case MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION: ++ rc = 0; ++ break; ++ case MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED: ++ desc = "sata capability failed"; ++ break; ++ case MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT: ++ desc = "sata affiliation conflict"; ++ break; ++ case MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE: ++ desc = "route not addressable"; ++ break; ++ case MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE: ++ desc = "smp error not addressable"; ++ break; ++ case MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED: ++ desc = "device blocked"; ++ break; ++ case MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX: ++ desc = "sata initialization failed"; ++ break; ++ default: ++ desc = "unknown"; ++ break; ++ } ++ ++ if (!rc) ++ return 0; ++ ++ pr_err(MPT3SAS_FMT ++ "discovery errors(%s): sas_address(0x%016llx), handle(0x%04x)\n", ++ ioc->name, desc, (unsigned long long)sas_address, handle); ++ return rc; ++} ++ ++/** ++ * _scsih_check_device - checking device responsiveness ++ * @ioc: per adapter object ++ * @parent_sas_address: sas address of parent expander or sas host ++ * @handle: attached device handle ++ * @phy_numberv: phy number ++ * @link_rate: new link rate ++ * ++ * Returns nothing. ++ */ ++static void ++_scsih_check_device(struct MPT3SAS_ADAPTER *ioc, ++ u64 parent_sas_address, u16 handle, u8 phy_number, u8 link_rate) ++{ ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ struct _sas_device *sas_device; ++ u32 ioc_status; ++ unsigned long flags; ++ u64 sas_address; ++ struct scsi_target *starget; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ u32 device_info; ++ ++ ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, ++ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) ++ return; ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ return; ++ ++ /* wide port handling ~ we need only handle device once for the phy that ++ * is matched in sas device page zero ++ */ ++ if (phy_number != sas_device_pg0.PhyNum) ++ return; ++ ++ /* check if this is end device */ ++ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); ++ if (!(_scsih_is_end_device(device_info))) ++ return; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_address = le64_to_cpu(sas_device_pg0.SASAddress); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ sas_address); ++ ++ if (!sas_device) ++ goto out_unlock; ++ ++ if (unlikely(sas_device->handle != handle)) { ++ starget = sas_device->starget; ++ sas_target_priv_data = starget->hostdata; ++ starget_printk(KERN_INFO, starget, ++ "handle changed from(0x%04x) to (0x%04x)!!!\n", ++ sas_device->handle, handle); ++ sas_target_priv_data->handle = handle; ++ sas_device->handle = handle; ++ if (sas_device_pg0.Flags & ++ MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { ++ sas_device->enclosure_level = ++ le16_to_cpu(sas_device_pg0.EnclosureLevel); ++ memcpy(&sas_device->connector_name[0], ++ &sas_device_pg0.ConnectorName[0], 4); ++ } else { ++ sas_device->enclosure_level = 0; ++ sas_device->connector_name[0] = '\0'; ++ } ++ } ++ ++ /* check if device is present */ ++ if (!(le16_to_cpu(sas_device_pg0.Flags) & ++ MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { ++ pr_err(MPT3SAS_FMT ++ "device is not present handle(0x%04x), flags!!!\n", ++ ioc->name, handle); ++ goto out_unlock; ++ } ++ ++ /* check if there were any issues with discovery */ ++ if (_scsih_check_access_status(ioc, sas_address, handle, ++ sas_device_pg0.AccessStatus)) ++ goto out_unlock; ++ ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ _scsih_ublock_io_device(ioc, sas_address); ++ ++ if (sas_device) ++ sas_device_put(sas_device); ++ return; ++ ++out_unlock: ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ if (sas_device) ++ sas_device_put(sas_device); ++} ++ ++/** ++ * _scsih_add_device - creating sas device object ++ * @ioc: per adapter object ++ * @handle: sas device handle ++ * @phy_num: phy number end device attached to ++ * @is_pd: is this hidden raid component ++ * ++ * Creating end device object, stored in ioc->sas_device_list. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, ++ u8 is_pd) ++{ ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ Mpi2SasEnclosurePage0_t enclosure_pg0; ++ struct _sas_device *sas_device; ++ u32 ioc_status; ++ u64 sas_address; ++ u32 device_info; ++ ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, ++ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ ++ /* check if this is end device */ ++ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); ++ if (!(_scsih_is_end_device(device_info))) ++ return -1; ++ sas_address = le64_to_cpu(sas_device_pg0.SASAddress); ++ ++ /* check if device is present */ ++ if (!(le16_to_cpu(sas_device_pg0.Flags) & ++ MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { ++ pr_err(MPT3SAS_FMT "device is not present handle(0x04%x)!!!\n", ++ ioc->name, handle); ++ return -1; ++ } ++ ++ /* check if there were any issues with discovery */ ++ if (_scsih_check_access_status(ioc, sas_address, handle, ++ sas_device_pg0.AccessStatus)) ++ return -1; ++ ++ sas_device = mpt2sas_get_sdev_by_addr(ioc, ++ sas_address); ++ if (sas_device) { ++ sas_device_put(sas_device); ++ return -1; ++ } ++ ++ sas_device = kzalloc(sizeof(struct _sas_device), ++ GFP_KERNEL); ++ if (!sas_device) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return 0; ++ } ++ ++ kref_init(&sas_device->refcount); ++ sas_device->handle = handle; ++ if (_scsih_get_sas_address(ioc, ++ le16_to_cpu(sas_device_pg0.ParentDevHandle), ++ &sas_device->sas_address_parent) != 0) ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ sas_device->enclosure_handle = ++ le16_to_cpu(sas_device_pg0.EnclosureHandle); ++ if (sas_device->enclosure_handle != 0) ++ sas_device->slot = ++ le16_to_cpu(sas_device_pg0.Slot); ++ sas_device->device_info = device_info; ++ sas_device->sas_address = sas_address; ++ sas_device->phy = sas_device_pg0.PhyNum; ++ sas_device->fast_path = (le16_to_cpu(sas_device_pg0.Flags) & ++ MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE) ? 1 : 0; ++ ++ if (sas_device_pg0.Flags & MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { ++ sas_device->enclosure_level = ++ le16_to_cpu(sas_device_pg0.EnclosureLevel); ++ memcpy(&sas_device->connector_name[0], ++ &sas_device_pg0.ConnectorName[0], 4); ++ } else { ++ sas_device->enclosure_level = 0; ++ sas_device->connector_name[0] = '\0'; ++ } ++ /* get enclosure_logical_id */ ++ if (sas_device->enclosure_handle && !(mpt2sas_config_get_enclosure_pg0( ++ ioc, &mpi_reply, &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, ++ sas_device->enclosure_handle))) ++ sas_device->enclosure_logical_id = ++ le64_to_cpu(enclosure_pg0.EnclosureLogicalID); ++ ++ /* get device name */ ++ sas_device->device_name = le64_to_cpu(sas_device_pg0.DeviceName); ++ ++ if (ioc->wait_for_discovery_to_complete) ++ _scsih_sas_device_init_add(ioc, sas_device); ++ else ++ _scsih_sas_device_add(ioc, sas_device); ++ ++ sas_device_put(sas_device); ++ return 0; ++} ++ ++/** ++ * _scsih_remove_device - removing sas device object ++ * @ioc: per adapter object ++ * @sas_device_delete: the sas_device object ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_remove_device(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_device *sas_device) ++{ ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ ++ if ((ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) && ++ (sas_device->pfa_led_on)) { ++ _scsih_turn_off_pfa_led(ioc, sas_device); ++ sas_device->pfa_led_on = 0; ++ } ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enter: handle(0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, __func__, ++ sas_device->handle, (unsigned long long) ++ sas_device->sas_address)); ++ if (sas_device->enclosure_handle != 0) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enter: enclosure logical id(0x%016llx), slot(%d)\n", ++ ioc->name, __func__, ++ (unsigned long long)sas_device->enclosure_logical_id, ++ sas_device->slot)); ++ if (sas_device->connector_name[0] != '\0') ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enter: enclosure level(0x%04x), connector name( %s)\n", ++ ioc->name, __func__, ++ sas_device->enclosure_level, ++ sas_device->connector_name)); ++ ++ if (sas_device->starget && sas_device->starget->hostdata) { ++ sas_target_priv_data = sas_device->starget->hostdata; ++ sas_target_priv_data->deleted = 1; ++ _scsih_ublock_io_device(ioc, sas_device->sas_address); ++ sas_target_priv_data->handle = ++ MPT3SAS_INVALID_DEVICE_HANDLE; ++ } ++ ++ if (!ioc->hide_drives) ++ mpt2sas_transport_port_remove(ioc, ++ sas_device->sas_address, ++ sas_device->sas_address_parent); ++ ++ pr_info(MPT3SAS_FMT ++ "removing handle(0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, sas_device->handle, ++ (unsigned long long) sas_device->sas_address); ++ if (sas_device->enclosure_handle != 0) ++ pr_info(MPT3SAS_FMT ++ "removing : enclosure logical id(0x%016llx), slot(%d)\n", ++ ioc->name, ++ (unsigned long long)sas_device->enclosure_logical_id, ++ sas_device->slot); ++ if (sas_device->connector_name[0] != '\0') ++ pr_info(MPT3SAS_FMT ++ "removing enclosure level(0x%04x), connector name( %s)\n", ++ ioc->name, sas_device->enclosure_level, ++ sas_device->connector_name); ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: exit: handle(0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, __func__, ++ sas_device->handle, (unsigned long long) ++ sas_device->sas_address)); ++ if (sas_device->enclosure_handle != 0) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: exit: enclosure logical id(0x%016llx), slot(%d)\n", ++ ioc->name, __func__, ++ (unsigned long long)sas_device->enclosure_logical_id, ++ sas_device->slot)); ++ if (sas_device->connector_name[0] != '\0') ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: exit: enclosure level(0x%04x), connector name(%s)\n", ++ ioc->name, __func__, sas_device->enclosure_level, ++ sas_device->connector_name)); ++} ++ ++/** ++ * _scsih_sas_topology_change_event_debug - debug for topology event ++ * @ioc: per adapter object ++ * @event_data: event data payload ++ * Context: user. ++ */ ++static void ++_scsih_sas_topology_change_event_debug(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataSasTopologyChangeList_t *event_data) ++{ ++ int i; ++ u16 handle; ++ u16 reason_code; ++ u8 phy_number; ++ char *status_str = NULL; ++ u8 link_rate, prev_link_rate; ++ ++ switch (event_data->ExpStatus) { ++ case MPI2_EVENT_SAS_TOPO_ES_ADDED: ++ status_str = "add"; ++ break; ++ case MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING: ++ status_str = "remove"; ++ break; ++ case MPI2_EVENT_SAS_TOPO_ES_RESPONDING: ++ case 0: ++ status_str = "responding"; ++ break; ++ case MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING: ++ status_str = "remove delay"; ++ break; ++ default: ++ status_str = "unknown status"; ++ break; ++ } ++ pr_info(MPT3SAS_FMT "sas topology change: (%s)\n", ++ ioc->name, status_str); ++ pr_info("\thandle(0x%04x), enclosure_handle(0x%04x) " \ ++ "start_phy(%02d), count(%d)\n", ++ le16_to_cpu(event_data->ExpanderDevHandle), ++ le16_to_cpu(event_data->EnclosureHandle), ++ event_data->StartPhyNum, event_data->NumEntries); ++ for (i = 0; i < event_data->NumEntries; i++) { ++ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); ++ if (!handle) ++ continue; ++ phy_number = event_data->StartPhyNum + i; ++ reason_code = event_data->PHY[i].PhyStatus & ++ MPI2_EVENT_SAS_TOPO_RC_MASK; ++ switch (reason_code) { ++ case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: ++ status_str = "target add"; ++ break; ++ case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING: ++ status_str = "target remove"; ++ break; ++ case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING: ++ status_str = "delay target remove"; ++ break; ++ case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED: ++ status_str = "link rate change"; ++ break; ++ case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE: ++ status_str = "target responding"; ++ break; ++ default: ++ status_str = "unknown"; ++ break; ++ } ++ link_rate = event_data->PHY[i].LinkRate >> 4; ++ prev_link_rate = event_data->PHY[i].LinkRate & 0xF; ++ pr_info("\tphy(%02d), attached_handle(0x%04x): %s:" \ ++ " link rate: new(0x%02x), old(0x%02x)\n", phy_number, ++ handle, status_str, link_rate, prev_link_rate); ++ ++ } ++} ++ ++/** ++ * _scsih_sas_topology_change_event - handle topology changes ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ */ ++static int ++_scsih_sas_topology_change_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ int i; ++ u16 parent_handle, handle; ++ u16 reason_code; ++ u8 phy_number, max_phys; ++ struct _sas_node *sas_expander; ++ u64 sas_address; ++ unsigned long flags; ++ u8 link_rate, prev_link_rate; ++ Mpi2EventDataSasTopologyChangeList_t *event_data = ++ (Mpi2EventDataSasTopologyChangeList_t *) ++ fw_event->event_data; ++ ++ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) ++ _scsih_sas_topology_change_event_debug(ioc, event_data); ++ ++ if (ioc->shost_recovery || ioc->remove_host || ioc->pci_error_recovery) ++ return 0; ++ ++ if (!ioc->sas_hba.num_phys) ++ _scsih_sas_host_add(ioc); ++ else ++ _scsih_sas_host_refresh(ioc); ++ ++ if (fw_event->ignore) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "ignoring expander event\n", ioc->name)); ++ return 0; ++ } ++ ++ parent_handle = le16_to_cpu(event_data->ExpanderDevHandle); ++ ++ /* handle expander add */ ++ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_ADDED) ++ if (_scsih_expander_add(ioc, parent_handle) != 0) ++ return 0; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, ++ parent_handle); ++ if (sas_expander) { ++ sas_address = sas_expander->sas_address; ++ max_phys = sas_expander->num_phys; ++ } else if (parent_handle < ioc->sas_hba.num_phys) { ++ sas_address = ioc->sas_hba.sas_address; ++ max_phys = ioc->sas_hba.num_phys; ++ } else { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return 0; ++ } ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ /* handle siblings events */ ++ for (i = 0; i < event_data->NumEntries; i++) { ++ if (fw_event->ignore) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "ignoring expander event\n", ioc->name)); ++ return 0; ++ } ++ if (ioc->remove_host || ioc->pci_error_recovery) ++ return 0; ++ phy_number = event_data->StartPhyNum + i; ++ if (phy_number >= max_phys) ++ continue; ++ reason_code = event_data->PHY[i].PhyStatus & ++ MPI2_EVENT_SAS_TOPO_RC_MASK; ++ if ((event_data->PHY[i].PhyStatus & ++ MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) && (reason_code != ++ MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING)) ++ continue; ++ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); ++ if (!handle) ++ continue; ++ link_rate = event_data->PHY[i].LinkRate >> 4; ++ prev_link_rate = event_data->PHY[i].LinkRate & 0xF; ++ switch (reason_code) { ++ case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED: ++ ++ if (ioc->shost_recovery) ++ break; ++ ++ if (link_rate == prev_link_rate) ++ break; ++ ++ mpt2sas_transport_update_links(ioc, sas_address, ++ handle, phy_number, link_rate); ++ ++ if (link_rate < MPI2_SAS_NEG_LINK_RATE_1_5) ++ break; ++ ++ _scsih_check_device(ioc, sas_address, handle, ++ phy_number, link_rate); ++ ++ ++ case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: ++ ++ if (ioc->shost_recovery) ++ break; ++ ++ mpt2sas_transport_update_links(ioc, sas_address, ++ handle, phy_number, link_rate); ++ ++ _scsih_add_device(ioc, handle, phy_number, 0); ++ ++ break; ++ case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING: ++ ++ _scsih_device_remove_by_handle(ioc, handle); ++ break; ++ } ++ } ++ ++ /* handle expander removal */ ++ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING && ++ sas_expander) ++ mpt2sas_expander_remove(ioc, sas_address); ++ ++ return 0; ++} ++ ++/** ++ * _scsih_sas_device_status_change_event_debug - debug for device event ++ * @event_data: event data payload ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_device_status_change_event_debug(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataSasDeviceStatusChange_t *event_data) ++{ ++ char *reason_str = NULL; ++ ++ switch (event_data->ReasonCode) { ++ case MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA: ++ reason_str = "smart data"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED: ++ reason_str = "unsupported device discovered"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET: ++ reason_str = "internal device reset"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL: ++ reason_str = "internal task abort"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL: ++ reason_str = "internal task abort set"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL: ++ reason_str = "internal clear task set"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL: ++ reason_str = "internal query task"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE: ++ reason_str = "sata init failure"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET: ++ reason_str = "internal device reset complete"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL: ++ reason_str = "internal task abort complete"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION: ++ reason_str = "internal async notification"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY: ++ reason_str = "expander reduced functionality"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY: ++ reason_str = "expander reduced functionality complete"; ++ break; ++ default: ++ reason_str = "unknown reason"; ++ break; ++ } ++ pr_info(MPT3SAS_FMT "device status change: (%s)\n" ++ "\thandle(0x%04x), sas address(0x%016llx), tag(%d)", ++ ioc->name, reason_str, le16_to_cpu(event_data->DevHandle), ++ (unsigned long long)le64_to_cpu(event_data->SASAddress), ++ le16_to_cpu(event_data->TaskTag)); ++ if (event_data->ReasonCode == MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA) ++ pr_info(MPT3SAS_FMT ", ASC(0x%x), ASCQ(0x%x)\n", ioc->name, ++ event_data->ASC, event_data->ASCQ); ++ pr_info("\n"); ++} ++ ++/** ++ * _scsih_sas_device_status_change_event - handle device status change ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_device_status_change_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ struct MPT3SAS_TARGET *target_priv_data; ++ struct _sas_device *sas_device; ++ u64 sas_address; ++ unsigned long flags; ++ Mpi2EventDataSasDeviceStatusChange_t *event_data = ++ (Mpi2EventDataSasDeviceStatusChange_t *) ++ fw_event->event_data; ++ ++ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) ++ _scsih_sas_device_status_change_event_debug(ioc, ++ event_data); ++ ++ /* In MPI Revision K (0xC), the internal device reset complete was ++ * implemented, so avoid setting tm_busy flag for older firmware. ++ */ ++ if ((ioc->facts.HeaderVersion >> 8) < 0xC) ++ return; ++ ++ if (event_data->ReasonCode != ++ MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET && ++ event_data->ReasonCode != ++ MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET) ++ return; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_address = le64_to_cpu(event_data->SASAddress); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ sas_address); ++ ++ if (!sas_device || !sas_device->starget) ++ goto out; ++ ++ target_priv_data = sas_device->starget->hostdata; ++ if (!target_priv_data) ++ goto out; ++ ++ if (event_data->ReasonCode == ++ MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET) ++ target_priv_data->tm_busy = 1; ++ else ++ target_priv_data->tm_busy = 0; ++ ++out: ++ if (sas_device) ++ sas_device_put(sas_device); ++ ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++} ++ ++/** ++ * _scsih_sas_enclosure_dev_status_change_event_debug - debug for enclosure ++ * event ++ * @ioc: per adapter object ++ * @event_data: event data payload ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_enclosure_dev_status_change_event_debug(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataSasEnclDevStatusChange_t *event_data) ++{ ++ char *reason_str = NULL; ++ ++ switch (event_data->ReasonCode) { ++ case MPI2_EVENT_SAS_ENCL_RC_ADDED: ++ reason_str = "enclosure add"; ++ break; ++ case MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING: ++ reason_str = "enclosure remove"; ++ break; ++ default: ++ reason_str = "unknown reason"; ++ break; ++ } ++ ++ pr_info(MPT3SAS_FMT "enclosure status change: (%s)\n" ++ "\thandle(0x%04x), enclosure logical id(0x%016llx)" ++ " number slots(%d)\n", ioc->name, reason_str, ++ le16_to_cpu(event_data->EnclosureHandle), ++ (unsigned long long)le64_to_cpu(event_data->EnclosureLogicalID), ++ le16_to_cpu(event_data->StartSlot)); ++} ++ ++/** ++ * _scsih_sas_enclosure_dev_status_change_event - handle enclosure events ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_enclosure_dev_status_change_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) ++ _scsih_sas_enclosure_dev_status_change_event_debug(ioc, ++ (Mpi2EventDataSasEnclDevStatusChange_t *) ++ fw_event->event_data); ++} ++ ++/** ++ * _scsih_sas_broadcast_primitive_event - handle broadcast events ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_broadcast_primitive_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ struct scsi_cmnd *scmd; ++ struct scsi_device *sdev; ++ u16 smid, handle; ++ u32 lun; ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ u32 termination_count; ++ u32 query_count; ++ Mpi2SCSITaskManagementReply_t *mpi_reply; ++ Mpi2EventDataSasBroadcastPrimitive_t *event_data = ++ (Mpi2EventDataSasBroadcastPrimitive_t *) ++ fw_event->event_data; ++ u16 ioc_status; ++ unsigned long flags; ++ int r; ++ u8 max_retries = 0; ++ u8 task_abort_retries; ++ ++ mutex_lock(&ioc->tm_cmds.mutex); ++ pr_info(MPT3SAS_FMT ++ "%s: enter: phy number(%d), width(%d)\n", ++ ioc->name, __func__, event_data->PhyNum, ++ event_data->PortWidth); ++ ++ _scsih_block_io_all_device(ioc); ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ mpi_reply = ioc->tm_cmds.reply; ++ broadcast_aen_retry: ++ ++ /* sanity checks for retrying this loop */ ++ if (max_retries++ == 5) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT "%s: giving up\n", ++ ioc->name, __func__)); ++ goto out; ++ } else if (max_retries > 1) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT "%s: %d retry\n", ++ ioc->name, __func__, max_retries - 1)); ++ ++ termination_count = 0; ++ query_count = 0; ++ for (smid = 1; smid <= ioc->scsiio_depth; smid++) { ++ if (ioc->shost_recovery) ++ goto out; ++ scmd = _scsih_scsi_lookup_get(ioc, smid); ++ if (!scmd) ++ continue; ++ sdev = scmd->device; ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) ++ continue; ++ /* skip hidden raid components */ ++ if (sas_device_priv_data->sas_target->flags & ++ MPT_TARGET_FLAGS_RAID_COMPONENT) ++ continue; ++ /* skip volumes */ ++ if (sas_device_priv_data->sas_target->flags & ++ MPT_TARGET_FLAGS_VOLUME) ++ continue; ++ ++ handle = sas_device_priv_data->sas_target->handle; ++ lun = sas_device_priv_data->lun; ++ query_count++; ++ ++ if (ioc->shost_recovery) ++ goto out; ++ ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ r = mpt2sas_scsih_issue_tm(ioc, handle, 0, 0, lun, ++ MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30, ++ TM_MUTEX_OFF); ++ if (r == FAILED) { ++ sdev_printk(KERN_WARNING, sdev, ++ "mpt2sas_scsih_issue_tm: FAILED when sending " ++ "QUERY_TASK: scmd(%p)\n", scmd); ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ goto broadcast_aen_retry; ++ } ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) ++ & MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ sdev_printk(KERN_WARNING, sdev, ++ "query task: FAILED with IOCSTATUS(0x%04x), scmd(%p)\n", ++ ioc_status, scmd); ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ goto broadcast_aen_retry; ++ } ++ ++ /* see if IO is still owned by IOC and target */ ++ if (mpi_reply->ResponseCode == ++ MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED || ++ mpi_reply->ResponseCode == ++ MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC) { ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ continue; ++ } ++ task_abort_retries = 0; ++ tm_retry: ++ if (task_abort_retries++ == 60) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: ABORT_TASK: giving up\n", ioc->name, ++ __func__)); ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ goto broadcast_aen_retry; ++ } ++ ++ if (ioc->shost_recovery) ++ goto out_no_lock; ++ ++ r = mpt2sas_scsih_issue_tm(ioc, handle, sdev->channel, sdev->id, ++ sdev->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, ++ TM_MUTEX_OFF); ++ if (r == FAILED) { ++ sdev_printk(KERN_WARNING, sdev, ++ "mpt2sas_scsih_issue_tm: ABORT_TASK: FAILED : " ++ "scmd(%p)\n", scmd); ++ goto tm_retry; ++ } ++ ++ if (task_abort_retries > 1) ++ sdev_printk(KERN_WARNING, sdev, ++ "mpt2sas_scsih_issue_tm: ABORT_TASK: RETRIES (%d):" ++ " scmd(%p)\n", ++ task_abort_retries - 1, scmd); ++ ++ termination_count += le32_to_cpu(mpi_reply->TerminationCount); ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ } ++ ++ if (ioc->broadcast_aen_pending) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: loop back due to pending AEN\n", ++ ioc->name, __func__)); ++ ioc->broadcast_aen_pending = 0; ++ goto broadcast_aen_retry; ++ } ++ ++ out: ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ out_no_lock: ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s - exit, query_count = %d termination_count = %d\n", ++ ioc->name, __func__, query_count, termination_count)); ++ ++ ioc->broadcast_aen_busy = 0; ++ if (!ioc->shost_recovery) ++ _scsih_ublock_io_all_device(ioc); ++ mutex_unlock(&ioc->tm_cmds.mutex); ++} ++ ++/** ++ * _scsih_sas_discovery_event - handle discovery events ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_discovery_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ Mpi2EventDataSasDiscovery_t *event_data = ++ (Mpi2EventDataSasDiscovery_t *) fw_event->event_data; ++ ++ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) { ++ pr_info(MPT3SAS_FMT "discovery event: (%s)", ioc->name, ++ (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ? ++ "start" : "stop"); ++ if (event_data->DiscoveryStatus) ++ pr_info("discovery_status(0x%08x)", ++ le32_to_cpu(event_data->DiscoveryStatus)); ++ pr_info("\n"); ++ } ++ ++ if (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED && ++ !ioc->sas_hba.num_phys) { ++ if (disable_discovery > 0 && ioc->shost_recovery) { ++ /* Wait for the reset to complete */ ++ while (ioc->shost_recovery) ++ ssleep(1); ++ } ++ _scsih_sas_host_add(ioc); ++ } ++} ++ ++/** ++ * _scsih_ir_fastpath - turn on fastpath for IR physdisk ++ * @ioc: per adapter object ++ * @handle: device handle for physical disk ++ * @phys_disk_num: physical disk number ++ * ++ * Return 0 for success, else failure. ++ */ ++static int ++_scsih_ir_fastpath(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phys_disk_num) ++{ ++ Mpi2RaidActionRequest_t *mpi_request; ++ Mpi2RaidActionReply_t *mpi_reply; ++ u16 smid; ++ u8 issue_reset = 0; ++ int rc = 0; ++ u16 ioc_status; ++ u32 log_info; ++ ++ if (ioc->hba_mpi_version_belonged == MPI2_VERSION) ++ return rc; ++ ++ mutex_lock(&ioc->scsih_cmds.mutex); ++ ++ if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: scsih_cmd in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ioc->scsih_cmds.status = MPT3_CMD_PENDING; ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->scsih_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->scsih_cmds.smid = smid; ++ memset(mpi_request, 0, sizeof(Mpi2RaidActionRequest_t)); ++ ++ mpi_request->Function = MPI2_FUNCTION_RAID_ACTION; ++ mpi_request->Action = MPI2_RAID_ACTION_PHYSDISK_HIDDEN; ++ mpi_request->PhysDiskNum = phys_disk_num; ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT "IR RAID_ACTION: turning fast "\ ++ "path on for handle(0x%04x), phys_disk_num (0x%02x)\n", ioc->name, ++ handle, phys_disk_num)); ++ ++ init_completion(&ioc->scsih_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ); ++ ++ if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ if (!(ioc->scsih_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ rc = -EFAULT; ++ goto out; ++ } ++ ++ if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) { ++ ++ mpi_reply = ioc->scsih_cmds.reply; ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus); ++ if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) ++ log_info = le32_to_cpu(mpi_reply->IOCLogInfo); ++ else ++ log_info = 0; ++ ioc_status &= MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "IR RAID_ACTION: failed: ioc_status(0x%04x), " ++ "loginfo(0x%08x)!!!\n", ioc->name, ioc_status, ++ log_info)); ++ rc = -EFAULT; ++ } else ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "IR RAID_ACTION: completed successfully\n", ++ ioc->name)); ++ } ++ ++ out: ++ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_unlock(&ioc->scsih_cmds.mutex); ++ ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ return rc; ++} ++ ++/** ++ * _scsih_reprobe_lun - reprobing lun ++ * @sdev: scsi device struct ++ * @no_uld_attach: sdev->no_uld_attach flag setting ++ * ++ **/ ++static void ++_scsih_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach) ++{ ++ int rc; ++ sdev->no_uld_attach = no_uld_attach ? 1 : 0; ++ sdev_printk(KERN_INFO, sdev, "%s raid component\n", ++ sdev->no_uld_attach ? "hidding" : "exposing"); ++ rc = scsi_device_reprobe(sdev); ++} ++ ++/** ++ * _scsih_sas_volume_add - add new volume ++ * @ioc: per adapter object ++ * @element: IR config element data ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_volume_add(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventIrConfigElement_t *element) ++{ ++ struct _raid_device *raid_device; ++ unsigned long flags; ++ u64 wwid; ++ u16 handle = le16_to_cpu(element->VolDevHandle); ++ int rc; ++ ++ mpt2sas_config_get_volume_wwid(ioc, handle, &wwid); ++ if (!wwid) { ++ pr_err(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = _scsih_raid_device_find_by_wwid(ioc, wwid); ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ ++ if (raid_device) ++ return; ++ ++ raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL); ++ if (!raid_device) { ++ pr_err(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ raid_device->id = ioc->sas_id++; ++ raid_device->channel = RAID_CHANNEL; ++ raid_device->handle = handle; ++ raid_device->wwid = wwid; ++ _scsih_raid_device_add(ioc, raid_device); ++ if (!ioc->wait_for_discovery_to_complete) { ++ rc = scsi_add_device(ioc->shost, RAID_CHANNEL, ++ raid_device->id, 0); ++ if (rc) ++ _scsih_raid_device_remove(ioc, raid_device); ++ } else { ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ _scsih_determine_boot_device(ioc, raid_device, 1); ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ } ++} ++ ++/** ++ * _scsih_sas_volume_delete - delete volume ++ * @ioc: per adapter object ++ * @handle: volume device handle ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_volume_delete(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _raid_device *raid_device; ++ unsigned long flags; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct scsi_target *starget = NULL; ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = mpt2sas_raid_device_find_by_handle(ioc, handle); ++ if (raid_device) { ++ if (raid_device->starget) { ++ starget = raid_device->starget; ++ sas_target_priv_data = starget->hostdata; ++ sas_target_priv_data->deleted = 1; ++ } ++ pr_info(MPT3SAS_FMT "removing handle(0x%04x), wwid(0x%016llx)\n", ++ ioc->name, raid_device->handle, ++ (unsigned long long) raid_device->wwid); ++ list_del(&raid_device->list); ++ kfree(raid_device); ++ } ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ if (starget) ++ scsi_remove_target(&starget->dev); ++} ++ ++/** ++ * _scsih_sas_pd_expose - expose pd component to /dev/sdX ++ * @ioc: per adapter object ++ * @element: IR config element data ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_pd_expose(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventIrConfigElement_t *element) ++{ ++ struct _sas_device *sas_device; ++ struct scsi_target *starget = NULL; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ unsigned long flags; ++ u16 handle = le16_to_cpu(element->PhysDiskDevHandle); ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (sas_device) { ++ sas_device->volume_handle = 0; ++ sas_device->volume_wwid = 0; ++ clear_bit(handle, ioc->pd_handles); ++ if (sas_device->starget && sas_device->starget->hostdata) { ++ starget = sas_device->starget; ++ sas_target_priv_data = starget->hostdata; ++ sas_target_priv_data->flags &= ++ ~MPT_TARGET_FLAGS_RAID_COMPONENT; ++ } ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ if (!sas_device) ++ return; ++ ++ /* exposing raid component */ ++ if (starget) ++ starget_for_each_device(starget, NULL, _scsih_reprobe_lun); ++ ++ sas_device_put(sas_device); ++} ++ ++/** ++ * _scsih_sas_pd_hide - hide pd component from /dev/sdX ++ * @ioc: per adapter object ++ * @element: IR config element data ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_pd_hide(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventIrConfigElement_t *element) ++{ ++ struct _sas_device *sas_device; ++ struct scsi_target *starget = NULL; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ unsigned long flags; ++ u16 handle = le16_to_cpu(element->PhysDiskDevHandle); ++ u16 volume_handle = 0; ++ u64 volume_wwid = 0; ++ ++ mpt2sas_config_get_volume_handle(ioc, handle, &volume_handle); ++ if (volume_handle) ++ mpt2sas_config_get_volume_wwid(ioc, volume_handle, ++ &volume_wwid); ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (sas_device) { ++ set_bit(handle, ioc->pd_handles); ++ if (sas_device->starget && sas_device->starget->hostdata) { ++ starget = sas_device->starget; ++ sas_target_priv_data = starget->hostdata; ++ sas_target_priv_data->flags |= ++ MPT_TARGET_FLAGS_RAID_COMPONENT; ++ sas_device->volume_handle = volume_handle; ++ sas_device->volume_wwid = volume_wwid; ++ } ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ if (!sas_device) ++ return; ++ ++ /* hiding raid component */ ++ _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum); ++ ++ if (starget) ++ starget_for_each_device(starget, (void *)1, _scsih_reprobe_lun); ++ ++ sas_device_put(sas_device); ++} ++ ++/** ++ * _scsih_sas_pd_delete - delete pd component ++ * @ioc: per adapter object ++ * @element: IR config element data ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_pd_delete(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventIrConfigElement_t *element) ++{ ++ u16 handle = le16_to_cpu(element->PhysDiskDevHandle); ++ ++ _scsih_device_remove_by_handle(ioc, handle); ++} ++ ++/** ++ * _scsih_sas_pd_add - remove pd component ++ * @ioc: per adapter object ++ * @element: IR config element data ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_pd_add(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventIrConfigElement_t *element) ++{ ++ struct _sas_device *sas_device; ++ u16 handle = le16_to_cpu(element->PhysDiskDevHandle); ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ u32 ioc_status; ++ u64 sas_address; ++ u16 parent_handle; ++ ++ set_bit(handle, ioc->pd_handles); ++ ++ sas_device = mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (sas_device) { ++ _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum); ++ sas_device_put(sas_device); ++ return; ++ } ++ ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, ++ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); ++ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) ++ mpt2sas_transport_update_links(ioc, sas_address, handle, ++ sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); ++ ++ _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum); ++ _scsih_add_device(ioc, handle, 0, 1); ++} ++ ++/** ++ * _scsih_sas_ir_config_change_event_debug - debug for IR Config Change events ++ * @ioc: per adapter object ++ * @event_data: event data payload ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_ir_config_change_event_debug(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataIrConfigChangeList_t *event_data) ++{ ++ Mpi2EventIrConfigElement_t *element; ++ u8 element_type; ++ int i; ++ char *reason_str = NULL, *element_str = NULL; ++ ++ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; ++ ++ pr_info(MPT3SAS_FMT "raid config change: (%s), elements(%d)\n", ++ ioc->name, (le32_to_cpu(event_data->Flags) & ++ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? ++ "foreign" : "native", event_data->NumElements); ++ for (i = 0; i < event_data->NumElements; i++, element++) { ++ switch (element->ReasonCode) { ++ case MPI2_EVENT_IR_CHANGE_RC_ADDED: ++ reason_str = "add"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_REMOVED: ++ reason_str = "remove"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE: ++ reason_str = "no change"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_HIDE: ++ reason_str = "hide"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_UNHIDE: ++ reason_str = "unhide"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED: ++ reason_str = "volume_created"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED: ++ reason_str = "volume_deleted"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED: ++ reason_str = "pd_created"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED: ++ reason_str = "pd_deleted"; ++ break; ++ default: ++ reason_str = "unknown reason"; ++ break; ++ } ++ element_type = le16_to_cpu(element->ElementFlags) & ++ MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK; ++ switch (element_type) { ++ case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT: ++ element_str = "volume"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT: ++ element_str = "phys disk"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT: ++ element_str = "hot spare"; ++ break; ++ default: ++ element_str = "unknown element"; ++ break; ++ } ++ pr_info("\t(%s:%s), vol handle(0x%04x), " \ ++ "pd handle(0x%04x), pd num(0x%02x)\n", element_str, ++ reason_str, le16_to_cpu(element->VolDevHandle), ++ le16_to_cpu(element->PhysDiskDevHandle), ++ element->PhysDiskNum); ++ } ++} ++ ++/** ++ * _scsih_sas_ir_config_change_event - handle ir configuration change events ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_ir_config_change_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ Mpi2EventIrConfigElement_t *element; ++ int i; ++ u8 foreign_config; ++ Mpi2EventDataIrConfigChangeList_t *event_data = ++ (Mpi2EventDataIrConfigChangeList_t *) ++ fw_event->event_data; ++ ++ if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) && ++ (!ioc->hide_ir_msg)) ++ _scsih_sas_ir_config_change_event_debug(ioc, event_data); ++ ++ foreign_config = (le32_to_cpu(event_data->Flags) & ++ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? 1 : 0; ++ ++ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; ++ if (ioc->shost_recovery && ++ ioc->hba_mpi_version_belonged != MPI2_VERSION) { ++ for (i = 0; i < event_data->NumElements; i++, element++) { ++ if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_HIDE) ++ _scsih_ir_fastpath(ioc, ++ le16_to_cpu(element->PhysDiskDevHandle), ++ element->PhysDiskNum); ++ } ++ return; ++ } ++ ++ for (i = 0; i < event_data->NumElements; i++, element++) { ++ ++ switch (element->ReasonCode) { ++ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED: ++ case MPI2_EVENT_IR_CHANGE_RC_ADDED: ++ if (!foreign_config) ++ _scsih_sas_volume_add(ioc, element); ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED: ++ case MPI2_EVENT_IR_CHANGE_RC_REMOVED: ++ if (!foreign_config) ++ _scsih_sas_volume_delete(ioc, ++ le16_to_cpu(element->VolDevHandle)); ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED: ++ if (!ioc->is_warpdrive) ++ _scsih_sas_pd_hide(ioc, element); ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED: ++ if (!ioc->is_warpdrive) ++ _scsih_sas_pd_expose(ioc, element); ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_HIDE: ++ if (!ioc->is_warpdrive) ++ _scsih_sas_pd_add(ioc, element); ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_UNHIDE: ++ if (!ioc->is_warpdrive) ++ _scsih_sas_pd_delete(ioc, element); ++ break; ++ } ++ } ++} ++ ++/** ++ * _scsih_sas_ir_volume_event - IR volume event ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_ir_volume_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ u64 wwid; ++ unsigned long flags; ++ struct _raid_device *raid_device; ++ u16 handle; ++ u32 state; ++ int rc; ++ Mpi2EventDataIrVolume_t *event_data = ++ (Mpi2EventDataIrVolume_t *) fw_event->event_data; ++ ++ if (ioc->shost_recovery) ++ return; ++ ++ if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED) ++ return; ++ ++ handle = le16_to_cpu(event_data->VolDevHandle); ++ state = le32_to_cpu(event_data->NewValue); ++ if (!ioc->hide_ir_msg) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: handle(0x%04x), old(0x%08x), new(0x%08x)\n", ++ ioc->name, __func__, handle, ++ le32_to_cpu(event_data->PreviousValue), state)); ++ switch (state) { ++ case MPI2_RAID_VOL_STATE_MISSING: ++ case MPI2_RAID_VOL_STATE_FAILED: ++ _scsih_sas_volume_delete(ioc, handle); ++ break; ++ ++ case MPI2_RAID_VOL_STATE_ONLINE: ++ case MPI2_RAID_VOL_STATE_DEGRADED: ++ case MPI2_RAID_VOL_STATE_OPTIMAL: ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = mpt2sas_raid_device_find_by_handle(ioc, handle); ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ ++ if (raid_device) ++ break; ++ ++ mpt2sas_config_get_volume_wwid(ioc, handle, &wwid); ++ if (!wwid) { ++ pr_err(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__); ++ break; ++ } ++ ++ raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL); ++ if (!raid_device) { ++ pr_err(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__); ++ break; ++ } ++ ++ raid_device->id = ioc->sas_id++; ++ raid_device->channel = RAID_CHANNEL; ++ raid_device->handle = handle; ++ raid_device->wwid = wwid; ++ _scsih_raid_device_add(ioc, raid_device); ++ rc = scsi_add_device(ioc->shost, RAID_CHANNEL, ++ raid_device->id, 0); ++ if (rc) ++ _scsih_raid_device_remove(ioc, raid_device); ++ break; ++ ++ case MPI2_RAID_VOL_STATE_INITIALIZING: ++ default: ++ break; ++ } ++} ++ ++/** ++ * _scsih_sas_ir_physical_disk_event - PD event ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_ir_physical_disk_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ u16 handle, parent_handle; ++ u32 state; ++ struct _sas_device *sas_device; ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ u32 ioc_status; ++ Mpi2EventDataIrPhysicalDisk_t *event_data = ++ (Mpi2EventDataIrPhysicalDisk_t *) fw_event->event_data; ++ u64 sas_address; ++ ++ if (ioc->shost_recovery) ++ return; ++ ++ if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED) ++ return; ++ ++ handle = le16_to_cpu(event_data->PhysDiskDevHandle); ++ state = le32_to_cpu(event_data->NewValue); ++ ++ if (!ioc->hide_ir_msg) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: handle(0x%04x), old(0x%08x), new(0x%08x)\n", ++ ioc->name, __func__, handle, ++ le32_to_cpu(event_data->PreviousValue), state)); ++ ++ switch (state) { ++ case MPI2_RAID_PD_STATE_ONLINE: ++ case MPI2_RAID_PD_STATE_DEGRADED: ++ case MPI2_RAID_PD_STATE_REBUILDING: ++ case MPI2_RAID_PD_STATE_OPTIMAL: ++ case MPI2_RAID_PD_STATE_HOT_SPARE: ++ ++ if (!ioc->is_warpdrive) ++ set_bit(handle, ioc->pd_handles); ++ ++ sas_device = mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (sas_device) { ++ sas_device_put(sas_device); ++ return; ++ } ++ ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, ++ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ++ handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); ++ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) ++ mpt2sas_transport_update_links(ioc, sas_address, handle, ++ sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); ++ ++ _scsih_add_device(ioc, handle, 0, 1); ++ ++ break; ++ ++ case MPI2_RAID_PD_STATE_OFFLINE: ++ case MPI2_RAID_PD_STATE_NOT_CONFIGURED: ++ case MPI2_RAID_PD_STATE_NOT_COMPATIBLE: ++ default: ++ break; ++ } ++} ++ ++/** ++ * _scsih_sas_ir_operation_status_event_debug - debug for IR op event ++ * @ioc: per adapter object ++ * @event_data: event data payload ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_ir_operation_status_event_debug(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataIrOperationStatus_t *event_data) ++{ ++ char *reason_str = NULL; ++ ++ switch (event_data->RAIDOperation) { ++ case MPI2_EVENT_IR_RAIDOP_RESYNC: ++ reason_str = "resync"; ++ break; ++ case MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION: ++ reason_str = "online capacity expansion"; ++ break; ++ case MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK: ++ reason_str = "consistency check"; ++ break; ++ case MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT: ++ reason_str = "background init"; ++ break; ++ case MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT: ++ reason_str = "make data consistent"; ++ break; ++ } ++ ++ if (!reason_str) ++ return; ++ ++ pr_info(MPT3SAS_FMT "raid operational status: (%s)" \ ++ "\thandle(0x%04x), percent complete(%d)\n", ++ ioc->name, reason_str, ++ le16_to_cpu(event_data->VolDevHandle), ++ event_data->PercentComplete); ++} ++ ++/** ++ * _scsih_sas_ir_operation_status_event - handle RAID operation events ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_ir_operation_status_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ Mpi2EventDataIrOperationStatus_t *event_data = ++ (Mpi2EventDataIrOperationStatus_t *) ++ fw_event->event_data; ++ static struct _raid_device *raid_device; ++ unsigned long flags; ++ u16 handle; ++ ++ if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) && ++ (!ioc->hide_ir_msg)) ++ _scsih_sas_ir_operation_status_event_debug(ioc, ++ event_data); ++ ++ /* code added for raid transport support */ ++ if (event_data->RAIDOperation == MPI2_EVENT_IR_RAIDOP_RESYNC) { ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ handle = le16_to_cpu(event_data->VolDevHandle); ++ raid_device = mpt2sas_raid_device_find_by_handle(ioc, handle); ++ if (raid_device) ++ raid_device->percent_complete = ++ event_data->PercentComplete; ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ } ++} ++ ++/** ++ * _scsih_prep_device_scan - initialize parameters prior to device scan ++ * @ioc: per adapter object ++ * ++ * Set the deleted flag prior to device scan. If the device is found during ++ * the scan, then we clear the deleted flag. ++ */ ++static void ++_scsih_prep_device_scan(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_device *sdev; ++ ++ shost_for_each_device(sdev, ioc->shost) { ++ sas_device_priv_data = sdev->hostdata; ++ if (sas_device_priv_data && sas_device_priv_data->sas_target) ++ sas_device_priv_data->sas_target->deleted = 1; ++ } ++} ++ ++/** ++ * _scsih_mark_responding_sas_device - mark a sas_devices as responding ++ * @ioc: per adapter object ++ * @sas_device_pg0: SAS Device page 0 ++ * ++ * After host reset, find out whether devices are still responding. ++ * Used in _scsih_remove_unresponsive_sas_devices. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, ++Mpi2SasDevicePage0_t *sas_device_pg0) ++{ ++ struct MPT3SAS_TARGET *sas_target_priv_data = NULL; ++ struct scsi_target *starget; ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ list_for_each_entry(sas_device, &ioc->sas_device_list, list) { ++ if ((sas_device->sas_address == sas_device_pg0->SASAddress) && ++ (sas_device->slot == sas_device_pg0->Slot)) { ++ sas_device->responding = 1; ++ starget = sas_device->starget; ++ if (starget && starget->hostdata) { ++ sas_target_priv_data = starget->hostdata; ++ sas_target_priv_data->tm_busy = 0; ++ sas_target_priv_data->deleted = 0; ++ } else ++ sas_target_priv_data = NULL; ++ if (starget) { ++ starget_printk(KERN_INFO, starget, ++ "handle(0x%04x), sas_addr(0x%016llx)\n", ++ sas_device_pg0->DevHandle, ++ (unsigned long long) ++ sas_device->sas_address); ++ ++ if (sas_device->enclosure_handle != 0) ++ starget_printk(KERN_INFO, starget, ++ "enclosure logical id(0x%016llx)," ++ " slot(%d)\n", ++ (unsigned long long) ++ sas_device->enclosure_logical_id, ++ sas_device->slot); ++ } ++ if (sas_device_pg0->Flags & ++ MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { ++ sas_device->enclosure_level = ++ le16_to_cpu(sas_device_pg0->EnclosureLevel); ++ memcpy(&sas_device->connector_name[0], ++ &sas_device_pg0->ConnectorName[0], 4); ++ } else { ++ sas_device->enclosure_level = 0; ++ sas_device->connector_name[0] = '\0'; ++ } ++ ++ if (sas_device->handle == sas_device_pg0->DevHandle) ++ goto out; ++ pr_info("\thandle changed from(0x%04x)!!!\n", ++ sas_device->handle); ++ sas_device->handle = sas_device_pg0->DevHandle; ++ if (sas_target_priv_data) ++ sas_target_priv_data->handle = ++ sas_device_pg0->DevHandle; ++ goto out; ++ } ++ } ++ out: ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++} ++ ++/** ++ * _scsih_search_responding_sas_devices - ++ * @ioc: per adapter object ++ * ++ * After host reset, find out whether devices are still responding. ++ * If not remove. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_search_responding_sas_devices(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 ioc_status; ++ u16 handle; ++ u32 device_info; ++ ++ pr_info(MPT3SAS_FMT "search for end-devices: start\n", ioc->name); ++ ++ if (list_empty(&ioc->sas_device_list)) ++ goto out; ++ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, ++ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, ++ handle))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ break; ++ handle = sas_device_pg0.DevHandle = ++ le16_to_cpu(sas_device_pg0.DevHandle); ++ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); ++ if (!(_scsih_is_end_device(device_info))) ++ continue; ++ sas_device_pg0.SASAddress = ++ le64_to_cpu(sas_device_pg0.SASAddress); ++ sas_device_pg0.Slot = le16_to_cpu(sas_device_pg0.Slot); ++ _scsih_mark_responding_sas_device(ioc, &sas_device_pg0); ++ } ++ ++ out: ++ pr_info(MPT3SAS_FMT "search for end-devices: complete\n", ++ ioc->name); ++} ++ ++/** ++ * _scsih_mark_responding_raid_device - mark a raid_device as responding ++ * @ioc: per adapter object ++ * @wwid: world wide identifier for raid volume ++ * @handle: device handle ++ * ++ * After host reset, find out whether devices are still responding. ++ * Used in _scsih_remove_unresponsive_raid_devices. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_mark_responding_raid_device(struct MPT3SAS_ADAPTER *ioc, u64 wwid, ++ u16 handle) ++{ ++ struct MPT3SAS_TARGET *sas_target_priv_data = NULL; ++ struct scsi_target *starget; ++ struct _raid_device *raid_device; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ list_for_each_entry(raid_device, &ioc->raid_device_list, list) { ++ if (raid_device->wwid == wwid && raid_device->starget) { ++ starget = raid_device->starget; ++ if (starget && starget->hostdata) { ++ sas_target_priv_data = starget->hostdata; ++ sas_target_priv_data->deleted = 0; ++ } else ++ sas_target_priv_data = NULL; ++ raid_device->responding = 1; ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ starget_printk(KERN_INFO, raid_device->starget, ++ "handle(0x%04x), wwid(0x%016llx)\n", handle, ++ (unsigned long long)raid_device->wwid); ++ ++ /* ++ * WARPDRIVE: The handles of the PDs might have changed ++ * across the host reset so re-initialize the ++ * required data for Direct IO ++ */ ++ mpt2sas_init_warpdrive_properties(ioc, raid_device); ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ if (raid_device->handle == handle) { ++ spin_unlock_irqrestore(&ioc->raid_device_lock, ++ flags); ++ return; ++ } ++ pr_info("\thandle changed from(0x%04x)!!!\n", ++ raid_device->handle); ++ raid_device->handle = handle; ++ if (sas_target_priv_data) ++ sas_target_priv_data->handle = handle; ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ return; ++ } ++ } ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++} ++ ++/** ++ * _scsih_search_responding_raid_devices - ++ * @ioc: per adapter object ++ * ++ * After host reset, find out whether devices are still responding. ++ * If not remove. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_search_responding_raid_devices(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2RaidVolPage1_t volume_pg1; ++ Mpi2RaidVolPage0_t volume_pg0; ++ Mpi2RaidPhysDiskPage0_t pd_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 ioc_status; ++ u16 handle; ++ u8 phys_disk_num; ++ ++ if (!ioc->ir_firmware) ++ return; ++ ++ pr_info(MPT3SAS_FMT "search for raid volumes: start\n", ++ ioc->name); ++ ++ if (list_empty(&ioc->raid_device_list)) ++ goto out; ++ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, ++ &volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ break; ++ handle = le16_to_cpu(volume_pg1.DevHandle); ++ ++ if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, ++ &volume_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, ++ sizeof(Mpi2RaidVolPage0_t))) ++ continue; ++ ++ if (volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_OPTIMAL || ++ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_ONLINE || ++ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_DEGRADED) ++ _scsih_mark_responding_raid_device(ioc, ++ le64_to_cpu(volume_pg1.WWID), handle); ++ } ++ ++ /* refresh the pd_handles */ ++ if (!ioc->is_warpdrive) { ++ phys_disk_num = 0xFF; ++ memset(ioc->pd_handles, 0, ioc->pd_handles_sz); ++ while (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, ++ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM, ++ phys_disk_num))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ break; ++ phys_disk_num = pd_pg0.PhysDiskNum; ++ handle = le16_to_cpu(pd_pg0.DevHandle); ++ set_bit(handle, ioc->pd_handles); ++ } ++ } ++ out: ++ pr_info(MPT3SAS_FMT "search for responding raid volumes: complete\n", ++ ioc->name); ++} ++ ++/** ++ * _scsih_mark_responding_expander - mark a expander as responding ++ * @ioc: per adapter object ++ * @sas_address: sas address ++ * @handle: ++ * ++ * After host reset, find out whether devices are still responding. ++ * Used in _scsih_remove_unresponsive_expanders. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_mark_responding_expander(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, ++ u16 handle) ++{ ++ struct _sas_node *sas_expander; ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { ++ if (sas_expander->sas_address != sas_address) ++ continue; ++ sas_expander->responding = 1; ++ if (sas_expander->handle == handle) ++ goto out; ++ pr_info("\texpander(0x%016llx): handle changed" \ ++ " from(0x%04x) to (0x%04x)!!!\n", ++ (unsigned long long)sas_expander->sas_address, ++ sas_expander->handle, handle); ++ sas_expander->handle = handle; ++ for (i = 0 ; i < sas_expander->num_phys ; i++) ++ sas_expander->phy[i].handle = handle; ++ goto out; ++ } ++ out: ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++} ++ ++/** ++ * _scsih_search_responding_expanders - ++ * @ioc: per adapter object ++ * ++ * After host reset, find out whether devices are still responding. ++ * If not remove. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_search_responding_expanders(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2ExpanderPage0_t expander_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 ioc_status; ++ u64 sas_address; ++ u16 handle; ++ ++ pr_info(MPT3SAS_FMT "search for expanders: start\n", ioc->name); ++ ++ if (list_empty(&ioc->sas_expander_list)) ++ goto out; ++ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, ++ MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) { ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ break; ++ ++ handle = le16_to_cpu(expander_pg0.DevHandle); ++ sas_address = le64_to_cpu(expander_pg0.SASAddress); ++ pr_info("\texpander present: handle(0x%04x), sas_addr(0x%016llx)\n", ++ handle, ++ (unsigned long long)sas_address); ++ _scsih_mark_responding_expander(ioc, sas_address, handle); ++ } ++ ++ out: ++ pr_info(MPT3SAS_FMT "search for expanders: complete\n", ioc->name); ++} ++ ++/** ++ * _scsih_remove_unresponding_sas_devices - removing unresponding devices ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_remove_unresponding_sas_devices(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct _sas_device *sas_device, *sas_device_next; ++ struct _sas_node *sas_expander, *sas_expander_next; ++ struct _raid_device *raid_device, *raid_device_next; ++ struct list_head tmp_list; ++ unsigned long flags; ++ LIST_HEAD(head); ++ ++ pr_info(MPT3SAS_FMT "removing unresponding devices: start\n", ++ ioc->name); ++ ++ /* removing unresponding end devices */ ++ pr_info(MPT3SAS_FMT "removing unresponding devices: end-devices\n", ++ ioc->name); ++ /* ++ * Iterate, pulling off devices marked as non-responding. We become the ++ * owner for the reference the list had on any object we prune. ++ */ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ list_for_each_entry_safe(sas_device, sas_device_next, ++ &ioc->sas_device_list, list) { ++ if (!sas_device->responding) ++ list_move_tail(&sas_device->list, &head); ++ else ++ sas_device->responding = 0; ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ /* ++ * Now, uninitialize and remove the unresponding devices we pruned. ++ */ ++ list_for_each_entry_safe(sas_device, sas_device_next, &head, list) { ++ _scsih_remove_device(ioc, sas_device); ++ list_del_init(&sas_device->list); ++ sas_device_put(sas_device); ++ } ++ ++ /* removing unresponding volumes */ ++ if (ioc->ir_firmware) { ++ pr_info(MPT3SAS_FMT "removing unresponding devices: volumes\n", ++ ioc->name); ++ list_for_each_entry_safe(raid_device, raid_device_next, ++ &ioc->raid_device_list, list) { ++ if (!raid_device->responding) ++ _scsih_sas_volume_delete(ioc, ++ raid_device->handle); ++ else ++ raid_device->responding = 0; ++ } ++ } ++ ++ /* removing unresponding expanders */ ++ pr_info(MPT3SAS_FMT "removing unresponding devices: expanders\n", ++ ioc->name); ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ INIT_LIST_HEAD(&tmp_list); ++ list_for_each_entry_safe(sas_expander, sas_expander_next, ++ &ioc->sas_expander_list, list) { ++ if (!sas_expander->responding) ++ list_move_tail(&sas_expander->list, &tmp_list); ++ else ++ sas_expander->responding = 0; ++ } ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ list_for_each_entry_safe(sas_expander, sas_expander_next, &tmp_list, ++ list) { ++ list_del(&sas_expander->list); ++ _scsih_expander_node_remove(ioc, sas_expander); ++ } ++ ++ pr_info(MPT3SAS_FMT "removing unresponding devices: complete\n", ++ ioc->name); ++ ++ /* unblock devices */ ++ _scsih_ublock_io_all_device(ioc); ++} ++ ++static void ++_scsih_refresh_expander_links(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_node *sas_expander, u16 handle) ++{ ++ Mpi2ExpanderPage1_t expander_pg1; ++ Mpi2ConfigReply_t mpi_reply; ++ int i; ++ ++ for (i = 0 ; i < sas_expander->num_phys ; i++) { ++ if ((mpt2sas_config_get_expander_pg1(ioc, &mpi_reply, ++ &expander_pg1, i, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ mpt2sas_transport_update_links(ioc, sas_expander->sas_address, ++ le16_to_cpu(expander_pg1.AttachedDevHandle), i, ++ expander_pg1.NegotiatedLinkRate >> 4); ++ } ++} ++ ++/** ++ * _scsih_scan_for_devices_after_reset - scan for devices after host reset ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2ExpanderPage0_t expander_pg0; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ Mpi2RaidVolPage1_t volume_pg1; ++ Mpi2RaidVolPage0_t volume_pg0; ++ Mpi2RaidPhysDiskPage0_t pd_pg0; ++ Mpi2EventIrConfigElement_t element; ++ Mpi2ConfigReply_t mpi_reply; ++ u8 phys_disk_num; ++ u16 ioc_status; ++ u16 handle, parent_handle; ++ u64 sas_address; ++ struct _sas_device *sas_device; ++ struct _sas_node *expander_device; ++ static struct _raid_device *raid_device; ++ u8 retry_count; ++ unsigned long flags; ++ ++ pr_info(MPT3SAS_FMT "scan devices: start\n", ioc->name); ++ ++ _scsih_sas_host_refresh(ioc); ++ ++ pr_info(MPT3SAS_FMT "\tscan devices: expanders start\n", ioc->name); ++ ++ /* expanders */ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, ++ MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_info(MPT3SAS_FMT "\tbreak from expander scan: " \ ++ "ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, ioc_status, ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ break; ++ } ++ handle = le16_to_cpu(expander_pg0.DevHandle); ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ expander_device = mpt2sas_scsih_expander_find_by_sas_address( ++ ioc, le64_to_cpu(expander_pg0.SASAddress)); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ if (expander_device) ++ _scsih_refresh_expander_links(ioc, expander_device, ++ handle); ++ else { ++ pr_info(MPT3SAS_FMT "\tBEFORE adding expander: " \ ++ "handle (0x%04x), sas_addr(0x%016llx)\n", ioc->name, ++ handle, (unsigned long long) ++ le64_to_cpu(expander_pg0.SASAddress)); ++ _scsih_expander_add(ioc, handle); ++ pr_info(MPT3SAS_FMT "\tAFTER adding expander: " \ ++ "handle (0x%04x), sas_addr(0x%016llx)\n", ioc->name, ++ handle, (unsigned long long) ++ le64_to_cpu(expander_pg0.SASAddress)); ++ } ++ } ++ ++ pr_info(MPT3SAS_FMT "\tscan devices: expanders complete\n", ++ ioc->name); ++ ++ if (!ioc->ir_firmware) ++ goto skip_to_sas; ++ ++ pr_info(MPT3SAS_FMT "\tscan devices: phys disk start\n", ioc->name); ++ ++ /* phys disk */ ++ phys_disk_num = 0xFF; ++ while (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, ++ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM, ++ phys_disk_num))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_info(MPT3SAS_FMT "\tbreak from phys disk scan: "\ ++ "ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, ioc_status, ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ break; ++ } ++ phys_disk_num = pd_pg0.PhysDiskNum; ++ handle = le16_to_cpu(pd_pg0.DevHandle); ++ sas_device = mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (sas_device) { ++ sas_device_put(sas_device); ++ continue; ++ } ++ if (mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, ++ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ++ handle) != 0) ++ continue; ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_info(MPT3SAS_FMT "\tbreak from phys disk scan " \ ++ "ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, ioc_status, ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ break; ++ } ++ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); ++ if (!_scsih_get_sas_address(ioc, parent_handle, ++ &sas_address)) { ++ pr_info(MPT3SAS_FMT "\tBEFORE adding phys disk: " \ ++ " handle (0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, handle, (unsigned long long) ++ le64_to_cpu(sas_device_pg0.SASAddress)); ++ mpt2sas_transport_update_links(ioc, sas_address, ++ handle, sas_device_pg0.PhyNum, ++ MPI2_SAS_NEG_LINK_RATE_1_5); ++ set_bit(handle, ioc->pd_handles); ++ retry_count = 0; ++ /* This will retry adding the end device. ++ * _scsih_add_device() will decide on retries and ++ * return "1" when it should be retried ++ */ ++ while (_scsih_add_device(ioc, handle, retry_count++, ++ 1)) { ++ ssleep(1); ++ } ++ pr_info(MPT3SAS_FMT "\tAFTER adding phys disk: " \ ++ " handle (0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, handle, (unsigned long long) ++ le64_to_cpu(sas_device_pg0.SASAddress)); ++ } ++ } ++ ++ pr_info(MPT3SAS_FMT "\tscan devices: phys disk complete\n", ++ ioc->name); ++ ++ pr_info(MPT3SAS_FMT "\tscan devices: volumes start\n", ioc->name); ++ ++ /* volumes */ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, ++ &volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_info(MPT3SAS_FMT "\tbreak from volume scan: " \ ++ "ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, ioc_status, ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ break; ++ } ++ handle = le16_to_cpu(volume_pg1.DevHandle); ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = _scsih_raid_device_find_by_wwid(ioc, ++ le64_to_cpu(volume_pg1.WWID)); ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ if (raid_device) ++ continue; ++ if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, ++ &volume_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, ++ sizeof(Mpi2RaidVolPage0_t))) ++ continue; ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_info(MPT3SAS_FMT "\tbreak from volume scan: " \ ++ "ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, ioc_status, ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ break; ++ } ++ if (volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_OPTIMAL || ++ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_ONLINE || ++ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_DEGRADED) { ++ memset(&element, 0, sizeof(Mpi2EventIrConfigElement_t)); ++ element.ReasonCode = MPI2_EVENT_IR_CHANGE_RC_ADDED; ++ element.VolDevHandle = volume_pg1.DevHandle; ++ pr_info(MPT3SAS_FMT ++ "\tBEFORE adding volume: handle (0x%04x)\n", ++ ioc->name, volume_pg1.DevHandle); ++ _scsih_sas_volume_add(ioc, &element); ++ pr_info(MPT3SAS_FMT ++ "\tAFTER adding volume: handle (0x%04x)\n", ++ ioc->name, volume_pg1.DevHandle); ++ } ++ } ++ ++ pr_info(MPT3SAS_FMT "\tscan devices: volumes complete\n", ++ ioc->name); ++ ++ skip_to_sas: ++ ++ pr_info(MPT3SAS_FMT "\tscan devices: end devices start\n", ++ ioc->name); ++ ++ /* sas devices */ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, ++ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, ++ handle))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_info(MPT3SAS_FMT "\tbreak from end device scan:"\ ++ " ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, ioc_status, ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ break; ++ } ++ handle = le16_to_cpu(sas_device_pg0.DevHandle); ++ if (!(_scsih_is_end_device( ++ le32_to_cpu(sas_device_pg0.DeviceInfo)))) ++ continue; ++ sas_device = mpt2sas_get_sdev_by_addr(ioc, ++ le64_to_cpu(sas_device_pg0.SASAddress)); ++ if (sas_device) { ++ sas_device_put(sas_device); ++ continue; ++ } ++ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); ++ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) { ++ pr_info(MPT3SAS_FMT "\tBEFORE adding end device: " \ ++ "handle (0x%04x), sas_addr(0x%016llx)\n", ioc->name, ++ handle, (unsigned long long) ++ le64_to_cpu(sas_device_pg0.SASAddress)); ++ mpt2sas_transport_update_links(ioc, sas_address, handle, ++ sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); ++ retry_count = 0; ++ /* This will retry adding the end device. ++ * _scsih_add_device() will decide on retries and ++ * return "1" when it should be retried ++ */ ++ while (_scsih_add_device(ioc, handle, retry_count++, ++ 0)) { ++ ssleep(1); ++ } ++ pr_info(MPT3SAS_FMT "\tAFTER adding end device: " \ ++ "handle (0x%04x), sas_addr(0x%016llx)\n", ioc->name, ++ handle, (unsigned long long) ++ le64_to_cpu(sas_device_pg0.SASAddress)); ++ } ++ } ++ pr_info(MPT3SAS_FMT "\tscan devices: end devices complete\n", ++ ioc->name); ++ ++ pr_info(MPT3SAS_FMT "scan devices: complete\n", ioc->name); ++} ++/** ++ * mpt2sas_scsih_reset_handler - reset callback handler (for scsih) ++ * @ioc: per adapter object ++ * @reset_phase: phase ++ * ++ * The handler for doing any required cleanup or initialization. ++ * ++ * The reset phase can be MPT3_IOC_PRE_RESET, MPT3_IOC_AFTER_RESET, ++ * MPT3_IOC_DONE_RESET ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_scsih_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase) ++{ ++ switch (reset_phase) { ++ case MPT3_IOC_PRE_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_PRE_RESET\n", ioc->name, __func__)); ++ break; ++ case MPT3_IOC_AFTER_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_AFTER_RESET\n", ioc->name, __func__)); ++ if (ioc->scsih_cmds.status & MPT3_CMD_PENDING) { ++ ioc->scsih_cmds.status |= MPT3_CMD_RESET; ++ mpt2sas_base_free_smid(ioc, ioc->scsih_cmds.smid); ++ complete(&ioc->scsih_cmds.done); ++ } ++ if (ioc->tm_cmds.status & MPT3_CMD_PENDING) { ++ ioc->tm_cmds.status |= MPT3_CMD_RESET; ++ mpt2sas_base_free_smid(ioc, ioc->tm_cmds.smid); ++ complete(&ioc->tm_cmds.done); ++ } ++ ++ _scsih_fw_event_cleanup_queue(ioc); ++ _scsih_flush_running_cmds(ioc); ++ break; ++ case MPT3_IOC_DONE_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_DONE_RESET\n", ioc->name, __func__)); ++ if ((!ioc->is_driver_loading) && !(disable_discovery > 0 && ++ !ioc->sas_hba.num_phys)) { ++ _scsih_prep_device_scan(ioc); ++ _scsih_search_responding_sas_devices(ioc); ++ _scsih_search_responding_raid_devices(ioc); ++ _scsih_search_responding_expanders(ioc); ++ _scsih_error_recovery_delete_devices(ioc); ++ } ++ break; ++ } ++} ++ ++/** ++ * _mpt2sas_fw_work - delayed task for processing firmware events ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_mpt2sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) ++{ ++ _scsih_fw_event_del_from_list(ioc, fw_event); ++ ++ /* the queue is being flushed so ignore this event */ ++ if (ioc->remove_host || ioc->pci_error_recovery) { ++ fw_event_work_put(fw_event); ++ return; ++ } ++ ++ switch (fw_event->event) { ++ case MPT3SAS_PROCESS_TRIGGER_DIAG: ++ mpt2sas_process_trigger_data(ioc, ++ (struct SL_WH_TRIGGERS_EVENT_DATA_T *) ++ fw_event->event_data); ++ break; ++ case MPT3SAS_REMOVE_UNRESPONDING_DEVICES: ++ while (scsi_host_in_recovery(ioc->shost) || ++ ioc->shost_recovery) { ++ /* ++ * If we're unloading, bail. Otherwise, this can become ++ * an infinite loop. ++ */ ++ if (ioc->remove_host) ++ goto out; ++ ssleep(1); ++ } ++ _scsih_remove_unresponding_sas_devices(ioc); ++ _scsih_scan_for_devices_after_reset(ioc); ++ break; ++ case MPT3SAS_PORT_ENABLE_COMPLETE: ++ ioc->start_scan = 0; ++ if (missing_delay[0] != -1 && missing_delay[1] != -1) ++ mpt2sas_base_update_missing_delay(ioc, missing_delay[0], ++ missing_delay[1]); ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "port enable: complete from worker thread\n", ++ ioc->name)); ++ break; ++ case MPT3SAS_TURN_ON_PFA_LED: ++ _scsih_turn_on_pfa_led(ioc, fw_event->device_handle); ++ break; ++ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: ++ _scsih_sas_topology_change_event(ioc, fw_event); ++ break; ++ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: ++ _scsih_sas_device_status_change_event(ioc, fw_event); ++ break; ++ case MPI2_EVENT_SAS_DISCOVERY: ++ _scsih_sas_discovery_event(ioc, fw_event); ++ break; ++ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: ++ _scsih_sas_broadcast_primitive_event(ioc, fw_event); ++ break; ++ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: ++ _scsih_sas_enclosure_dev_status_change_event(ioc, ++ fw_event); ++ break; ++ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: ++ _scsih_sas_ir_config_change_event(ioc, fw_event); ++ break; ++ case MPI2_EVENT_IR_VOLUME: ++ _scsih_sas_ir_volume_event(ioc, fw_event); ++ break; ++ case MPI2_EVENT_IR_PHYSICAL_DISK: ++ _scsih_sas_ir_physical_disk_event(ioc, fw_event); ++ break; ++ case MPI2_EVENT_IR_OPERATION_STATUS: ++ _scsih_sas_ir_operation_status_event(ioc, fw_event); ++ break; ++ } ++out: ++ fw_event_work_put(fw_event); ++} ++ ++/** ++ * _firmware_event_work ++ * @ioc: per adapter object ++ * @work: The fw_event_work object ++ * Context: user. ++ * ++ * wrappers for the work thread handling firmware events ++ * ++ * Return nothing. ++ */ ++ ++static void ++_firmware_event_work(struct work_struct *work) ++{ ++ struct fw_event_work *fw_event = container_of(work, ++ struct fw_event_work, work); ++ ++ _mpt2sas_fw_work(fw_event->ioc, fw_event); ++} ++ ++/** ++ * mpt2sas_scsih_event_callback - firmware event handler (called at ISR time) ++ * @ioc: per adapter object ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: interrupt. ++ * ++ * This function merely adds a new work task into ioc->firmware_event_thread. ++ * The tasks are worked from _firmware_event_work in user context. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, ++ u32 reply) ++{ ++ struct fw_event_work *fw_event; ++ Mpi2EventNotificationReply_t *mpi_reply; ++ u16 event; ++ u16 sz; ++ Mpi26EventDataActiveCableExcept_t *ActiveCableEventData; ++ ++ /* events turned off due to host reset or driver unloading */ ++ if (ioc->remove_host || ioc->pci_error_recovery) ++ return 1; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ ++ if (unlikely(!mpi_reply)) { ++ pr_err(MPT3SAS_FMT "mpi_reply not valid at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return 1; ++ } ++ ++ event = le16_to_cpu(mpi_reply->Event); ++ ++ if (event != MPI2_EVENT_LOG_ENTRY_ADDED) ++ mpt2sas_trigger_event(ioc, event, 0); ++ ++ switch (event) { ++ /* handle these */ ++ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: ++ { ++ Mpi2EventDataSasBroadcastPrimitive_t *baen_data = ++ (Mpi2EventDataSasBroadcastPrimitive_t *) ++ mpi_reply->EventData; ++ ++ if (baen_data->Primitive != ++ MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT) ++ return 1; ++ ++ if (ioc->broadcast_aen_busy) { ++ ioc->broadcast_aen_pending++; ++ return 1; ++ } else ++ ioc->broadcast_aen_busy = 1; ++ break; ++ } ++ ++ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: ++ _scsih_check_topo_delete_events(ioc, ++ (Mpi2EventDataSasTopologyChangeList_t *) ++ mpi_reply->EventData); ++ break; ++ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: ++ _scsih_check_ir_config_unhide_events(ioc, ++ (Mpi2EventDataIrConfigChangeList_t *) ++ mpi_reply->EventData); ++ break; ++ case MPI2_EVENT_IR_VOLUME: ++ _scsih_check_volume_delete_events(ioc, ++ (Mpi2EventDataIrVolume_t *) ++ mpi_reply->EventData); ++ break; ++ case MPI2_EVENT_LOG_ENTRY_ADDED: ++ { ++ Mpi2EventDataLogEntryAdded_t *log_entry; ++ u32 *log_code; ++ ++ if (!ioc->is_warpdrive) ++ break; ++ ++ log_entry = (Mpi2EventDataLogEntryAdded_t *) ++ mpi_reply->EventData; ++ log_code = (u32 *)log_entry->LogData; ++ ++ if (le16_to_cpu(log_entry->LogEntryQualifier) ++ != MPT2_WARPDRIVE_LOGENTRY) ++ break; ++ ++ switch (le32_to_cpu(*log_code)) { ++ case MPT2_WARPDRIVE_LC_SSDT: ++ pr_warn(MPT3SAS_FMT "WarpDrive Warning: " ++ "IO Throttling has occurred in the WarpDrive " ++ "subsystem. Check WarpDrive documentation for " ++ "additional details.\n", ioc->name); ++ break; ++ case MPT2_WARPDRIVE_LC_SSDLW: ++ pr_warn(MPT3SAS_FMT "WarpDrive Warning: " ++ "Program/Erase Cycles for the WarpDrive subsystem " ++ "in degraded range. Check WarpDrive documentation " ++ "for additional details.\n", ioc->name); ++ break; ++ case MPT2_WARPDRIVE_LC_SSDLF: ++ pr_err(MPT3SAS_FMT "WarpDrive Fatal Error: " ++ "There are no Program/Erase Cycles for the " ++ "WarpDrive subsystem. The storage device will be " ++ "in read-only mode. Check WarpDrive documentation " ++ "for additional details.\n", ioc->name); ++ break; ++ case MPT2_WARPDRIVE_LC_BRMF: ++ pr_err(MPT3SAS_FMT "WarpDrive Fatal Error: " ++ "The Backup Rail Monitor has failed on the " ++ "WarpDrive subsystem. Check WarpDrive " ++ "documentation for additional details.\n", ++ ioc->name); ++ break; ++ } ++ ++ break; ++ } ++ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: ++ case MPI2_EVENT_IR_OPERATION_STATUS: ++ case MPI2_EVENT_SAS_DISCOVERY: ++ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: ++ case MPI2_EVENT_IR_PHYSICAL_DISK: ++ break; ++ ++ case MPI2_EVENT_TEMP_THRESHOLD: ++ _scsih_temp_threshold_events(ioc, ++ (Mpi2EventDataTemperature_t *) ++ mpi_reply->EventData); ++ break; ++ case MPI2_EVENT_ACTIVE_CABLE_EXCEPTION: ++ ActiveCableEventData = ++ (Mpi26EventDataActiveCableExcept_t *) mpi_reply->EventData; ++ if (ActiveCableEventData->ReasonCode == ++ MPI26_EVENT_ACTIVE_CABLE_INSUFFICIENT_POWER) ++ pr_info(MPT3SAS_FMT "Currently an active cable with ReceptacleID %d", ++ ioc->name, ActiveCableEventData->ReceptacleID); ++ pr_info("cannot be powered and devices connected to this active cable"); ++ pr_info("will not be seen. This active cable"); ++ pr_info("requires %d mW of power", ++ ActiveCableEventData->ActiveCablePowerRequirement); ++ break; ++ ++ default: /* ignore the rest */ ++ return 1; ++ } ++ ++ sz = le16_to_cpu(mpi_reply->EventDataLength) * 4; ++ fw_event = alloc_fw_event_work(sz); ++ if (!fw_event) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return 1; ++ } ++ ++ memcpy(fw_event->event_data, mpi_reply->EventData, sz); ++ fw_event->ioc = ioc; ++ fw_event->VF_ID = mpi_reply->VF_ID; ++ fw_event->VP_ID = mpi_reply->VP_ID; ++ fw_event->event = event; ++ _scsih_fw_event_add(ioc, fw_event); ++ fw_event_work_put(fw_event); ++ return 1; ++} ++ ++/** ++ * _scsih_expander_node_remove - removing expander device from list. ++ * @ioc: per adapter object ++ * @sas_expander: the sas_device object ++ * Context: Calling function should acquire ioc->sas_node_lock. ++ * ++ * Removing object and freeing associated memory from the ++ * ioc->sas_expander_list. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_node *sas_expander) ++{ ++ struct _sas_port *mpt2sas_port, *next; ++ ++ /* remove sibling ports attached to this expander */ ++ list_for_each_entry_safe(mpt2sas_port, next, ++ &sas_expander->sas_port_list, port_list) { ++ if (ioc->shost_recovery) ++ return; ++ if (mpt2sas_port->remote_identify.device_type == ++ SAS_END_DEVICE) ++ mpt2sas_device_remove_by_sas_address(ioc, ++ mpt2sas_port->remote_identify.sas_address); ++ else if (mpt2sas_port->remote_identify.device_type == ++ SAS_EDGE_EXPANDER_DEVICE || ++ mpt2sas_port->remote_identify.device_type == ++ SAS_FANOUT_EXPANDER_DEVICE) ++ mpt2sas_expander_remove(ioc, ++ mpt2sas_port->remote_identify.sas_address); ++ } ++ ++ mpt2sas_transport_port_remove(ioc, sas_expander->sas_address, ++ sas_expander->sas_address_parent); ++ ++ pr_info(MPT3SAS_FMT ++ "expander_remove: handle(0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, ++ sas_expander->handle, (unsigned long long) ++ sas_expander->sas_address); ++ ++ kfree(sas_expander->phy); ++ kfree(sas_expander); ++} ++ ++/** ++ * _scsih_ir_shutdown - IR shutdown notification ++ * @ioc: per adapter object ++ * ++ * Sending RAID Action to alert the Integrated RAID subsystem of the IOC that ++ * the host system is shutting down. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_ir_shutdown(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2RaidActionRequest_t *mpi_request; ++ Mpi2RaidActionReply_t *mpi_reply; ++ u16 smid; ++ ++ /* is IR firmware build loaded ? */ ++ if (!ioc->ir_firmware) ++ return; ++ ++ /* are there any volumes ? */ ++ if (list_empty(&ioc->raid_device_list)) ++ return; ++ ++ mutex_lock(&ioc->scsih_cmds.mutex); ++ ++ if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: scsih_cmd in use\n", ++ ioc->name, __func__); ++ goto out; ++ } ++ ioc->scsih_cmds.status = MPT3_CMD_PENDING; ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->scsih_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; ++ goto out; ++ } ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->scsih_cmds.smid = smid; ++ memset(mpi_request, 0, sizeof(Mpi2RaidActionRequest_t)); ++ ++ mpi_request->Function = MPI2_FUNCTION_RAID_ACTION; ++ mpi_request->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED; ++ ++ if (!ioc->hide_ir_msg) ++ pr_info(MPT3SAS_FMT "IR shutdown (sending)\n", ioc->name); ++ init_completion(&ioc->scsih_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ); ++ ++ if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ goto out; ++ } ++ ++ if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) { ++ mpi_reply = ioc->scsih_cmds.reply; ++ if (!ioc->hide_ir_msg) ++ pr_info(MPT3SAS_FMT "IR shutdown " ++ "(complete): ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply->IOCStatus), ++ le32_to_cpu(mpi_reply->IOCLogInfo)); ++ } ++ ++ out: ++ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_unlock(&ioc->scsih_cmds.mutex); ++} ++ ++/** ++ * scsih_remove_mpt2sas - detach and remove add host ++ * @pdev: PCI device struct ++ * ++ * Routine called when unloading the driver. ++ * Return nothing. ++ */ ++void scsih_remove_mpt2sas(struct pci_dev *pdev) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ struct _sas_port *mpt2sas_port, *next_port; ++ struct _raid_device *raid_device, *next; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct workqueue_struct *wq; ++ unsigned long flags; ++ ++ ioc->remove_host = 1; ++ _scsih_fw_event_cleanup_queue(ioc); ++ ++ spin_lock_irqsave(&ioc->fw_event_lock, flags); ++ wq = ioc->firmware_event_thread; ++ ioc->firmware_event_thread = NULL; ++ spin_unlock_irqrestore(&ioc->fw_event_lock, flags); ++ if (wq) ++ destroy_workqueue(wq); ++ ++ /* release all the volumes */ ++ _scsih_ir_shutdown(ioc); ++ list_for_each_entry_safe(raid_device, next, &ioc->raid_device_list, ++ list) { ++ if (raid_device->starget) { ++ sas_target_priv_data = ++ raid_device->starget->hostdata; ++ sas_target_priv_data->deleted = 1; ++ scsi_remove_target(&raid_device->starget->dev); ++ } ++ pr_info(MPT3SAS_FMT "removing handle(0x%04x), wwid(0x%016llx)\n", ++ ioc->name, raid_device->handle, ++ (unsigned long long) raid_device->wwid); ++ _scsih_raid_device_remove(ioc, raid_device); ++ } ++ ++ /* free ports attached to the sas_host */ ++ list_for_each_entry_safe(mpt2sas_port, next_port, ++ &ioc->sas_hba.sas_port_list, port_list) { ++ if (mpt2sas_port->remote_identify.device_type == ++ SAS_END_DEVICE) ++ mpt2sas_device_remove_by_sas_address(ioc, ++ mpt2sas_port->remote_identify.sas_address); ++ else if (mpt2sas_port->remote_identify.device_type == ++ SAS_EDGE_EXPANDER_DEVICE || ++ mpt2sas_port->remote_identify.device_type == ++ SAS_FANOUT_EXPANDER_DEVICE) ++ mpt2sas_expander_remove(ioc, ++ mpt2sas_port->remote_identify.sas_address); ++ } ++ ++ /* free phys attached to the sas_host */ ++ if (ioc->sas_hba.num_phys) { ++ kfree(ioc->sas_hba.phy); ++ ioc->sas_hba.phy = NULL; ++ ioc->sas_hba.num_phys = 0; ++ } ++ ++ sas_remove_host(shost); ++ scsi_remove_host(shost); ++ mpt2sas_base_detach(ioc); ++ spin_lock(&gioc_lock_mpt2sas); ++ list_del(&ioc->list); ++ spin_unlock(&gioc_lock_mpt2sas); ++ scsi_host_put(shost); ++} ++ ++/** ++ * scsih_shutdown_mpt2sas - routine call during system shutdown ++ * @pdev: PCI device struct ++ * ++ * Return nothing. ++ */ ++void ++scsih_shutdown_mpt2sas(struct pci_dev *pdev) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ struct workqueue_struct *wq; ++ unsigned long flags; ++ ++ ioc->remove_host = 1; ++ _scsih_fw_event_cleanup_queue(ioc); ++ ++ spin_lock_irqsave(&ioc->fw_event_lock, flags); ++ wq = ioc->firmware_event_thread; ++ ioc->firmware_event_thread = NULL; ++ spin_unlock_irqrestore(&ioc->fw_event_lock, flags); ++ if (wq) ++ destroy_workqueue(wq); ++ ++ _scsih_ir_shutdown(ioc); ++ mpt2sas_base_detach(ioc); ++} ++ ++ ++/** ++ * _scsih_probe_boot_devices - reports 1st device ++ * @ioc: per adapter object ++ * ++ * If specified in bios page 2, this routine reports the 1st ++ * device scsi-ml or sas transport for persistent boot device ++ * purposes. Please refer to function _scsih_determine_boot_device() ++ */ ++static void ++_scsih_probe_boot_devices(struct MPT3SAS_ADAPTER *ioc) ++{ ++ u8 is_raid; ++ void *device; ++ struct _sas_device *sas_device; ++ struct _raid_device *raid_device; ++ u16 handle; ++ u64 sas_address_parent; ++ u64 sas_address; ++ unsigned long flags; ++ int rc; ++ ++ /* no Bios, return immediately */ ++ if (!ioc->bios_pg3.BiosVersion) ++ return; ++ ++ device = NULL; ++ is_raid = 0; ++ if (ioc->req_boot_device.device) { ++ device = ioc->req_boot_device.device; ++ is_raid = ioc->req_boot_device.is_raid; ++ } else if (ioc->req_alt_boot_device.device) { ++ device = ioc->req_alt_boot_device.device; ++ is_raid = ioc->req_alt_boot_device.is_raid; ++ } else if (ioc->current_boot_device.device) { ++ device = ioc->current_boot_device.device; ++ is_raid = ioc->current_boot_device.is_raid; ++ } ++ ++ if (!device) ++ return; ++ ++ if (is_raid) { ++ raid_device = device; ++ rc = scsi_add_device(ioc->shost, RAID_CHANNEL, ++ raid_device->id, 0); ++ if (rc) ++ _scsih_raid_device_remove(ioc, raid_device); ++ } else { ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = device; ++ handle = sas_device->handle; ++ sas_address_parent = sas_device->sas_address_parent; ++ sas_address = sas_device->sas_address; ++ list_move_tail(&sas_device->list, &ioc->sas_device_list); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ if (ioc->hide_drives) ++ return; ++ if (!mpt2sas_transport_port_add(ioc, handle, ++ sas_address_parent)) { ++ _scsih_sas_device_remove(ioc, sas_device); ++ } else if (!sas_device->starget) { ++ if (!ioc->is_driver_loading) { ++ mpt2sas_transport_port_remove(ioc, ++ sas_address, ++ sas_address_parent); ++ _scsih_sas_device_remove(ioc, sas_device); ++ } ++ } ++ } ++} ++ ++/** ++ * _scsih_probe_raid - reporting raid volumes to scsi-ml ++ * @ioc: per adapter object ++ * ++ * Called during initial loading of the driver. ++ */ ++static void ++_scsih_probe_raid(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct _raid_device *raid_device, *raid_next; ++ int rc; ++ ++ list_for_each_entry_safe(raid_device, raid_next, ++ &ioc->raid_device_list, list) { ++ if (raid_device->starget) ++ continue; ++ rc = scsi_add_device(ioc->shost, RAID_CHANNEL, ++ raid_device->id, 0); ++ if (rc) ++ _scsih_raid_device_remove(ioc, raid_device); ++ } ++} ++ ++static struct _sas_device *get_next_sas_device(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct _sas_device *sas_device = NULL; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ if (!list_empty(&ioc->sas_device_init_list)) { ++ sas_device = list_first_entry(&ioc->sas_device_init_list, ++ struct _sas_device, list); ++ sas_device_get(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ return sas_device; ++} ++ ++static void sas_device_make_active(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_device *sas_device) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ ++ /* ++ * Since we dropped the lock during the call to port_add(), we need to ++ * be careful here that somebody else didn't move or delete this item ++ * while we were busy with other things. ++ * ++ * If it was on the list, we need a put() for the reference the list ++ * had. Either way, we need a get() for the destination list. ++ */ ++ if (!list_empty(&sas_device->list)) { ++ list_del_init(&sas_device->list); ++ sas_device_put(sas_device); ++ } ++ ++ sas_device_get(sas_device); ++ list_add_tail(&sas_device->list, &ioc->sas_device_list); ++ ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++} ++ ++/** ++ * _scsih_probe_sas - reporting sas devices to sas transport ++ * @ioc: per adapter object ++ * ++ * Called during initial loading of the driver. ++ */ ++static void ++_scsih_probe_sas(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct _sas_device *sas_device; ++ ++ if (ioc->hide_drives) ++ return; ++ ++ while ((sas_device = get_next_sas_device(ioc))) { ++ if (!mpt2sas_transport_port_add(ioc, sas_device->handle, ++ sas_device->sas_address_parent)) { ++ _scsih_sas_device_remove(ioc, sas_device); ++ sas_device_put(sas_device); ++ continue; ++ } else if (!sas_device->starget) { ++ /* ++ * When asyn scanning is enabled, its not possible to ++ * remove devices while scanning is turned on due to an ++ * oops in scsi_sysfs_add_sdev()->add_device()-> ++ * sysfs_addrm_start() ++ */ ++ if (!ioc->is_driver_loading) { ++ mpt2sas_transport_port_remove(ioc, ++ sas_device->sas_address, ++ sas_device->sas_address_parent); ++ _scsih_sas_device_remove(ioc, sas_device); ++ sas_device_put(sas_device); ++ continue; ++ } ++ } ++ sas_device_make_active(ioc, sas_device); ++ sas_device_put(sas_device); ++ } ++} ++ ++/** ++ * _scsih_probe_devices - probing for devices ++ * @ioc: per adapter object ++ * ++ * Called during initial loading of the driver. ++ */ ++static void ++_scsih_probe_devices(struct MPT3SAS_ADAPTER *ioc) ++{ ++ u16 volume_mapping_flags; ++ ++ if (!(ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR)) ++ return; /* return when IOC doesn't support initiator mode */ ++ ++ _scsih_probe_boot_devices(ioc); ++ ++ if (ioc->ir_firmware) { ++ volume_mapping_flags = ++ le16_to_cpu(ioc->ioc_pg8.IRVolumeMappingFlags) & ++ MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE; ++ if (volume_mapping_flags == ++ MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING) { ++ _scsih_probe_raid(ioc); ++ _scsih_probe_sas(ioc); ++ } else { ++ _scsih_probe_sas(ioc); ++ _scsih_probe_raid(ioc); ++ } ++ } else ++ _scsih_probe_sas(ioc); ++} ++ ++/** ++ * scsih_scan_start_mpt2sas - scsi lld callback for .scan_start ++ * @shost: SCSI host pointer ++ * ++ * The shost has the ability to discover targets on its own instead ++ * of scanning the entire bus. In our implemention, we will kick off ++ * firmware discovery. ++ */ ++void ++scsih_scan_start_mpt2sas(struct Scsi_Host *shost) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ int rc; ++ if (diag_buffer_enable != -1 && diag_buffer_enable != 0) ++ mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable); ++ ++ if (disable_discovery > 0) ++ return; ++ ++ ioc->start_scan = 1; ++ rc = mpt2sas_port_enable(ioc); ++ ++ if (rc != 0) ++ pr_info(MPT3SAS_FMT "port enable: FAILED\n", ioc->name); ++} ++ ++/** ++ * scsih_scan_finished_mpt2sas - scsi lld callback for .scan_finished ++ * @shost: SCSI host pointer ++ * @time: elapsed time of the scan in jiffies ++ * ++ * This function will be called periodicallyn until it returns 1 with the ++ * scsi_host and the elapsed time of the scan in jiffies. In our implemention, ++ * we wait for firmware discovery to complete, then return 1. ++ */ ++int ++scsih_scan_finished_mpt2sas(struct Scsi_Host *shost, unsigned long time) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ if (disable_discovery > 0) { ++ ioc->is_driver_loading = 0; ++ ioc->wait_for_discovery_to_complete = 0; ++ return 1; ++ } ++ ++ if (time >= (300 * HZ)) { ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ pr_info(MPT3SAS_FMT ++ "port enable: FAILED with timeout (timeout=300s)\n", ++ ioc->name); ++ ioc->is_driver_loading = 0; ++ return 1; ++ } ++ ++ if (ioc->start_scan) ++ return 0; ++ ++ if (ioc->start_scan_failed) { ++ pr_info(MPT3SAS_FMT ++ "port enable: FAILED with (ioc_status=0x%08x)\n", ++ ioc->name, ioc->start_scan_failed); ++ ioc->is_driver_loading = 0; ++ ioc->wait_for_discovery_to_complete = 0; ++ ioc->remove_host = 1; ++ return 1; ++ } ++ ++ pr_info(MPT3SAS_FMT "port enable: SUCCESS\n", ioc->name); ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ ++ if (ioc->wait_for_discovery_to_complete) { ++ ioc->wait_for_discovery_to_complete = 0; ++ _scsih_probe_devices(ioc); ++ } ++ mpt2sas_base_start_watchdog(ioc); ++ ioc->is_driver_loading = 0; ++ return 1; ++} ++ ++/* shost template for SAS 2.0 HBA devices */ ++static struct scsi_host_template mpt2sas_driver_template_mpt2sas = { ++ .module = THIS_MODULE, ++ .name = "Fusion MPT SAS Host", ++ .proc_name = MPT2SAS_DRIVER_NAME, ++ .queuecommand = scsih_qcmd_mpt2sas, ++ .target_alloc = scsih_target_alloc_mpt2sas, ++ .slave_alloc = scsih_slave_alloc_mpt2sas, ++ .slave_configure = scsih_slave_configure_mpt2sas, ++ .target_destroy = scsih_target_destroy_mpt2sas, ++ .slave_destroy = scsih_slave_destroy_mpt2sas, ++ .scan_finished = scsih_scan_finished_mpt2sas, ++ .scan_start = scsih_scan_start_mpt2sas, ++ .change_queue_depth = scsih_change_queue_depth_mpt2sas, ++ .change_queue_type = _scsih_change_queue_type_mpt2sas, ++ .eh_abort_handler = scsih_abort_mpt2sas, ++ .eh_device_reset_handler = scsih_dev_reset_mpt2sas, ++ .eh_target_reset_handler = scsih_target_reset_mpt2sas, ++ .eh_host_reset_handler = scsih_host_reset_mpt2sas, ++ .bios_param = scsih_bios_param_mpt2sas, ++ .can_queue = 1, ++ .this_id = -1, ++ .sg_tablesize = MPT2SAS_SG_DEPTH, ++ .max_sectors = 32767, ++ .cmd_per_lun = 7, ++ .use_clustering = ENABLE_CLUSTERING, ++ .shost_attrs = mpt2sas_host_attrs, ++ .sdev_attrs = mpt2sas_dev_attrs, ++}; ++ ++#ifdef MPT2SAS_SCSI ++/* raid transport support for SAS 2.0 HBA devices */ ++static struct raid_function_template mpt2sas_raid_functions = { ++ .cookie = &mpt2sas_driver_template_mpt2sas, ++ .is_raid = scsih_is_raid_mpt2sas, ++ .get_resync = scsih_get_resync_mpt2sas, ++ .get_state = scsih_get_state_mpt2sas, ++}; ++#endif /* MPT2SAS_SCSI */ ++ ++/* shost template for SAS 3.0 HBA devices */ ++static struct scsi_host_template mpt3sas_driver_template_mpt2sas = { ++ .module = THIS_MODULE, ++ .name = "Fusion MPT SAS Host", ++ .proc_name = MPT3SAS_DRIVER_NAME, ++ .queuecommand = scsih_qcmd_mpt2sas, ++ .target_alloc = scsih_target_alloc_mpt2sas, ++ .slave_alloc = scsih_slave_alloc_mpt2sas, ++ .slave_configure = scsih_slave_configure_mpt2sas, ++ .target_destroy = scsih_target_destroy_mpt2sas, ++ .slave_destroy = scsih_slave_destroy_mpt2sas, ++ .scan_finished = scsih_scan_finished_mpt2sas, ++ .scan_start = scsih_scan_start_mpt2sas, ++ .change_queue_depth = scsih_change_queue_depth_mpt2sas, ++ .change_queue_type = _scsih_change_queue_type_mpt2sas, ++ .eh_abort_handler = scsih_abort_mpt2sas, ++ .eh_device_reset_handler = scsih_dev_reset_mpt2sas, ++ .eh_target_reset_handler = scsih_target_reset_mpt2sas, ++ .eh_host_reset_handler = scsih_host_reset_mpt2sas, ++ .bios_param = scsih_bios_param_mpt2sas, ++ .can_queue = 1, ++ .this_id = -1, ++ .sg_tablesize = MPT3SAS_SG_DEPTH, ++ .max_sectors = 32767, ++ .cmd_per_lun = 7, ++ .use_clustering = ENABLE_CLUSTERING, ++ .shost_attrs = mpt2sas_host_attrs, ++ .sdev_attrs = mpt2sas_dev_attrs, ++}; ++ ++#ifndef MPT2SAS_SCSI ++/* raid transport support for SAS 3.0 HBA devices */ ++static struct raid_function_template mpt3sas_raid_functions = { ++ .cookie = &mpt3sas_driver_template_mpt2sas, ++ .is_raid = scsih_is_raid_mpt2sas, ++ .get_resync = scsih_get_resync_mpt2sas, ++ .get_state = scsih_get_state_mpt2sas, ++}; ++#endif /* MPT2SAS_SCSI */ ++ ++/** ++ * _scsih_determine_hba_mpi_version_mpt2sas - determine in which MPI version class ++ * this device belongs to. ++ * @pdev: PCI device struct ++ * ++ * return MPI2_VERSION for SAS 2.0 HBA devices, ++ * MPI25_VERSION for SAS 3.0 HBA devices, and ++ * MPI26 VERSION for Cutlass & Invader SAS 3.0 HBA devices ++ */ ++u16 ++_scsih_determine_hba_mpi_version_mpt2sas(struct pci_dev *pdev) ++{ ++ ++ switch (pdev->device) { ++ case MPI2_MFGPAGE_DEVID_SSS6200: ++ case MPI2_MFGPAGE_DEVID_SAS2004: ++ case MPI2_MFGPAGE_DEVID_SAS2008: ++ case MPI2_MFGPAGE_DEVID_SAS2108_1: ++ case MPI2_MFGPAGE_DEVID_SAS2108_2: ++ case MPI2_MFGPAGE_DEVID_SAS2108_3: ++ case MPI2_MFGPAGE_DEVID_SAS2116_1: ++ case MPI2_MFGPAGE_DEVID_SAS2116_2: ++ case MPI2_MFGPAGE_DEVID_SAS2208_1: ++ case MPI2_MFGPAGE_DEVID_SAS2208_2: ++ case MPI2_MFGPAGE_DEVID_SAS2208_3: ++ case MPI2_MFGPAGE_DEVID_SAS2208_4: ++ case MPI2_MFGPAGE_DEVID_SAS2208_5: ++ case MPI2_MFGPAGE_DEVID_SAS2208_6: ++ case MPI2_MFGPAGE_DEVID_SAS2308_1: ++ case MPI2_MFGPAGE_DEVID_SAS2308_2: ++ case MPI2_MFGPAGE_DEVID_SAS2308_3: ++ return MPI2_VERSION; ++ case MPI25_MFGPAGE_DEVID_SAS3004: ++ case MPI25_MFGPAGE_DEVID_SAS3008: ++ case MPI25_MFGPAGE_DEVID_SAS3108_1: ++ case MPI25_MFGPAGE_DEVID_SAS3108_2: ++ case MPI25_MFGPAGE_DEVID_SAS3108_5: ++ case MPI25_MFGPAGE_DEVID_SAS3108_6: ++ return MPI25_VERSION; ++ case MPI26_MFGPAGE_DEVID_SAS3216: ++ case MPI26_MFGPAGE_DEVID_SAS3224: ++ case MPI26_MFGPAGE_DEVID_SAS3316_1: ++ case MPI26_MFGPAGE_DEVID_SAS3316_2: ++ case MPI26_MFGPAGE_DEVID_SAS3316_3: ++ case MPI26_MFGPAGE_DEVID_SAS3316_4: ++ case MPI26_MFGPAGE_DEVID_SAS3324_1: ++ case MPI26_MFGPAGE_DEVID_SAS3324_2: ++ case MPI26_MFGPAGE_DEVID_SAS3324_3: ++ case MPI26_MFGPAGE_DEVID_SAS3324_4: ++ return MPI26_VERSION; ++ } ++ return 0; ++} ++ ++/** ++ * _scsih_probe_mpt2sas - attach and add scsi host ++ * @pdev: PCI device struct ++ * @id: pci device id ++ * ++ * Returns 0 success, anything else error. ++ */ ++int ++_scsih_probe_mpt2sas(struct pci_dev *pdev, const struct pci_device_id *id) ++{ ++ struct MPT3SAS_ADAPTER *ioc; ++ struct Scsi_Host *shost = NULL; ++ int rv; ++ u16 hba_mpi_version; ++ ++ /* Determine in which MPI version class this pci device belongs */ ++ hba_mpi_version = _scsih_determine_hba_mpi_version_mpt2sas(pdev); ++ if (hba_mpi_version == 0) ++ return -ENODEV; ++ ++ switch (hba_mpi_version) { ++ case MPI2_VERSION: ++ /* Use mpt2sas driver host template for SAS 2.0 HBA's */ ++ shost = scsi_host_alloc(&mpt2sas_driver_template_mpt2sas, ++ sizeof(struct MPT3SAS_ADAPTER)); ++ if (!shost) ++ return -ENODEV; ++ ioc = shost_priv(shost); ++ memset(ioc, 0, sizeof(struct MPT3SAS_ADAPTER)); ++ ioc->hba_mpi_version_belonged = hba_mpi_version; ++ ioc->id = mpt2_ids++; ++ sprintf(ioc->driver_name, "%s", MPT2SAS_DRIVER_NAME); ++ if (pdev->device == MPI2_MFGPAGE_DEVID_SSS6200) { ++ ioc->is_warpdrive = 1; ++ ioc->hide_ir_msg = 1; ++ } else ++ ioc->mfg_pg10_hide_flag = MFG_PAGE10_EXPOSE_ALL_DISKS; ++ break; ++ case MPI25_VERSION: ++ case MPI26_VERSION: ++ /* Use mpt3sas driver host template for SAS 3.0 HBA's */ ++ shost = scsi_host_alloc(&mpt3sas_driver_template_mpt2sas, ++ sizeof(struct MPT3SAS_ADAPTER)); ++ if (!shost) ++ return -ENODEV; ++ ioc = shost_priv(shost); ++ memset(ioc, 0, sizeof(struct MPT3SAS_ADAPTER)); ++ ioc->hba_mpi_version_belonged = hba_mpi_version; ++ ioc->id = mpt3_ids++; ++ sprintf(ioc->driver_name, "%s", MPT3SAS_DRIVER_NAME); ++ if ((ioc->hba_mpi_version_belonged == MPI25_VERSION && ++ pdev->revision >= SAS3_PCI_DEVICE_C0_REVISION) || ++ (ioc->hba_mpi_version_belonged == MPI26_VERSION)) ++ ioc->msix96_vector = 1; ++ break; ++ default: ++ return -ENODEV; ++ } ++ ++ INIT_LIST_HEAD(&ioc->list); ++ spin_lock(&gioc_lock_mpt2sas); ++ list_add_tail(&ioc->list, &mpt2sas_ioc_list); ++ spin_unlock(&gioc_lock_mpt2sas); ++ ioc->shost = shost; ++ ioc->pdev = pdev; ++ ioc->scsi_io_cb_idx = scsi_io_cb_idx; ++ ioc->tm_cb_idx = tm_cb_idx; ++ ioc->ctl_cb_idx = ctl_cb_idx; ++ ioc->base_cb_idx = base_cb_idx; ++ ioc->port_enable_cb_idx = port_enable_cb_idx; ++ ioc->transport_cb_idx = transport_cb_idx; ++ ioc->scsih_cb_idx = scsih_cb_idx; ++ ioc->config_cb_idx = config_cb_idx; ++ ioc->tm_tr_cb_idx = tm_tr_cb_idx; ++ ioc->tm_tr_volume_cb_idx = tm_tr_volume_cb_idx; ++ ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx; ++ ioc->logging_level = logging_level; ++ ioc->schedule_dead_ioc_flush_running_cmds = &_scsih_flush_running_cmds; ++ /* misc semaphores and spin locks */ ++ mutex_init(&ioc->reset_in_progress_mutex); ++ /* initializing pci_access_mutex lock */ ++ mutex_init(&ioc->pci_access_mutex); ++ spin_lock_init(&ioc->ioc_reset_in_progress_lock); ++ spin_lock_init(&ioc->scsi_lookup_lock); ++ spin_lock_init(&ioc->sas_device_lock); ++ spin_lock_init(&ioc->sas_node_lock); ++ spin_lock_init(&ioc->fw_event_lock); ++ spin_lock_init(&ioc->raid_device_lock); ++ spin_lock_init(&ioc->diag_trigger_lock); ++ ++ INIT_LIST_HEAD(&ioc->sas_device_list); ++ INIT_LIST_HEAD(&ioc->sas_device_init_list); ++ INIT_LIST_HEAD(&ioc->sas_expander_list); ++ INIT_LIST_HEAD(&ioc->fw_event_list); ++ INIT_LIST_HEAD(&ioc->raid_device_list); ++ INIT_LIST_HEAD(&ioc->sas_hba.sas_port_list); ++ INIT_LIST_HEAD(&ioc->delayed_tr_list); ++ INIT_LIST_HEAD(&ioc->delayed_sc_list); ++ INIT_LIST_HEAD(&ioc->delayed_event_ack_list); ++ INIT_LIST_HEAD(&ioc->delayed_tr_volume_list); ++ INIT_LIST_HEAD(&ioc->reply_queue_list); ++ ++ sprintf(ioc->name, "%s_cm%d", ioc->driver_name, ioc->id); ++ ++ /* init shost parameters */ ++ shost->max_cmd_len = 32; ++ shost->max_lun = max_lun; ++ shost->transportt = mpt2sas_transport_template; ++ shost->unique_id = ioc->id; ++ ++ if (max_sectors != 0xFFFF) { ++ if (max_sectors < 64) { ++ shost->max_sectors = 64; ++ pr_warn(MPT3SAS_FMT "Invalid value %d passed " \ ++ "for max_sectors, range is 64 to 32767. Assigning " ++ "value of 64.\n", ioc->name, max_sectors); ++ } else if (max_sectors > 32767) { ++ shost->max_sectors = 32767; ++ pr_warn(MPT3SAS_FMT "Invalid value %d passed " \ ++ "for max_sectors, range is 64 to 32767. Assigning " ++ "default value of 32767.\n", ioc->name, ++ max_sectors); ++ } else { ++ shost->max_sectors = max_sectors & 0xFFFE; ++ pr_info(MPT3SAS_FMT ++ "The max_sectors value is set to %d\n", ++ ioc->name, shost->max_sectors); ++ } ++ } ++ ++ /* register EEDP capabilities with SCSI layer */ ++ if (prot_mask > 0) ++ scsi_host_set_prot(shost, prot_mask); ++ else ++ scsi_host_set_prot(shost, SHOST_DIF_TYPE1_PROTECTION ++ | SHOST_DIF_TYPE2_PROTECTION ++ | SHOST_DIF_TYPE3_PROTECTION); ++ ++ scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC); ++ ++ /* event thread */ ++ snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name), ++ "fw_event_%s%d", ioc->driver_name, ioc->id); ++ ioc->firmware_event_thread = alloc_ordered_workqueue( ++ ioc->firmware_event_name, WQ_MEM_RECLAIM); ++ if (!ioc->firmware_event_thread) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rv = -ENODEV; ++ goto out_thread_fail; ++ } ++ ++ ioc->is_driver_loading = 1; ++ if ((mpt2sas_base_attach(ioc))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rv = -ENODEV; ++ goto out_attach_fail; ++ } ++ ++ if (ioc->is_warpdrive) { ++ if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS) ++ ioc->hide_drives = 0; ++ else if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_HIDE_ALL_DISKS) ++ ioc->hide_drives = 1; ++ else { ++ if (mpt2sas_get_num_volumes(ioc)) ++ ioc->hide_drives = 1; ++ else ++ ioc->hide_drives = 0; ++ } ++ } else ++ ioc->hide_drives = 0; ++ ++ rv = scsi_add_host(shost, &pdev->dev); ++ if (rv) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out_add_shost_fail; ++ } ++ ++ scsi_scan_host(shost); ++ return 0; ++out_add_shost_fail: ++ mpt2sas_base_detach(ioc); ++ out_attach_fail: ++ destroy_workqueue(ioc->firmware_event_thread); ++ out_thread_fail: ++ spin_lock(&gioc_lock_mpt2sas); ++ list_del(&ioc->list); ++ spin_unlock(&gioc_lock_mpt2sas); ++ scsi_host_put(shost); ++ return rv; ++} ++ ++#ifdef CONFIG_PM ++/** ++ * scsih_suspend_mpt2sas - power management suspend main entry point ++ * @pdev: PCI device struct ++ * @state: PM state change to (usually PCI_D3) ++ * ++ * Returns 0 success, anything else error. ++ */ ++int ++scsih_suspend_mpt2sas(struct pci_dev *pdev, pm_message_t state) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ pci_power_t device_state; ++ ++ mpt2sas_base_stop_watchdog(ioc); ++ flush_scheduled_work(); ++ scsi_block_requests(shost); ++ device_state = pci_choose_state(pdev, state); ++ pr_info(MPT3SAS_FMT ++ "pdev=0x%p, slot=%s, entering operating state [D%d]\n", ++ ioc->name, pdev, pci_name(pdev), device_state); ++ ++ pci_save_state(pdev); ++ mpt2sas_base_free_resources(ioc); ++ pci_set_power_state(pdev, device_state); ++ return 0; ++} ++ ++/** ++ * scsih_resume_mpt2sas - power management resume main entry point ++ * @pdev: PCI device struct ++ * ++ * Returns 0 success, anything else error. ++ */ ++int ++scsih_resume_mpt2sas(struct pci_dev *pdev) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ pci_power_t device_state = pdev->current_state; ++ int r; ++ ++ pr_info(MPT3SAS_FMT ++ "pdev=0x%p, slot=%s, previous operating state [D%d]\n", ++ ioc->name, pdev, pci_name(pdev), device_state); ++ ++ pci_set_power_state(pdev, PCI_D0); ++ pci_enable_wake(pdev, PCI_D0, 0); ++ pci_restore_state(pdev); ++ ioc->pdev = pdev; ++ r = mpt2sas_base_map_resources(ioc); ++ if (r) ++ return r; ++ ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, SOFT_RESET); ++ scsi_unblock_requests(shost); ++ mpt2sas_base_start_watchdog(ioc); ++ return 0; ++} ++#endif /* CONFIG_PM */ ++ ++/** ++ * scsih_pci_error_detected_mpt2sas - Called when a PCI error is detected. ++ * @pdev: PCI device struct ++ * @state: PCI channel state ++ * ++ * Description: Called when a PCI error is detected. ++ * ++ * Return value: ++ * PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT ++ */ ++pci_ers_result_t ++scsih_pci_error_detected_mpt2sas(struct pci_dev *pdev, pci_channel_state_t state) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ pr_info(MPT3SAS_FMT "PCI error: detected callback, state(%d)!!\n", ++ ioc->name, state); ++ ++ switch (state) { ++ case pci_channel_io_normal: ++ return PCI_ERS_RESULT_CAN_RECOVER; ++ case pci_channel_io_frozen: ++ /* Fatal error, prepare for slot reset */ ++ ioc->pci_error_recovery = 1; ++ scsi_block_requests(ioc->shost); ++ mpt2sas_base_stop_watchdog(ioc); ++ mpt2sas_base_free_resources(ioc); ++ return PCI_ERS_RESULT_NEED_RESET; ++ case pci_channel_io_perm_failure: ++ /* Permanent error, prepare for device removal */ ++ ioc->pci_error_recovery = 1; ++ mpt2sas_base_stop_watchdog(ioc); ++ _scsih_flush_running_cmds(ioc); ++ return PCI_ERS_RESULT_DISCONNECT; ++ } ++ return PCI_ERS_RESULT_NEED_RESET; ++} ++ ++/** ++ * scsih_pci_slot_reset_mpt2sas - Called when PCI slot has been reset. ++ * @pdev: PCI device struct ++ * ++ * Description: This routine is called by the pci error recovery ++ * code after the PCI slot has been reset, just before we ++ * should resume normal operations. ++ */ ++pci_ers_result_t ++scsih_pci_slot_reset_mpt2sas(struct pci_dev *pdev) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ int rc; ++ ++ pr_info(MPT3SAS_FMT "PCI error: slot reset callback!!\n", ++ ioc->name); ++ ++ ioc->pci_error_recovery = 0; ++ ioc->pdev = pdev; ++ pci_restore_state(pdev); ++ rc = mpt2sas_base_map_resources(ioc); ++ if (rc) ++ return PCI_ERS_RESULT_DISCONNECT; ++ ++ rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ ++ pr_warn(MPT3SAS_FMT "hard reset: %s\n", ioc->name, ++ (rc == 0) ? "success" : "failed"); ++ ++ if (!rc) ++ return PCI_ERS_RESULT_RECOVERED; ++ else ++ return PCI_ERS_RESULT_DISCONNECT; ++} ++ ++/** ++ * scsih_pci_resume_mpt2sas() - resume normal ops after PCI reset ++ * @pdev: pointer to PCI device ++ * ++ * Called when the error recovery driver tells us that its ++ * OK to resume normal operation. Use completion to allow ++ * halted scsi ops to resume. ++ */ ++void ++scsih_pci_resume_mpt2sas(struct pci_dev *pdev) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ pr_info(MPT3SAS_FMT "PCI error: resume callback!!\n", ioc->name); ++ ++ pci_cleanup_aer_uncorrect_error_status(pdev); ++ mpt2sas_base_start_watchdog(ioc); ++ scsi_unblock_requests(ioc->shost); ++} ++ ++/** ++ * scsih_pci_mmio_enabled_mpt2sas - Enable MMIO and dump debug registers ++ * @pdev: pointer to PCI device ++ */ ++pci_ers_result_t ++scsih_pci_mmio_enabled_mpt2sas(struct pci_dev *pdev) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ pr_info(MPT3SAS_FMT "PCI error: mmio enabled callback!!\n", ++ ioc->name); ++ ++ /* TODO - dump whatever for debugging purposes */ ++ ++ /* This called only if scsih_pci_error_detected_mpt2sas returns ++ * PCI_ERS_RESULT_CAN_RECOVER. Read/write to the device still ++ * works, no need to reset slot. ++ */ ++ return PCI_ERS_RESULT_RECOVERED; ++} ++ ++/* ++ * The pci device ids are defined in mpi/mpi2_cnfg.h. ++ */ ++static const struct pci_device_id mpt2sas_pci_table[] = { ++#ifdef MPT2SAS_SCSI ++ /* Spitfire ~ 2004 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2004, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Falcon ~ 2008 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2008, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Liberator ~ 2108 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_1, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_2, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_3, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Meteor ~ 2116 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_1, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_2, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Thunderbolt ~ 2208 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_1, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_2, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_3, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_4, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_5, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Mustang ~ 2308 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_1, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* SSS6200 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200, ++ PCI_ANY_ID, PCI_ANY_ID }, ++#else ++ /* Fury ~ 3004 and 3008 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3004, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3008, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Invader ~ 3108 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_1, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_2, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_5, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_6, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Cutlass ~ 3216 and 3224 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3216, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3224, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Intruder ~ 3316 and 3324 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_1, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_2, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_3, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_4, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3324_1, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3324_2, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3324_3, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3324_4, ++ PCI_ANY_ID, PCI_ANY_ID }, ++#endif /* MPT2SAS_SCSI */ ++ {0} /* Terminating entry */ ++}; ++MODULE_DEVICE_TABLE(pci, mpt2sas_pci_table); ++ ++static struct pci_error_handlers _mpt2sas_err_handler = { ++ .error_detected = scsih_pci_error_detected_mpt2sas, ++ .mmio_enabled = scsih_pci_mmio_enabled_mpt2sas, ++ .slot_reset = scsih_pci_slot_reset_mpt2sas, ++ .resume = scsih_pci_resume_mpt2sas, ++}; ++ ++static struct pci_driver mpt3sas_driver = { ++#ifdef MPT2SAS_SCSI ++ .name = MPT2SAS_DRIVER_NAME, ++#else ++ .name = MPT3SAS_DRIVER_NAME, ++#endif /* MPT2SAS_SCSI */ ++ .id_table = mpt2sas_pci_table, ++ .probe = _scsih_probe_mpt2sas, ++ .remove = scsih_remove_mpt2sas, ++ .shutdown = scsih_shutdown_mpt2sas, ++ .err_handler = &_mpt2sas_err_handler, ++#ifdef CONFIG_PM ++ .suspend = scsih_suspend_mpt2sas, ++ .resume = scsih_resume_mpt2sas, ++#endif ++}; ++ ++/** ++ * scsih_init_mpt2sas - main entry point for this driver. ++ * ++ * Returns 0 success, anything else error. ++ */ ++int ++scsih_init_mpt2sas(void) ++{ ++ mpt2_ids = 0; ++ mpt3_ids = 0; ++ ++ mpt2sas_base_initialize_callback_handler(); ++ ++ /* queuecommand callback hander */ ++ scsi_io_cb_idx = mpt2sas_base_register_callback_handler(_scsih_io_done); ++ ++ /* task managment callback handler */ ++ tm_cb_idx = mpt2sas_base_register_callback_handler(_scsih_tm_done); ++ ++ /* base internal commands callback handler */ ++ base_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_base_done); ++ port_enable_cb_idx = mpt2sas_base_register_callback_handler( ++ mpt2sas_port_enable_done); ++ ++ /* transport internal commands callback handler */ ++ transport_cb_idx = mpt2sas_base_register_callback_handler( ++ mpt2sas_transport_done); ++ ++ /* scsih internal commands callback handler */ ++ scsih_cb_idx = mpt2sas_base_register_callback_handler(_scsih_done); ++ ++ /* configuration page API internal commands callback handler */ ++ config_cb_idx = mpt2sas_base_register_callback_handler( ++ mpt2sas_config_done); ++ ++ /* ctl module callback handler */ ++ ctl_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_ctl_done); ++ ++ tm_tr_cb_idx = mpt2sas_base_register_callback_handler( ++ _scsih_tm_tr_complete); ++ ++ tm_tr_volume_cb_idx = mpt2sas_base_register_callback_handler( ++ _scsih_tm_volume_tr_complete); ++ ++ tm_sas_control_cb_idx = mpt2sas_base_register_callback_handler( ++ _scsih_sas_control_complete); ++ ++ return 0; ++} ++ ++/** ++ * scsih_exit_mpt2sas - exit point for this driver (when it is a module). ++ * ++ * Returns 0 success, anything else error. ++ */ ++void ++scsih_exit_mpt2sas(void) ++{ ++ ++ mpt2sas_base_release_callback_handler(scsi_io_cb_idx); ++ mpt2sas_base_release_callback_handler(tm_cb_idx); ++ mpt2sas_base_release_callback_handler(base_cb_idx); ++ mpt2sas_base_release_callback_handler(port_enable_cb_idx); ++ mpt2sas_base_release_callback_handler(transport_cb_idx); ++ mpt2sas_base_release_callback_handler(scsih_cb_idx); ++ mpt2sas_base_release_callback_handler(config_cb_idx); ++ mpt2sas_base_release_callback_handler(ctl_cb_idx); ++ ++ mpt2sas_base_release_callback_handler(tm_tr_cb_idx); ++ mpt2sas_base_release_callback_handler(tm_tr_volume_cb_idx); ++ mpt2sas_base_release_callback_handler(tm_sas_control_cb_idx); ++ ++/* raid transport support */ ++#ifdef MPT2SAS_SCSI ++ raid_class_release(mpt2sas_raid_template_mpt2sas); ++#else ++ raid_class_release(mpt3sas_raid_template_mpt2sas); ++#endif /* MPT2SAS_SCSI */ ++ sas_release_transport(mpt2sas_transport_template); ++} ++ ++/** ++ * _mpt2sas_init - main entry point for this driver. ++ * ++ * Returns 0 success, anything else error. ++ */ ++static int __init ++_mpt2sas_init(void) ++{ ++ int error; ++ ++#ifdef MPT2SAS_SCSI ++ pr_info("%s version %s loaded\n", MPT2SAS_DRIVER_NAME, ++ MPT2SAS_DRIVER_VERSION); ++#else ++ pr_info("%s version %s loaded\n", MPT3SAS_DRIVER_NAME, ++ MPT3SAS_DRIVER_VERSION); ++#endif /* MPT2SAS_SCSI */ ++ ++ mpt2sas_transport_template = ++ sas_attach_transport(&mpt2sas_transport_functions); ++ if (!mpt2sas_transport_template) ++ return -ENODEV; ++ ++#ifdef MPT2SAS_SCSI ++ mpt2sas_raid_template_mpt2sas = raid_class_attach(&mpt2sas_raid_functions); ++ if (!mpt2sas_raid_template_mpt2sas) { ++ sas_release_transport(mpt2sas_transport_template); ++ return -ENODEV; ++ } ++#else ++ mpt3sas_raid_template_mpt2sas = raid_class_attach(&mpt3sas_raid_functions); ++ if (!mpt3sas_raid_template_mpt2sas) { ++ sas_release_transport(mpt2sas_transport_template); ++ return -ENODEV; ++ } ++#endif /* MPT2SAS_SCSI */ ++ ++ error = scsih_init_mpt2sas(); ++ if (error) { ++ scsih_exit_mpt2sas(); ++ return error; ++ } ++ ++ ++#ifdef MPT2SAS_SCSI ++ mpt2sas_ctl_init(1); ++#else ++ mpt2sas_ctl_init(2); ++#endif /* MPT2SAS_SCSI */ ++ ++ error = pci_register_driver(&mpt3sas_driver); ++ if (error) ++ scsih_exit_mpt2sas(); ++ ++ return error; ++} ++ ++/** ++ * _mpt2sas_exit - exit point for this driver (when it is a module). ++ * ++ */ ++static void __exit ++_mpt2sas_exit(void) ++{ ++ pr_info("mpt3sas version %s unloading\n", ++ MPT3SAS_DRIVER_VERSION); ++ ++ pci_unregister_driver(&mpt3sas_driver); ++ ++#ifdef MPT2SAS_SCSI ++ mpt2sas_ctl_exit(1); ++#else ++ mpt2sas_ctl_exit(2); ++#endif /* MPT2SAS_SCSI */ ++ ++ scsih_exit_mpt2sas(); ++} ++ ++module_init(_mpt2sas_init); ++module_exit(_mpt2sas_exit); +diff --git a/drivers/scsi/mpt2sas/mpt3sas_transport.c b/drivers/scsi/mpt2sas/mpt3sas_transport.c +new file mode 100644 +index 0000000..690afa5 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_transport.c +@@ -0,0 +1,2138 @@ ++/* ++ * SAS Transport Layer for MPT (Message Passing Technology) based controllers ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_transport.c ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mpt3sas_base.h" ++ ++/** ++ * _transport_sas_node_find_by_sas_address - sas node search ++ * @ioc: per adapter object ++ * @sas_address: sas address of expander or sas host ++ * Context: Calling function should acquire ioc->sas_node_lock. ++ * ++ * Search for either hba phys or expander device based on handle, then returns ++ * the sas_node object. ++ */ ++static struct _sas_node * ++_transport_sas_node_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address) ++{ ++ if (ioc->sas_hba.sas_address == sas_address) ++ return &ioc->sas_hba; ++ else ++ return mpt2sas_scsih_expander_find_by_sas_address(ioc, ++ sas_address); ++} ++ ++/** ++ * _transport_convert_phy_link_rate - ++ * @link_rate: link rate returned from mpt firmware ++ * ++ * Convert link_rate from mpi fusion into sas_transport form. ++ */ ++static enum sas_linkrate ++_transport_convert_phy_link_rate(u8 link_rate) ++{ ++ enum sas_linkrate rc; ++ ++ switch (link_rate) { ++ case MPI2_SAS_NEG_LINK_RATE_1_5: ++ rc = SAS_LINK_RATE_1_5_GBPS; ++ break; ++ case MPI2_SAS_NEG_LINK_RATE_3_0: ++ rc = SAS_LINK_RATE_3_0_GBPS; ++ break; ++ case MPI2_SAS_NEG_LINK_RATE_6_0: ++ rc = SAS_LINK_RATE_6_0_GBPS; ++ break; ++ case MPI25_SAS_NEG_LINK_RATE_12_0: ++ rc = SAS_LINK_RATE_12_0_GBPS; ++ break; ++ case MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED: ++ rc = SAS_PHY_DISABLED; ++ break; ++ case MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED: ++ rc = SAS_LINK_RATE_FAILED; ++ break; ++ case MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR: ++ rc = SAS_SATA_PORT_SELECTOR; ++ break; ++ case MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS: ++ rc = SAS_PHY_RESET_IN_PROGRESS; ++ break; ++ ++ default: ++ case MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE: ++ case MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE: ++ rc = SAS_LINK_RATE_UNKNOWN; ++ break; ++ } ++ return rc; ++} ++ ++/** ++ * _transport_set_identify - set identify for phys and end devices ++ * @ioc: per adapter object ++ * @handle: device handle ++ * @identify: sas identify info ++ * ++ * Populates sas identify info. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_transport_set_identify(struct MPT3SAS_ADAPTER *ioc, u16 handle, ++ struct sas_identify *identify) ++{ ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u32 device_info; ++ u32 ioc_status; ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery) { ++ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", ++ __func__, ioc->name); ++ return -EFAULT; ++ } ++ ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, ++ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -ENXIO; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT ++ "handle(0x%04x), ioc_status(0x%04x)\nfailure at %s:%d/%s()!\n", ++ ioc->name, handle, ioc_status, ++ __FILE__, __LINE__, __func__); ++ return -EIO; ++ } ++ ++ memset(identify, 0, sizeof(struct sas_identify)); ++ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); ++ ++ /* sas_address */ ++ identify->sas_address = le64_to_cpu(sas_device_pg0.SASAddress); ++ ++ /* phy number of the parent device this device is linked to */ ++ identify->phy_identifier = sas_device_pg0.PhyNum; ++ ++ /* device_type */ ++ switch (device_info & MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) { ++ case MPI2_SAS_DEVICE_INFO_NO_DEVICE: ++ identify->device_type = SAS_PHY_UNUSED; ++ break; ++ case MPI2_SAS_DEVICE_INFO_END_DEVICE: ++ identify->device_type = SAS_END_DEVICE; ++ break; ++ case MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER: ++ identify->device_type = SAS_EDGE_EXPANDER_DEVICE; ++ break; ++ case MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER: ++ identify->device_type = SAS_FANOUT_EXPANDER_DEVICE; ++ break; ++ } ++ ++ /* initiator_port_protocols */ ++ if (device_info & MPI2_SAS_DEVICE_INFO_SSP_INITIATOR) ++ identify->initiator_port_protocols |= SAS_PROTOCOL_SSP; ++ if (device_info & MPI2_SAS_DEVICE_INFO_STP_INITIATOR) ++ identify->initiator_port_protocols |= SAS_PROTOCOL_STP; ++ if (device_info & MPI2_SAS_DEVICE_INFO_SMP_INITIATOR) ++ identify->initiator_port_protocols |= SAS_PROTOCOL_SMP; ++ if (device_info & MPI2_SAS_DEVICE_INFO_SATA_HOST) ++ identify->initiator_port_protocols |= SAS_PROTOCOL_SATA; ++ ++ /* target_port_protocols */ ++ if (device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) ++ identify->target_port_protocols |= SAS_PROTOCOL_SSP; ++ if (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) ++ identify->target_port_protocols |= SAS_PROTOCOL_STP; ++ if (device_info & MPI2_SAS_DEVICE_INFO_SMP_TARGET) ++ identify->target_port_protocols |= SAS_PROTOCOL_SMP; ++ if (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE) ++ identify->target_port_protocols |= SAS_PROTOCOL_SATA; ++ ++ return 0; ++} ++ ++/** ++ * mpt2sas_transport_done - internal transport layer callback handler. ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * ++ * Callback handler when sending internal generated transport cmds. ++ * The callback index passed is `ioc->transport_cb_idx` ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_transport_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (ioc->transport_cmds.status == MPT3_CMD_NOT_USED) ++ return 1; ++ if (ioc->transport_cmds.smid != smid) ++ return 1; ++ ioc->transport_cmds.status |= MPT3_CMD_COMPLETE; ++ if (mpi_reply) { ++ memcpy(ioc->transport_cmds.reply, mpi_reply, ++ mpi_reply->MsgLength*4); ++ ioc->transport_cmds.status |= MPT3_CMD_REPLY_VALID; ++ } ++ ioc->transport_cmds.status &= ~MPT3_CMD_PENDING; ++ complete(&ioc->transport_cmds.done); ++ return 1; ++} ++ ++/* report manufacture request structure */ ++struct rep_manu_request { ++ u8 smp_frame_type; ++ u8 function; ++ u8 reserved; ++ u8 request_length; ++}; ++ ++/* report manufacture reply structure */ ++struct rep_manu_reply { ++ u8 smp_frame_type; /* 0x41 */ ++ u8 function; /* 0x01 */ ++ u8 function_result; ++ u8 response_length; ++ u16 expander_change_count; ++ u8 reserved0[2]; ++ u8 sas_format; ++ u8 reserved2[3]; ++ u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN]; ++ u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN]; ++ u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN]; ++ u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN]; ++ u16 component_id; ++ u8 component_revision_id; ++ u8 reserved3; ++ u8 vendor_specific[8]; ++}; ++ ++/** ++ * transport_expander_report_manufacture - obtain SMP report_manufacture ++ * @ioc: per adapter object ++ * @sas_address: expander sas address ++ * @edev: the sas_expander_device object ++ * ++ * Fills in the sas_expander_device object when SMP port is created. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address, struct sas_expander_device *edev) ++{ ++ Mpi2SmpPassthroughRequest_t *mpi_request; ++ Mpi2SmpPassthroughReply_t *mpi_reply; ++ struct rep_manu_reply *manufacture_reply; ++ struct rep_manu_request *manufacture_request; ++ int rc; ++ u16 smid; ++ u32 ioc_state; ++ unsigned long timeleft; ++ void *psge; ++ u8 issue_reset = 0; ++ void *data_out = NULL; ++ dma_addr_t data_out_dma; ++ dma_addr_t data_in_dma; ++ size_t data_in_sz; ++ size_t data_out_sz; ++ u16 wait_state_count; ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery) { ++ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", ++ __func__, ioc->name); ++ return -EFAULT; ++ } ++ ++ mutex_lock(&ioc->transport_cmds.mutex); ++ ++ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: transport_cmds in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ioc->transport_cmds.status = MPT3_CMD_PENDING; ++ ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, __func__, wait_state_count); ++ } ++ if (wait_state_count) ++ pr_info(MPT3SAS_FMT "%s: ioc is operational\n", ++ ioc->name, __func__); ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ rc = 0; ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->transport_cmds.smid = smid; ++ ++ data_out_sz = sizeof(struct rep_manu_request); ++ data_in_sz = sizeof(struct rep_manu_reply); ++ data_out = pci_alloc_consistent(ioc->pdev, data_out_sz + data_in_sz, ++ &data_out_dma); ++ ++ if (!data_out) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ rc = -ENOMEM; ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ ++ data_in_dma = data_out_dma + sizeof(struct rep_manu_request); ++ ++ manufacture_request = data_out; ++ manufacture_request->smp_frame_type = 0x40; ++ manufacture_request->function = 1; ++ manufacture_request->reserved = 0; ++ manufacture_request->request_length = 0; ++ ++ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; ++ mpi_request->PhysicalPort = 0xFF; ++ mpi_request->SASAddress = cpu_to_le64(sas_address); ++ mpi_request->RequestDataLength = cpu_to_le16(data_out_sz); ++ psge = &mpi_request->SGL; ++ ++ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma, ++ data_in_sz); ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "report_manufacture - send to sas_addr(0x%016llx)\n", ++ ioc->name, (unsigned long long)sas_address)); ++ init_completion(&ioc->transport_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, ++ 10*HZ); ++ ++ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SmpPassthroughRequest_t)/4); ++ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ goto issue_host_reset; ++ } ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "report_manufacture - complete\n", ioc->name)); ++ ++ if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) { ++ u8 *tmp; ++ ++ mpi_reply = ioc->transport_cmds.reply; ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "report_manufacture - reply data transfer size(%d)\n", ++ ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength))); ++ ++ if (le16_to_cpu(mpi_reply->ResponseDataLength) != ++ sizeof(struct rep_manu_reply)) ++ goto out; ++ ++ manufacture_reply = data_out + sizeof(struct rep_manu_request); ++ strncpy(edev->vendor_id, manufacture_reply->vendor_id, ++ SAS_EXPANDER_VENDOR_ID_LEN); ++ strncpy(edev->product_id, manufacture_reply->product_id, ++ SAS_EXPANDER_PRODUCT_ID_LEN); ++ strncpy(edev->product_rev, manufacture_reply->product_rev, ++ SAS_EXPANDER_PRODUCT_REV_LEN); ++ edev->level = manufacture_reply->sas_format & 1; ++ if (edev->level) { ++ strncpy(edev->component_vendor_id, ++ manufacture_reply->component_vendor_id, ++ SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN); ++ tmp = (u8 *)&manufacture_reply->component_id; ++ edev->component_id = tmp[0] << 8 | tmp[1]; ++ edev->component_revision_id = ++ manufacture_reply->component_revision_id; ++ } ++ } else ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "report_manufacture - no reply\n", ioc->name)); ++ ++ issue_host_reset: ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ out: ++ ioc->transport_cmds.status = MPT3_CMD_NOT_USED; ++ if (data_out) ++ pci_free_consistent(ioc->pdev, data_out_sz + data_in_sz, ++ data_out, data_out_dma); ++ ++ mutex_unlock(&ioc->transport_cmds.mutex); ++ return rc; ++} ++ ++ ++/** ++ * _transport_delete_port - helper function to removing a port ++ * @ioc: per adapter object ++ * @mpt2sas_port: mpt3sas per port object ++ * ++ * Returns nothing. ++ */ ++static void ++_transport_delete_port(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_port *mpt2sas_port) ++{ ++ u64 sas_address = mpt2sas_port->remote_identify.sas_address; ++ enum sas_device_type device_type = ++ mpt2sas_port->remote_identify.device_type; ++ ++ dev_printk(KERN_INFO, &mpt2sas_port->port->dev, ++ "remove: sas_addr(0x%016llx)\n", ++ (unsigned long long) sas_address); ++ ++ ioc->logging_level |= MPT_DEBUG_TRANSPORT; ++ if (device_type == SAS_END_DEVICE) ++ mpt2sas_device_remove_by_sas_address(ioc, sas_address); ++ else if (device_type == SAS_EDGE_EXPANDER_DEVICE || ++ device_type == SAS_FANOUT_EXPANDER_DEVICE) ++ mpt2sas_expander_remove(ioc, sas_address); ++ ioc->logging_level &= ~MPT_DEBUG_TRANSPORT; ++} ++ ++/** ++ * _transport_delete_phy - helper function to removing single phy from port ++ * @ioc: per adapter object ++ * @mpt2sas_port: mpt3sas per port object ++ * @mpt2sas_phy: mpt3sas per phy object ++ * ++ * Returns nothing. ++ */ ++static void ++_transport_delete_phy(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_port *mpt2sas_port, struct _sas_phy *mpt2sas_phy) ++{ ++ u64 sas_address = mpt2sas_port->remote_identify.sas_address; ++ ++ dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev, ++ "remove: sas_addr(0x%016llx), phy(%d)\n", ++ (unsigned long long) sas_address, mpt2sas_phy->phy_id); ++ ++ list_del(&mpt2sas_phy->port_siblings); ++ mpt2sas_port->num_phys--; ++ sas_port_delete_phy(mpt2sas_port->port, mpt2sas_phy->phy); ++ mpt2sas_phy->phy_belongs_to_port = 0; ++} ++ ++/** ++ * _transport_add_phy - helper function to adding single phy to port ++ * @ioc: per adapter object ++ * @mpt2sas_port: mpt3sas per port object ++ * @mpt2sas_phy: mpt3sas per phy object ++ * ++ * Returns nothing. ++ */ ++static void ++_transport_add_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_port *mpt2sas_port, ++ struct _sas_phy *mpt2sas_phy) ++{ ++ u64 sas_address = mpt2sas_port->remote_identify.sas_address; ++ ++ dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev, ++ "add: sas_addr(0x%016llx), phy(%d)\n", (unsigned long long) ++ sas_address, mpt2sas_phy->phy_id); ++ ++ list_add_tail(&mpt2sas_phy->port_siblings, &mpt2sas_port->phy_list); ++ mpt2sas_port->num_phys++; ++ sas_port_add_phy(mpt2sas_port->port, mpt2sas_phy->phy); ++ mpt2sas_phy->phy_belongs_to_port = 1; ++} ++ ++/** ++ * _transport_add_phy_to_an_existing_port - adding new phy to existing port ++ * @ioc: per adapter object ++ * @sas_node: sas node object (either expander or sas host) ++ * @mpt2sas_phy: mpt3sas per phy object ++ * @sas_address: sas address of device/expander were phy needs to be added to ++ * ++ * Returns nothing. ++ */ ++static void ++_transport_add_phy_to_an_existing_port(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_node *sas_node, struct _sas_phy *mpt2sas_phy, ++ u64 sas_address) ++{ ++ struct _sas_port *mpt2sas_port; ++ struct _sas_phy *phy_srch; ++ ++ if (mpt2sas_phy->phy_belongs_to_port == 1) ++ return; ++ ++ list_for_each_entry(mpt2sas_port, &sas_node->sas_port_list, ++ port_list) { ++ if (mpt2sas_port->remote_identify.sas_address != ++ sas_address) ++ continue; ++ list_for_each_entry(phy_srch, &mpt2sas_port->phy_list, ++ port_siblings) { ++ if (phy_srch == mpt2sas_phy) ++ return; ++ } ++ _transport_add_phy(ioc, mpt2sas_port, mpt2sas_phy); ++ return; ++ } ++ ++} ++ ++/** ++ * _transport_del_phy_from_an_existing_port - delete phy from existing port ++ * @ioc: per adapter object ++ * @sas_node: sas node object (either expander or sas host) ++ * @mpt2sas_phy: mpt3sas per phy object ++ * ++ * Returns nothing. ++ */ ++static void ++_transport_del_phy_from_an_existing_port(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_node *sas_node, struct _sas_phy *mpt2sas_phy) ++{ ++ struct _sas_port *mpt2sas_port, *next; ++ struct _sas_phy *phy_srch; ++ ++ if (mpt2sas_phy->phy_belongs_to_port == 0) ++ return; ++ ++ list_for_each_entry_safe(mpt2sas_port, next, &sas_node->sas_port_list, ++ port_list) { ++ list_for_each_entry(phy_srch, &mpt2sas_port->phy_list, ++ port_siblings) { ++ if (phy_srch != mpt2sas_phy) ++ continue; ++ ++ if (mpt2sas_port->num_phys == 1) ++ _transport_delete_port(ioc, mpt2sas_port); ++ else ++ _transport_delete_phy(ioc, mpt2sas_port, ++ mpt2sas_phy); ++ return; ++ } ++ } ++} ++ ++/** ++ * _transport_sanity_check - sanity check when adding a new port ++ * @ioc: per adapter object ++ * @sas_node: sas node object (either expander or sas host) ++ * @sas_address: sas address of device being added ++ * ++ * See the explanation above from _transport_delete_duplicate_port ++ */ ++static void ++_transport_sanity_check(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_node, ++ u64 sas_address) ++{ ++ int i; ++ ++ for (i = 0; i < sas_node->num_phys; i++) { ++ if (sas_node->phy[i].remote_identify.sas_address != sas_address) ++ continue; ++ if (sas_node->phy[i].phy_belongs_to_port == 1) ++ _transport_del_phy_from_an_existing_port(ioc, sas_node, ++ &sas_node->phy[i]); ++ } ++} ++ ++/** ++ * mpt2sas_transport_port_add - insert port to the list ++ * @ioc: per adapter object ++ * @handle: handle of attached device ++ * @sas_address: sas address of parent expander or sas host ++ * Context: This function will acquire ioc->sas_node_lock. ++ * ++ * Adding new port object to the sas_node->sas_port_list. ++ * ++ * Returns mpt2sas_port. ++ */ ++struct _sas_port * ++mpt2sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, ++ u64 sas_address) ++{ ++ struct _sas_phy *mpt2sas_phy, *next; ++ struct _sas_port *mpt2sas_port; ++ unsigned long flags; ++ struct _sas_node *sas_node; ++ struct sas_rphy *rphy; ++ struct _sas_device *sas_device = NULL; ++ int i; ++ struct sas_port *port; ++ ++ mpt2sas_port = kzalloc(sizeof(struct _sas_port), ++ GFP_KERNEL); ++ if (!mpt2sas_port) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return NULL; ++ } ++ ++ INIT_LIST_HEAD(&mpt2sas_port->port_list); ++ INIT_LIST_HEAD(&mpt2sas_port->phy_list); ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ if (!sas_node) { ++ pr_err(MPT3SAS_FMT ++ "%s: Could not find parent sas_address(0x%016llx)!\n", ++ ioc->name, __func__, (unsigned long long)sas_address); ++ goto out_fail; ++ } ++ ++ if ((_transport_set_identify(ioc, handle, ++ &mpt2sas_port->remote_identify))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out_fail; ++ } ++ ++ if (mpt2sas_port->remote_identify.device_type == SAS_PHY_UNUSED) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out_fail; ++ } ++ ++ _transport_sanity_check(ioc, sas_node, ++ mpt2sas_port->remote_identify.sas_address); ++ ++ for (i = 0; i < sas_node->num_phys; i++) { ++ if (sas_node->phy[i].remote_identify.sas_address != ++ mpt2sas_port->remote_identify.sas_address) ++ continue; ++ list_add_tail(&sas_node->phy[i].port_siblings, ++ &mpt2sas_port->phy_list); ++ mpt2sas_port->num_phys++; ++ } ++ ++ if (!mpt2sas_port->num_phys) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out_fail; ++ } ++ ++ port = sas_port_alloc_num(sas_node->parent_dev); ++ if ((sas_port_add(port))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out_fail; ++ } ++ ++ list_for_each_entry(mpt2sas_phy, &mpt2sas_port->phy_list, ++ port_siblings) { ++ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) ++ dev_printk(KERN_INFO, &port->dev, ++ "add: handle(0x%04x), sas_addr(0x%016llx), phy(%d)\n", ++ handle, (unsigned long long) ++ mpt2sas_port->remote_identify.sas_address, ++ mpt2sas_phy->phy_id); ++ sas_port_add_phy(port, mpt2sas_phy->phy); ++ mpt2sas_phy->phy_belongs_to_port = 1; ++ } ++ ++ mpt2sas_port->port = port; ++ if (mpt2sas_port->remote_identify.device_type == SAS_END_DEVICE) ++ rphy = sas_end_device_alloc(port); ++ else ++ rphy = sas_expander_alloc(port, ++ mpt2sas_port->remote_identify.device_type); ++ ++ rphy->identify = mpt2sas_port->remote_identify; ++ ++ if (mpt2sas_port->remote_identify.device_type == SAS_END_DEVICE) { ++ sas_device = mpt2sas_get_sdev_by_addr(ioc, ++ mpt2sas_port->remote_identify.sas_address); ++ if (!sas_device) { ++ dfailprintk(ioc, printk(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__)); ++ goto out_fail; ++ } ++ sas_device->pend_sas_rphy_add = 1; ++ } ++ ++ if ((sas_rphy_add(rphy))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ } ++ ++ if (mpt2sas_port->remote_identify.device_type == SAS_END_DEVICE) { ++ sas_device->pend_sas_rphy_add = 0; ++ sas_device_put(sas_device); ++ } ++ ++ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) ++ dev_printk(KERN_INFO, &rphy->dev, ++ "add: handle(0x%04x), sas_addr(0x%016llx)\n", ++ handle, (unsigned long long) ++ mpt2sas_port->remote_identify.sas_address); ++ mpt2sas_port->rphy = rphy; ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ list_add_tail(&mpt2sas_port->port_list, &sas_node->sas_port_list); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ /* fill in report manufacture */ ++ if (mpt2sas_port->remote_identify.device_type == ++ MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER || ++ mpt2sas_port->remote_identify.device_type == ++ MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER) ++ _transport_expander_report_manufacture(ioc, ++ mpt2sas_port->remote_identify.sas_address, ++ rphy_to_expander_device(rphy)); ++ return mpt2sas_port; ++ ++ out_fail: ++ list_for_each_entry_safe(mpt2sas_phy, next, &mpt2sas_port->phy_list, ++ port_siblings) ++ list_del(&mpt2sas_phy->port_siblings); ++ kfree(mpt2sas_port); ++ return NULL; ++} ++ ++/** ++ * mpt2sas_transport_port_remove - remove port from the list ++ * @ioc: per adapter object ++ * @sas_address: sas address of attached device ++ * @sas_address_parent: sas address of parent expander or sas host ++ * Context: This function will acquire ioc->sas_node_lock. ++ * ++ * Removing object and freeing associated memory from the ++ * ioc->sas_port_list. ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, ++ u64 sas_address_parent) ++{ ++ int i; ++ unsigned long flags; ++ struct _sas_port *mpt2sas_port, *next; ++ struct _sas_node *sas_node; ++ u8 found = 0; ++ struct _sas_phy *mpt2sas_phy, *next_phy; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_node = _transport_sas_node_find_by_sas_address(ioc, ++ sas_address_parent); ++ if (!sas_node) { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return; ++ } ++ list_for_each_entry_safe(mpt2sas_port, next, &sas_node->sas_port_list, ++ port_list) { ++ if (mpt2sas_port->remote_identify.sas_address != sas_address) ++ continue; ++ found = 1; ++ list_del(&mpt2sas_port->port_list); ++ goto out; ++ } ++ out: ++ if (!found) { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return; ++ } ++ ++ for (i = 0; i < sas_node->num_phys; i++) { ++ if (sas_node->phy[i].remote_identify.sas_address == sas_address) ++ memset(&sas_node->phy[i].remote_identify, 0 , ++ sizeof(struct sas_identify)); ++ } ++ ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ list_for_each_entry_safe(mpt2sas_phy, next_phy, ++ &mpt2sas_port->phy_list, port_siblings) { ++ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) ++ dev_printk(KERN_INFO, &mpt2sas_port->port->dev, ++ "remove: sas_addr(0x%016llx), phy(%d)\n", ++ (unsigned long long) ++ mpt2sas_port->remote_identify.sas_address, ++ mpt2sas_phy->phy_id); ++ mpt2sas_phy->phy_belongs_to_port = 0; ++ sas_port_delete_phy(mpt2sas_port->port, mpt2sas_phy->phy); ++ list_del(&mpt2sas_phy->port_siblings); ++ } ++ sas_port_delete(mpt2sas_port->port); ++ kfree(mpt2sas_port); ++} ++ ++/** ++ * mpt2sas_transport_add_host_phy - report sas_host phy to transport ++ * @ioc: per adapter object ++ * @mpt2sas_phy: mpt3sas per phy object ++ * @phy_pg0: sas phy page 0 ++ * @parent_dev: parent device class object ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_transport_add_host_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy ++ *mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev) ++{ ++ struct sas_phy *phy; ++ int phy_index = mpt2sas_phy->phy_id; ++ ++ ++ INIT_LIST_HEAD(&mpt2sas_phy->port_siblings); ++ phy = sas_phy_alloc(parent_dev, phy_index); ++ if (!phy) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ if ((_transport_set_identify(ioc, mpt2sas_phy->handle, ++ &mpt2sas_phy->identify))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ sas_phy_free(phy); ++ return -1; ++ } ++ phy->identify = mpt2sas_phy->identify; ++ mpt2sas_phy->attached_handle = le16_to_cpu(phy_pg0.AttachedDevHandle); ++ if (mpt2sas_phy->attached_handle) ++ _transport_set_identify(ioc, mpt2sas_phy->attached_handle, ++ &mpt2sas_phy->remote_identify); ++ phy->identify.phy_identifier = mpt2sas_phy->phy_id; ++ phy->negotiated_linkrate = _transport_convert_phy_link_rate( ++ phy_pg0.NegotiatedLinkRate & MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL); ++ phy->minimum_linkrate_hw = _transport_convert_phy_link_rate( ++ phy_pg0.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK); ++ phy->maximum_linkrate_hw = _transport_convert_phy_link_rate( ++ phy_pg0.HwLinkRate >> 4); ++ phy->minimum_linkrate = _transport_convert_phy_link_rate( ++ phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); ++ phy->maximum_linkrate = _transport_convert_phy_link_rate( ++ phy_pg0.ProgrammedLinkRate >> 4); ++ ++ if ((sas_phy_add(phy))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ sas_phy_free(phy); ++ return -1; ++ } ++ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) ++ dev_printk(KERN_INFO, &phy->dev, ++ "add: handle(0x%04x), sas_addr(0x%016llx)\n" ++ "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", ++ mpt2sas_phy->handle, (unsigned long long) ++ mpt2sas_phy->identify.sas_address, ++ mpt2sas_phy->attached_handle, ++ (unsigned long long) ++ mpt2sas_phy->remote_identify.sas_address); ++ mpt2sas_phy->phy = phy; ++ return 0; ++} ++ ++ ++/** ++ * mpt2sas_transport_add_expander_phy - report expander phy to transport ++ * @ioc: per adapter object ++ * @mpt2sas_phy: mpt3sas per phy object ++ * @expander_pg1: expander page 1 ++ * @parent_dev: parent device class object ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_transport_add_expander_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy ++ *mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, ++ struct device *parent_dev) ++{ ++ struct sas_phy *phy; ++ int phy_index = mpt2sas_phy->phy_id; ++ ++ INIT_LIST_HEAD(&mpt2sas_phy->port_siblings); ++ phy = sas_phy_alloc(parent_dev, phy_index); ++ if (!phy) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ if ((_transport_set_identify(ioc, mpt2sas_phy->handle, ++ &mpt2sas_phy->identify))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ sas_phy_free(phy); ++ return -1; ++ } ++ phy->identify = mpt2sas_phy->identify; ++ mpt2sas_phy->attached_handle = ++ le16_to_cpu(expander_pg1.AttachedDevHandle); ++ if (mpt2sas_phy->attached_handle) ++ _transport_set_identify(ioc, mpt2sas_phy->attached_handle, ++ &mpt2sas_phy->remote_identify); ++ phy->identify.phy_identifier = mpt2sas_phy->phy_id; ++ phy->negotiated_linkrate = _transport_convert_phy_link_rate( ++ expander_pg1.NegotiatedLinkRate & ++ MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL); ++ phy->minimum_linkrate_hw = _transport_convert_phy_link_rate( ++ expander_pg1.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK); ++ phy->maximum_linkrate_hw = _transport_convert_phy_link_rate( ++ expander_pg1.HwLinkRate >> 4); ++ phy->minimum_linkrate = _transport_convert_phy_link_rate( ++ expander_pg1.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); ++ phy->maximum_linkrate = _transport_convert_phy_link_rate( ++ expander_pg1.ProgrammedLinkRate >> 4); ++ ++ if ((sas_phy_add(phy))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ sas_phy_free(phy); ++ return -1; ++ } ++ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) ++ dev_printk(KERN_INFO, &phy->dev, ++ "add: handle(0x%04x), sas_addr(0x%016llx)\n" ++ "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", ++ mpt2sas_phy->handle, (unsigned long long) ++ mpt2sas_phy->identify.sas_address, ++ mpt2sas_phy->attached_handle, ++ (unsigned long long) ++ mpt2sas_phy->remote_identify.sas_address); ++ mpt2sas_phy->phy = phy; ++ return 0; ++} ++ ++/** ++ * mpt2sas_transport_update_links - refreshing phy link changes ++ * @ioc: per adapter object ++ * @sas_address: sas address of parent expander or sas host ++ * @handle: attached device handle ++ * @phy_numberv: phy number ++ * @link_rate: new link rate ++ * ++ * Returns nothing. ++ */ ++void ++mpt2sas_transport_update_links(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address, u16 handle, u8 phy_number, u8 link_rate) ++{ ++ unsigned long flags; ++ struct _sas_node *sas_node; ++ struct _sas_phy *mpt2sas_phy; ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery) ++ return; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address); ++ if (!sas_node) { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return; ++ } ++ ++ mpt2sas_phy = &sas_node->phy[phy_number]; ++ mpt2sas_phy->attached_handle = handle; ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) { ++ _transport_set_identify(ioc, handle, ++ &mpt2sas_phy->remote_identify); ++ _transport_add_phy_to_an_existing_port(ioc, sas_node, ++ mpt2sas_phy, mpt2sas_phy->remote_identify.sas_address); ++ } else ++ memset(&mpt2sas_phy->remote_identify, 0 , sizeof(struct ++ sas_identify)); ++ ++ if (mpt2sas_phy->phy) ++ mpt2sas_phy->phy->negotiated_linkrate = ++ _transport_convert_phy_link_rate(link_rate); ++ ++ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) ++ dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev, ++ "refresh: parent sas_addr(0x%016llx),\n" ++ "\tlink_rate(0x%02x), phy(%d)\n" ++ "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", ++ (unsigned long long)sas_address, ++ link_rate, phy_number, handle, (unsigned long long) ++ mpt2sas_phy->remote_identify.sas_address); ++} ++ ++static inline void * ++phy_to_ioc(struct sas_phy *phy) ++{ ++ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); ++ return shost_priv(shost); ++} ++ ++static inline void * ++rphy_to_ioc(struct sas_rphy *rphy) ++{ ++ struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent); ++ return shost_priv(shost); ++} ++ ++/* report phy error log structure */ ++struct phy_error_log_request { ++ u8 smp_frame_type; /* 0x40 */ ++ u8 function; /* 0x11 */ ++ u8 allocated_response_length; ++ u8 request_length; /* 02 */ ++ u8 reserved_1[5]; ++ u8 phy_identifier; ++ u8 reserved_2[2]; ++}; ++ ++/* report phy error log reply structure */ ++struct phy_error_log_reply { ++ u8 smp_frame_type; /* 0x41 */ ++ u8 function; /* 0x11 */ ++ u8 function_result; ++ u8 response_length; ++ __be16 expander_change_count; ++ u8 reserved_1[3]; ++ u8 phy_identifier; ++ u8 reserved_2[2]; ++ __be32 invalid_dword; ++ __be32 running_disparity_error; ++ __be32 loss_of_dword_sync; ++ __be32 phy_reset_problem; ++}; ++ ++/** ++ * _transport_get_expander_phy_error_log - return expander counters ++ * @ioc: per adapter object ++ * @phy: The sas phy object ++ * ++ * Returns 0 for success, non-zero for failure. ++ * ++ */ ++static int ++_transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc, ++ struct sas_phy *phy) ++{ ++ Mpi2SmpPassthroughRequest_t *mpi_request; ++ Mpi2SmpPassthroughReply_t *mpi_reply; ++ struct phy_error_log_request *phy_error_log_request; ++ struct phy_error_log_reply *phy_error_log_reply; ++ int rc; ++ u16 smid; ++ u32 ioc_state; ++ unsigned long timeleft; ++ void *psge; ++ u8 issue_reset = 0; ++ void *data_out = NULL; ++ dma_addr_t data_out_dma; ++ u32 sz; ++ u16 wait_state_count; ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery) { ++ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", ++ __func__, ioc->name); ++ return -EFAULT; ++ } ++ ++ mutex_lock(&ioc->transport_cmds.mutex); ++ ++ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: transport_cmds in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ioc->transport_cmds.status = MPT3_CMD_PENDING; ++ ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, __func__, wait_state_count); ++ } ++ if (wait_state_count) ++ pr_info(MPT3SAS_FMT "%s: ioc is operational\n", ++ ioc->name, __func__); ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->transport_cmds.smid = smid; ++ ++ sz = sizeof(struct phy_error_log_request) + ++ sizeof(struct phy_error_log_reply); ++ data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma); ++ if (!data_out) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ rc = -ENOMEM; ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ ++ rc = -EINVAL; ++ memset(data_out, 0, sz); ++ phy_error_log_request = data_out; ++ phy_error_log_request->smp_frame_type = 0x40; ++ phy_error_log_request->function = 0x11; ++ phy_error_log_request->request_length = 2; ++ phy_error_log_request->allocated_response_length = 0; ++ phy_error_log_request->phy_identifier = phy->number; ++ ++ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; ++ mpi_request->PhysicalPort = 0xFF; ++ mpi_request->VF_ID = 0; /* TODO */ ++ mpi_request->VP_ID = 0; ++ mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address); ++ mpi_request->RequestDataLength = ++ cpu_to_le16(sizeof(struct phy_error_log_request)); ++ psge = &mpi_request->SGL; ++ ++ ioc->build_sg(ioc, psge, data_out_dma, ++ sizeof(struct phy_error_log_request), ++ data_out_dma + sizeof(struct phy_error_log_request), ++ sizeof(struct phy_error_log_reply)); ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_error_log - send to sas_addr(0x%016llx), phy(%d)\n", ++ ioc->name, (unsigned long long)phy->identify.sas_address, ++ phy->number)); ++ init_completion(&ioc->transport_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, ++ 10*HZ); ++ ++ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SmpPassthroughRequest_t)/4); ++ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ goto issue_host_reset; ++ } ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_error_log - complete\n", ioc->name)); ++ ++ if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) { ++ ++ mpi_reply = ioc->transport_cmds.reply; ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_error_log - reply data transfer size(%d)\n", ++ ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength))); ++ ++ if (le16_to_cpu(mpi_reply->ResponseDataLength) != ++ sizeof(struct phy_error_log_reply)) ++ goto out; ++ ++ phy_error_log_reply = data_out + ++ sizeof(struct phy_error_log_request); ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_error_log - function_result(%d)\n", ++ ioc->name, phy_error_log_reply->function_result)); ++ ++ phy->invalid_dword_count = ++ be32_to_cpu(phy_error_log_reply->invalid_dword); ++ phy->running_disparity_error_count = ++ be32_to_cpu(phy_error_log_reply->running_disparity_error); ++ phy->loss_of_dword_sync_count = ++ be32_to_cpu(phy_error_log_reply->loss_of_dword_sync); ++ phy->phy_reset_problem_count = ++ be32_to_cpu(phy_error_log_reply->phy_reset_problem); ++ rc = 0; ++ } else ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_error_log - no reply\n", ioc->name)); ++ ++ issue_host_reset: ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ out: ++ ioc->transport_cmds.status = MPT3_CMD_NOT_USED; ++ if (data_out) ++ pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma); ++ ++ mutex_unlock(&ioc->transport_cmds.mutex); ++ return rc; ++} ++ ++/** ++ * _transport_get_linkerrors - return phy counters for both hba and expanders ++ * @phy: The sas phy object ++ * ++ * Returns 0 for success, non-zero for failure. ++ * ++ */ ++static int ++_transport_get_linkerrors(struct sas_phy *phy) ++{ ++ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy); ++ unsigned long flags; ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasPhyPage1_t phy_pg1; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ if (_transport_sas_node_find_by_sas_address(ioc, ++ phy->identify.sas_address) == NULL) { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return -EINVAL; ++ } ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ if (phy->identify.sas_address != ioc->sas_hba.sas_address) ++ return _transport_get_expander_phy_error_log(ioc, phy); ++ ++ /* get hba phy error logs */ ++ if ((mpt2sas_config_get_phy_pg1(ioc, &mpi_reply, &phy_pg1, ++ phy->number))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -ENXIO; ++ } ++ ++ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) ++ pr_info(MPT3SAS_FMT ++ "phy(%d), ioc_status (0x%04x), loginfo(0x%08x)\n", ++ ioc->name, phy->number, ++ le16_to_cpu(mpi_reply.IOCStatus), ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ ++ phy->invalid_dword_count = le32_to_cpu(phy_pg1.InvalidDwordCount); ++ phy->running_disparity_error_count = ++ le32_to_cpu(phy_pg1.RunningDisparityErrorCount); ++ phy->loss_of_dword_sync_count = ++ le32_to_cpu(phy_pg1.LossDwordSynchCount); ++ phy->phy_reset_problem_count = ++ le32_to_cpu(phy_pg1.PhyResetProblemCount); ++ return 0; ++} ++ ++/** ++ * _transport_get_enclosure_identifier - ++ * @phy: The sas phy object ++ * ++ * Obtain the enclosure logical id for an expander. ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier) ++{ ++ struct MPT3SAS_ADAPTER *ioc = rphy_to_ioc(rphy); ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ int rc; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ rphy->identify.sas_address); ++ if (sas_device) { ++ *identifier = sas_device->enclosure_logical_id; ++ rc = 0; ++ sas_device_put(sas_device); ++ } else { ++ *identifier = 0; ++ rc = -ENXIO; ++ } ++ ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ return rc; ++} ++ ++/** ++ * _transport_get_bay_identifier - ++ * @phy: The sas phy object ++ * ++ * Returns the slot id for a device that resides inside an enclosure. ++ */ ++static int ++_transport_get_bay_identifier(struct sas_rphy *rphy) ++{ ++ struct MPT3SAS_ADAPTER *ioc = rphy_to_ioc(rphy); ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ int rc; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ rphy->identify.sas_address); ++ if (sas_device) { ++ rc = sas_device->slot; ++ sas_device_put(sas_device); ++ } else { ++ rc = -ENXIO; ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ return rc; ++} ++ ++/* phy control request structure */ ++struct phy_control_request { ++ u8 smp_frame_type; /* 0x40 */ ++ u8 function; /* 0x91 */ ++ u8 allocated_response_length; ++ u8 request_length; /* 0x09 */ ++ u16 expander_change_count; ++ u8 reserved_1[3]; ++ u8 phy_identifier; ++ u8 phy_operation; ++ u8 reserved_2[13]; ++ u64 attached_device_name; ++ u8 programmed_min_physical_link_rate; ++ u8 programmed_max_physical_link_rate; ++ u8 reserved_3[6]; ++}; ++ ++/* phy control reply structure */ ++struct phy_control_reply { ++ u8 smp_frame_type; /* 0x41 */ ++ u8 function; /* 0x11 */ ++ u8 function_result; ++ u8 response_length; ++}; ++ ++#define SMP_PHY_CONTROL_LINK_RESET (0x01) ++#define SMP_PHY_CONTROL_HARD_RESET (0x02) ++#define SMP_PHY_CONTROL_DISABLE (0x03) ++ ++/** ++ * _transport_expander_phy_control - expander phy control ++ * @ioc: per adapter object ++ * @phy: The sas phy object ++ * ++ * Returns 0 for success, non-zero for failure. ++ * ++ */ ++static int ++_transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc, ++ struct sas_phy *phy, u8 phy_operation) ++{ ++ Mpi2SmpPassthroughRequest_t *mpi_request; ++ Mpi2SmpPassthroughReply_t *mpi_reply; ++ struct phy_control_request *phy_control_request; ++ struct phy_control_reply *phy_control_reply; ++ int rc; ++ u16 smid; ++ u32 ioc_state; ++ unsigned long timeleft; ++ void *psge; ++ u8 issue_reset = 0; ++ void *data_out = NULL; ++ dma_addr_t data_out_dma; ++ u32 sz; ++ u16 wait_state_count; ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery) { ++ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", ++ __func__, ioc->name); ++ return -EFAULT; ++ } ++ ++ mutex_lock(&ioc->transport_cmds.mutex); ++ ++ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: transport_cmds in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ioc->transport_cmds.status = MPT3_CMD_PENDING; ++ ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, __func__, wait_state_count); ++ } ++ if (wait_state_count) ++ pr_info(MPT3SAS_FMT "%s: ioc is operational\n", ++ ioc->name, __func__); ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->transport_cmds.smid = smid; ++ ++ sz = sizeof(struct phy_control_request) + ++ sizeof(struct phy_control_reply); ++ data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma); ++ if (!data_out) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ rc = -ENOMEM; ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ ++ rc = -EINVAL; ++ memset(data_out, 0, sz); ++ phy_control_request = data_out; ++ phy_control_request->smp_frame_type = 0x40; ++ phy_control_request->function = 0x91; ++ phy_control_request->request_length = 9; ++ phy_control_request->allocated_response_length = 0; ++ phy_control_request->phy_identifier = phy->number; ++ phy_control_request->phy_operation = phy_operation; ++ phy_control_request->programmed_min_physical_link_rate = ++ phy->minimum_linkrate << 4; ++ phy_control_request->programmed_max_physical_link_rate = ++ phy->maximum_linkrate << 4; ++ ++ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; ++ mpi_request->PhysicalPort = 0xFF; ++ mpi_request->VF_ID = 0; /* TODO */ ++ mpi_request->VP_ID = 0; ++ mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address); ++ mpi_request->RequestDataLength = ++ cpu_to_le16(sizeof(struct phy_error_log_request)); ++ psge = &mpi_request->SGL; ++ ++ ioc->build_sg(ioc, psge, data_out_dma, ++ sizeof(struct phy_control_request), ++ data_out_dma + sizeof(struct phy_control_request), ++ sizeof(struct phy_control_reply)); ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_control - send to sas_addr(0x%016llx), phy(%d), opcode(%d)\n", ++ ioc->name, (unsigned long long)phy->identify.sas_address, ++ phy->number, phy_operation)); ++ init_completion(&ioc->transport_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, ++ 10*HZ); ++ ++ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SmpPassthroughRequest_t)/4); ++ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ goto issue_host_reset; ++ } ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_control - complete\n", ioc->name)); ++ ++ if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) { ++ ++ mpi_reply = ioc->transport_cmds.reply; ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_control - reply data transfer size(%d)\n", ++ ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength))); ++ ++ if (le16_to_cpu(mpi_reply->ResponseDataLength) != ++ sizeof(struct phy_control_reply)) ++ goto out; ++ ++ phy_control_reply = data_out + ++ sizeof(struct phy_control_request); ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_control - function_result(%d)\n", ++ ioc->name, phy_control_reply->function_result)); ++ ++ rc = 0; ++ } else ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_control - no reply\n", ioc->name)); ++ ++ issue_host_reset: ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ out: ++ ioc->transport_cmds.status = MPT3_CMD_NOT_USED; ++ if (data_out) ++ pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma); ++ ++ mutex_unlock(&ioc->transport_cmds.mutex); ++ return rc; ++} ++ ++/** ++ * _transport_phy_reset - ++ * @phy: The sas phy object ++ * @hard_reset: ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_transport_phy_reset(struct sas_phy *phy, int hard_reset) ++{ ++ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy); ++ Mpi2SasIoUnitControlReply_t mpi_reply; ++ Mpi2SasIoUnitControlRequest_t mpi_request; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ if (_transport_sas_node_find_by_sas_address(ioc, ++ phy->identify.sas_address) == NULL) { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return -EINVAL; ++ } ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ /* handle expander phys */ ++ if (phy->identify.sas_address != ioc->sas_hba.sas_address) ++ return _transport_expander_phy_control(ioc, phy, ++ (hard_reset == 1) ? SMP_PHY_CONTROL_HARD_RESET : ++ SMP_PHY_CONTROL_LINK_RESET); ++ ++ /* handle hba phys */ ++ memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; ++ mpi_request.Operation = hard_reset ? ++ MPI2_SAS_OP_PHY_HARD_RESET : MPI2_SAS_OP_PHY_LINK_RESET; ++ mpi_request.PhyNum = phy->number; ++ ++ if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply, &mpi_request))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -ENXIO; ++ } ++ ++ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) ++ pr_info(MPT3SAS_FMT ++ "phy(%d), ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, phy->number, le16_to_cpu(mpi_reply.IOCStatus), ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ ++ return 0; ++} ++ ++/** ++ * _transport_phy_enable - enable/disable phys ++ * @phy: The sas phy object ++ * @enable: enable phy when true ++ * ++ * Only support sas_host direct attached phys. ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_transport_phy_enable(struct sas_phy *phy, int enable) ++{ ++ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy); ++ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; ++ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 ioc_status; ++ u16 sz; ++ int rc = 0; ++ unsigned long flags; ++ int i, discovery_active; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ if (_transport_sas_node_find_by_sas_address(ioc, ++ phy->identify.sas_address) == NULL) { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return -EINVAL; ++ } ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ /* handle expander phys */ ++ if (phy->identify.sas_address != ioc->sas_hba.sas_address) ++ return _transport_expander_phy_control(ioc, phy, ++ (enable == 1) ? SMP_PHY_CONTROL_LINK_RESET : ++ SMP_PHY_CONTROL_DISABLE); ++ ++ /* handle hba phys */ ++ ++ /* read sas_iounit page 0 */ ++ sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys * ++ sizeof(Mpi2SasIOUnit0PhyData_t)); ++ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); ++ if (!sas_iounit_pg0) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -ENOMEM; ++ goto out; ++ } ++ if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, ++ sas_iounit_pg0, sz))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -ENXIO; ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -EIO; ++ goto out; ++ } ++ ++ /* unable to enable/disable phys when when discovery is active */ ++ for (i = 0, discovery_active = 0; i < ioc->sas_hba.num_phys ; i++) { ++ if (sas_iounit_pg0->PhyData[i].PortFlags & ++ MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) { ++ pr_err(MPT3SAS_FMT "discovery is active on " \ ++ "port = %d, phy = %d: unable to enable/disable " ++ "phys, try again later!\n", ioc->name, ++ sas_iounit_pg0->PhyData[i].Port, i); ++ discovery_active = 1; ++ } ++ } ++ ++ if (discovery_active) { ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ /* read sas_iounit page 1 */ ++ sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * ++ sizeof(Mpi2SasIOUnit1PhyData_t)); ++ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); ++ if (!sas_iounit_pg1) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -ENOMEM; ++ goto out; ++ } ++ if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, ++ sas_iounit_pg1, sz))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -ENXIO; ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -EIO; ++ goto out; ++ } ++ ++ /* copy Port/PortFlags/PhyFlags from page 0 */ ++ for (i = 0; i < ioc->sas_hba.num_phys ; i++) { ++ sas_iounit_pg1->PhyData[i].Port = ++ sas_iounit_pg0->PhyData[i].Port; ++ sas_iounit_pg1->PhyData[i].PortFlags = ++ (sas_iounit_pg0->PhyData[i].PortFlags & ++ MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG); ++ sas_iounit_pg1->PhyData[i].PhyFlags = ++ (sas_iounit_pg0->PhyData[i].PhyFlags & ++ (MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED + ++ MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED)); ++ } ++ ++ if (enable) ++ sas_iounit_pg1->PhyData[phy->number].PhyFlags ++ &= ~MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE; ++ else ++ sas_iounit_pg1->PhyData[phy->number].PhyFlags ++ |= MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE; ++ ++ mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, sz); ++ ++ /* link reset */ ++ if (enable) ++ _transport_phy_reset(phy, 0); ++ ++ out: ++ kfree(sas_iounit_pg1); ++ kfree(sas_iounit_pg0); ++ return rc; ++} ++ ++/** ++ * _transport_phy_speed - set phy min/max link rates ++ * @phy: The sas phy object ++ * @rates: rates defined in sas_phy_linkrates ++ * ++ * Only support sas_host direct attached phys. ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates) ++{ ++ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy); ++ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; ++ Mpi2SasPhyPage0_t phy_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 ioc_status; ++ u16 sz; ++ int i; ++ int rc = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ if (_transport_sas_node_find_by_sas_address(ioc, ++ phy->identify.sas_address) == NULL) { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return -EINVAL; ++ } ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ if (!rates->minimum_linkrate) ++ rates->minimum_linkrate = phy->minimum_linkrate; ++ else if (rates->minimum_linkrate < phy->minimum_linkrate_hw) ++ rates->minimum_linkrate = phy->minimum_linkrate_hw; ++ ++ if (!rates->maximum_linkrate) ++ rates->maximum_linkrate = phy->maximum_linkrate; ++ else if (rates->maximum_linkrate > phy->maximum_linkrate_hw) ++ rates->maximum_linkrate = phy->maximum_linkrate_hw; ++ ++ /* handle expander phys */ ++ if (phy->identify.sas_address != ioc->sas_hba.sas_address) { ++ phy->minimum_linkrate = rates->minimum_linkrate; ++ phy->maximum_linkrate = rates->maximum_linkrate; ++ return _transport_expander_phy_control(ioc, phy, ++ SMP_PHY_CONTROL_LINK_RESET); ++ } ++ ++ /* handle hba phys */ ++ ++ /* sas_iounit page 1 */ ++ sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * ++ sizeof(Mpi2SasIOUnit1PhyData_t)); ++ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); ++ if (!sas_iounit_pg1) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -ENOMEM; ++ goto out; ++ } ++ if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, ++ sas_iounit_pg1, sz))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -ENXIO; ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -EIO; ++ goto out; ++ } ++ ++ for (i = 0; i < ioc->sas_hba.num_phys; i++) { ++ if (phy->number != i) { ++ sas_iounit_pg1->PhyData[i].MaxMinLinkRate = ++ (ioc->sas_hba.phy[i].phy->minimum_linkrate + ++ (ioc->sas_hba.phy[i].phy->maximum_linkrate << 4)); ++ } else { ++ sas_iounit_pg1->PhyData[i].MaxMinLinkRate = ++ (rates->minimum_linkrate + ++ (rates->maximum_linkrate << 4)); ++ } ++ } ++ ++ if (mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, ++ sz)) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ /* link reset */ ++ _transport_phy_reset(phy, 0); ++ ++ /* read phy page 0, then update the rates in the sas transport phy */ ++ if (!mpt2sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0, ++ phy->number)) { ++ phy->minimum_linkrate = _transport_convert_phy_link_rate( ++ phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); ++ phy->maximum_linkrate = _transport_convert_phy_link_rate( ++ phy_pg0.ProgrammedLinkRate >> 4); ++ phy->negotiated_linkrate = _transport_convert_phy_link_rate( ++ phy_pg0.NegotiatedLinkRate & ++ MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL); ++ } ++ ++ out: ++ kfree(sas_iounit_pg1); ++ return rc; ++} ++ ++/** ++ * _transport_smp_handler - transport portal for smp passthru ++ * @shost: shost object ++ * @rphy: sas transport rphy object ++ * @req: ++ * ++ * This used primarily for smp_utils. ++ * Example: ++ * smp_rep_general /sys/class/bsg/expander-5:0 ++ */ ++static int ++_transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, ++ struct request *req) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ Mpi2SmpPassthroughRequest_t *mpi_request; ++ Mpi2SmpPassthroughReply_t *mpi_reply; ++ int rc, i; ++ u16 smid; ++ u32 ioc_state; ++ unsigned long timeleft; ++ void *psge; ++ u8 issue_reset = 0; ++ dma_addr_t dma_addr_in = 0; ++ dma_addr_t dma_addr_out = 0; ++ dma_addr_t pci_dma_in = 0; ++ dma_addr_t pci_dma_out = 0; ++ void *pci_addr_in = NULL; ++ void *pci_addr_out = NULL; ++ u16 wait_state_count; ++ struct request *rsp = req->next_rq; ++ struct bio_vec *bvec = NULL; ++ ++ if (!rsp) { ++ pr_err(MPT3SAS_FMT "%s: the smp response space is missing\n", ++ ioc->name, __func__); ++ return -EINVAL; ++ } ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery) { ++ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", ++ __func__, ioc->name); ++ return -EFAULT; ++ } ++ ++ rc = mutex_lock_interruptible(&ioc->transport_cmds.mutex); ++ if (rc) ++ return rc; ++ ++ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: transport_cmds in use\n", ioc->name, ++ __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ioc->transport_cmds.status = MPT3_CMD_PENDING; ++ ++ /* Check if the request is split across multiple segments */ ++ if (req->bio->bi_vcnt > 1) { ++ u32 offset = 0; ++ ++ /* Allocate memory and copy the request */ ++ pci_addr_out = pci_alloc_consistent(ioc->pdev, ++ blk_rq_bytes(req), &pci_dma_out); ++ if (!pci_addr_out) { ++ pr_info(MPT3SAS_FMT "%s(): PCI Addr out = NULL\n", ++ ioc->name, __func__); ++ rc = -ENOMEM; ++ goto out; ++ } ++ ++ bio_for_each_segment(bvec, req->bio, i) { ++ memcpy(pci_addr_out + offset, ++ page_address(bvec->bv_page) + bvec->bv_offset, ++ bvec->bv_len); ++ offset += bvec->bv_len; ++ } ++ } else { ++ dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio), ++ blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL); ++ if (pci_dma_mapping_error(ioc->pdev, dma_addr_out)) { ++ pr_info(MPT3SAS_FMT "%s(): DMA Addr out = NULL\n", ++ ioc->name, __func__); ++ rc = -ENOMEM; ++ goto free_pci; ++ } ++ } ++ ++ /* Check if the response needs to be populated across ++ * multiple segments */ ++ if (rsp->bio->bi_vcnt > 1) { ++ pci_addr_in = pci_alloc_consistent(ioc->pdev, blk_rq_bytes(rsp), ++ &pci_dma_in); ++ if (!pci_addr_in) { ++ pr_info(MPT3SAS_FMT "%s(): PCI Addr in = NULL\n", ++ ioc->name, __func__); ++ rc = -ENOMEM; ++ goto unmap; ++ } ++ } else { ++ dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio), ++ blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL); ++ if (pci_dma_mapping_error(ioc->pdev, dma_addr_in)) { ++ pr_info(MPT3SAS_FMT "%s(): DMA Addr in = NULL\n", ++ ioc->name, __func__); ++ rc = -ENOMEM; ++ goto unmap; ++ } ++ } ++ ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto unmap; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, __func__, wait_state_count); ++ } ++ if (wait_state_count) ++ pr_info(MPT3SAS_FMT "%s: ioc is operational\n", ++ ioc->name, __func__); ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto unmap; ++ } ++ ++ rc = 0; ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->transport_cmds.smid = smid; ++ ++ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; ++ mpi_request->PhysicalPort = 0xFF; ++ mpi_request->SASAddress = (rphy) ? ++ cpu_to_le64(rphy->identify.sas_address) : ++ cpu_to_le64(ioc->sas_hba.sas_address); ++ mpi_request->RequestDataLength = cpu_to_le16(blk_rq_bytes(req) - 4); ++ psge = &mpi_request->SGL; ++ ++ if (req->bio->bi_vcnt > 1) ++ ioc->build_sg(ioc, psge, pci_dma_out, (blk_rq_bytes(req) - 4), ++ pci_dma_in, (blk_rq_bytes(rsp) + 4)); ++ else ++ ioc->build_sg(ioc, psge, dma_addr_out, (blk_rq_bytes(req) - 4), ++ dma_addr_in, (blk_rq_bytes(rsp) + 4)); ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s - sending smp request\n", ioc->name, __func__)); ++ ++ init_completion(&ioc->transport_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, ++ 10*HZ); ++ ++ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s : timeout\n", ++ __func__, ioc->name); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SmpPassthroughRequest_t)/4); ++ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ goto issue_host_reset; ++ } ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s - complete\n", ioc->name, __func__)); ++ ++ if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) { ++ ++ mpi_reply = ioc->transport_cmds.reply; ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s - reply data transfer size(%d)\n", ++ ioc->name, __func__, ++ le16_to_cpu(mpi_reply->ResponseDataLength))); ++ ++ memcpy(req->sense, mpi_reply, sizeof(*mpi_reply)); ++ req->sense_len = sizeof(*mpi_reply); ++ req->resid_len = 0; ++ rsp->resid_len -= ++ le16_to_cpu(mpi_reply->ResponseDataLength); ++ ++ /* check if the resp needs to be copied from the allocated ++ * pci mem */ ++ if (rsp->bio->bi_vcnt > 1) { ++ u32 offset = 0; ++ u32 bytes_to_copy = ++ le16_to_cpu(mpi_reply->ResponseDataLength); ++ bio_for_each_segment(bvec, rsp->bio, i) { ++ if (bytes_to_copy <= bvec->bv_len) { ++ memcpy(page_address(bvec->bv_page) + ++ bvec->bv_offset, pci_addr_in + ++ offset, bytes_to_copy); ++ break; ++ } else { ++ memcpy(page_address(bvec->bv_page) + ++ bvec->bv_offset, pci_addr_in + ++ offset, bvec->bv_len); ++ bytes_to_copy -= bvec->bv_len; ++ } ++ offset += bvec->bv_len; ++ } ++ } ++ } else { ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s - no reply\n", ioc->name, __func__)); ++ rc = -ENXIO; ++ } ++ ++ issue_host_reset: ++ if (issue_reset) { ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ rc = -ETIMEDOUT; ++ } ++ ++ unmap: ++ if (dma_addr_out) ++ pci_unmap_single(ioc->pdev, dma_addr_out, blk_rq_bytes(req), ++ PCI_DMA_BIDIRECTIONAL); ++ if (dma_addr_in) ++ pci_unmap_single(ioc->pdev, dma_addr_in, blk_rq_bytes(rsp), ++ PCI_DMA_BIDIRECTIONAL); ++ ++ free_pci: ++ if (pci_addr_out) ++ pci_free_consistent(ioc->pdev, blk_rq_bytes(req), pci_addr_out, ++ pci_dma_out); ++ ++ if (pci_addr_in) ++ pci_free_consistent(ioc->pdev, blk_rq_bytes(rsp), pci_addr_in, ++ pci_dma_in); ++ ++ out: ++ ioc->transport_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_unlock(&ioc->transport_cmds.mutex); ++ return rc; ++} ++ ++struct sas_function_template mpt2sas_transport_functions = { ++ .get_linkerrors = _transport_get_linkerrors, ++ .get_enclosure_identifier = _transport_get_enclosure_identifier, ++ .get_bay_identifier = _transport_get_bay_identifier, ++ .phy_reset = _transport_phy_reset, ++ .phy_enable = _transport_phy_enable, ++ .set_phy_speed = _transport_phy_speed, ++ .smp_handler = _transport_smp_handler, ++}; ++ ++struct scsi_transport_template *mpt2sas_transport_template; +diff --git a/drivers/scsi/mpt2sas/mpt3sas_trigger_diag.c b/drivers/scsi/mpt2sas/mpt3sas_trigger_diag.c +new file mode 100644 +index 0000000..d5038ec +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_trigger_diag.c +@@ -0,0 +1,434 @@ ++/* ++ * This module provides common API to set Diagnostic trigger for MPT ++ * (Message Passing Technology) based controllers ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "mpt3sas_base.h" ++ ++/** ++ * _mpt2sas_raise_sigio - notifiy app ++ * @ioc: per adapter object ++ * @event_data: ++ */ ++static void ++_mpt2sas_raise_sigio(struct MPT3SAS_ADAPTER *ioc, ++ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data) ++{ ++ Mpi2EventNotificationReply_t *mpi_reply; ++ u16 sz, event_data_sz; ++ unsigned long flags; ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ++ ioc->name, __func__)); ++ ++ sz = offsetof(Mpi2EventNotificationReply_t, EventData) + ++ sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T) + 4; ++ mpi_reply = kzalloc(sz, GFP_KERNEL); ++ if (!mpi_reply) ++ goto out; ++ mpi_reply->Event = cpu_to_le16(MPI3_EVENT_DIAGNOSTIC_TRIGGER_FIRED); ++ event_data_sz = (sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T) + 4) / 4; ++ mpi_reply->EventDataLength = cpu_to_le16(event_data_sz); ++ memcpy(&mpi_reply->EventData, event_data, ++ sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T)); ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: add to driver event log\n", ++ ioc->name, __func__)); ++ mpt2sas_ctl_add_to_event_log(ioc, mpi_reply); ++ kfree(mpi_reply); ++ out: ++ ++ /* clearing the diag_trigger_active flag */ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: clearing diag_trigger_active flag\n", ++ ioc->name, __func__)); ++ ioc->diag_trigger_active = 0; ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++} ++ ++/** ++ * mpt2sas_process_trigger_data - process the event data for the trigger ++ * @ioc: per adapter object ++ * @event_data: ++ */ ++void ++mpt2sas_process_trigger_data(struct MPT3SAS_ADAPTER *ioc, ++ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data) ++{ ++ u8 issue_reset = 0; ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ++ ioc->name, __func__)); ++ ++ /* release the diag buffer trace */ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) == 0) { ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: release trace diag buffer\n", ioc->name, __func__)); ++ mpt2sas_send_diag_release(ioc, MPI2_DIAG_BUF_TYPE_TRACE, ++ &issue_reset); ++ } ++ ++ _mpt2sas_raise_sigio(ioc, event_data); ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++} ++ ++/** ++ * mpt2sas_trigger_master - Master trigger handler ++ * @ioc: per adapter object ++ * @trigger_bitmask: ++ * ++ */ ++void ++mpt2sas_trigger_master(struct MPT3SAS_ADAPTER *ioc, u32 trigger_bitmask) ++{ ++ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data; ++ unsigned long flags; ++ u8 found_match = 0; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ ++ if (trigger_bitmask & MASTER_TRIGGER_FW_FAULT || ++ trigger_bitmask & MASTER_TRIGGER_ADAPTER_RESET) ++ goto by_pass_checks; ++ ++ /* check to see if trace buffers are currently registered */ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ /* check to see if trace buffers are currently released */ ++ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ by_pass_checks: ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enter - trigger_bitmask = 0x%08x\n", ++ ioc->name, __func__, trigger_bitmask)); ++ ++ /* don't send trigger if an trigger is currently active */ ++ if (ioc->diag_trigger_active) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ goto out; ++ } ++ ++ /* check for the trigger condition */ ++ if (ioc->diag_trigger_master.MasterData & trigger_bitmask) { ++ found_match = 1; ++ ioc->diag_trigger_active = 1; ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: setting diag_trigger_active flag\n", ++ ioc->name, __func__)); ++ } ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ ++ if (!found_match) ++ goto out; ++ ++ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T)); ++ event_data.trigger_type = MPT3SAS_TRIGGER_MASTER; ++ event_data.u.master.MasterData = trigger_bitmask; ++ ++ if (trigger_bitmask & MASTER_TRIGGER_FW_FAULT || ++ trigger_bitmask & MASTER_TRIGGER_ADAPTER_RESET) ++ _mpt2sas_raise_sigio(ioc, &event_data); ++ else ++ mpt2sas_send_trigger_data_event(ioc, &event_data); ++ ++ out: ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++} ++ ++/** ++ * mpt2sas_trigger_event - Event trigger handler ++ * @ioc: per adapter object ++ * @event: ++ * @log_entry_qualifier: ++ * ++ */ ++void ++mpt2sas_trigger_event(struct MPT3SAS_ADAPTER *ioc, u16 event, ++ u16 log_entry_qualifier) ++{ ++ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data; ++ struct SL_WH_EVENT_TRIGGER_T *event_trigger; ++ int i; ++ unsigned long flags; ++ u8 found_match; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ ++ /* check to see if trace buffers are currently registered */ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ /* check to see if trace buffers are currently released */ ++ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enter - event = 0x%04x, log_entry_qualifier = 0x%04x\n", ++ ioc->name, __func__, event, log_entry_qualifier)); ++ ++ /* don't send trigger if an trigger is currently active */ ++ if (ioc->diag_trigger_active) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ goto out; ++ } ++ ++ /* check for the trigger condition */ ++ event_trigger = ioc->diag_trigger_event.EventTriggerEntry; ++ for (i = 0 , found_match = 0; i < ioc->diag_trigger_event.ValidEntries ++ && !found_match; i++, event_trigger++) { ++ if (event_trigger->EventValue != event) ++ continue; ++ if (event == MPI2_EVENT_LOG_ENTRY_ADDED) { ++ if (event_trigger->LogEntryQualifier == ++ log_entry_qualifier) ++ found_match = 1; ++ continue; ++ } ++ found_match = 1; ++ ioc->diag_trigger_active = 1; ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: setting diag_trigger_active flag\n", ++ ioc->name, __func__)); ++ } ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ ++ if (!found_match) ++ goto out; ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: setting diag_trigger_active flag\n", ++ ioc->name, __func__)); ++ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T)); ++ event_data.trigger_type = MPT3SAS_TRIGGER_EVENT; ++ event_data.u.event.EventValue = event; ++ event_data.u.event.LogEntryQualifier = log_entry_qualifier; ++ mpt2sas_send_trigger_data_event(ioc, &event_data); ++ out: ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++} ++ ++/** ++ * mpt2sas_trigger_scsi - SCSI trigger handler ++ * @ioc: per adapter object ++ * @sense_key: ++ * @asc: ++ * @ascq: ++ * ++ */ ++void ++mpt2sas_trigger_scsi(struct MPT3SAS_ADAPTER *ioc, u8 sense_key, u8 asc, ++ u8 ascq) ++{ ++ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data; ++ struct SL_WH_SCSI_TRIGGER_T *scsi_trigger; ++ int i; ++ unsigned long flags; ++ u8 found_match; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ ++ /* check to see if trace buffers are currently registered */ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ /* check to see if trace buffers are currently released */ ++ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enter - sense_key = 0x%02x, asc = 0x%02x, ascq = 0x%02x\n", ++ ioc->name, __func__, sense_key, asc, ascq)); ++ ++ /* don't send trigger if an trigger is currently active */ ++ if (ioc->diag_trigger_active) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ goto out; ++ } ++ ++ /* check for the trigger condition */ ++ scsi_trigger = ioc->diag_trigger_scsi.SCSITriggerEntry; ++ for (i = 0 , found_match = 0; i < ioc->diag_trigger_scsi.ValidEntries ++ && !found_match; i++, scsi_trigger++) { ++ if (scsi_trigger->SenseKey != sense_key) ++ continue; ++ if (!(scsi_trigger->ASC == 0xFF || scsi_trigger->ASC == asc)) ++ continue; ++ if (!(scsi_trigger->ASCQ == 0xFF || scsi_trigger->ASCQ == ascq)) ++ continue; ++ found_match = 1; ++ ioc->diag_trigger_active = 1; ++ } ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ ++ if (!found_match) ++ goto out; ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: setting diag_trigger_active flag\n", ++ ioc->name, __func__)); ++ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T)); ++ event_data.trigger_type = MPT3SAS_TRIGGER_SCSI; ++ event_data.u.scsi.SenseKey = sense_key; ++ event_data.u.scsi.ASC = asc; ++ event_data.u.scsi.ASCQ = ascq; ++ mpt2sas_send_trigger_data_event(ioc, &event_data); ++ out: ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++} ++ ++/** ++ * mpt2sas_trigger_mpi - MPI trigger handler ++ * @ioc: per adapter object ++ * @ioc_status: ++ * @loginfo: ++ * ++ */ ++void ++mpt2sas_trigger_mpi(struct MPT3SAS_ADAPTER *ioc, u16 ioc_status, u32 loginfo) ++{ ++ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data; ++ struct SL_WH_MPI_TRIGGER_T *mpi_trigger; ++ int i; ++ unsigned long flags; ++ u8 found_match; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ ++ /* check to see if trace buffers are currently registered */ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ /* check to see if trace buffers are currently released */ ++ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enter - ioc_status = 0x%04x, loginfo = 0x%08x\n", ++ ioc->name, __func__, ioc_status, loginfo)); ++ ++ /* don't send trigger if an trigger is currently active */ ++ if (ioc->diag_trigger_active) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ goto out; ++ } ++ ++ /* check for the trigger condition */ ++ mpi_trigger = ioc->diag_trigger_mpi.MPITriggerEntry; ++ for (i = 0 , found_match = 0; i < ioc->diag_trigger_mpi.ValidEntries ++ && !found_match; i++, mpi_trigger++) { ++ if (mpi_trigger->IOCStatus != ioc_status) ++ continue; ++ if (!(mpi_trigger->IocLogInfo == 0xFFFFFFFF || ++ mpi_trigger->IocLogInfo == loginfo)) ++ continue; ++ found_match = 1; ++ ioc->diag_trigger_active = 1; ++ } ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ ++ if (!found_match) ++ goto out; ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: setting diag_trigger_active flag\n", ++ ioc->name, __func__)); ++ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T)); ++ event_data.trigger_type = MPT3SAS_TRIGGER_MPI; ++ event_data.u.mpi.IOCStatus = ioc_status; ++ event_data.u.mpi.IocLogInfo = loginfo; ++ mpt2sas_send_trigger_data_event(ioc, &event_data); ++ out: ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++} +diff --git a/drivers/scsi/mpt2sas/mpt3sas_trigger_diag.h b/drivers/scsi/mpt2sas/mpt3sas_trigger_diag.h +new file mode 100644 +index 0000000..6586a46 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_trigger_diag.h +@@ -0,0 +1,194 @@ ++/* ++ * This is the Fusion MPT base driver providing common API layer interface ++ * to set Diagnostic triggers for MPT (Message Passing Technology) based ++ * controllers ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.h ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ /* Diagnostic Trigger Configuration Data Structures */ ++ ++#ifndef MPT3SAS_TRIGGER_DIAG_H_INCLUDED ++#define MPT3SAS_TRIGGER_DIAG_H_INCLUDED ++ ++/* limitation on number of entries */ ++#define NUM_VALID_ENTRIES (20) ++ ++/* trigger types */ ++#define MPT3SAS_TRIGGER_MASTER (1) ++#define MPT3SAS_TRIGGER_EVENT (2) ++#define MPT3SAS_TRIGGER_SCSI (3) ++#define MPT3SAS_TRIGGER_MPI (4) ++ ++/* trigger names */ ++#define MASTER_TRIGGER_FILE_NAME "diag_trigger_master" ++#define EVENT_TRIGGERS_FILE_NAME "diag_trigger_event" ++#define SCSI_TRIGGERS_FILE_NAME "diag_trigger_scsi" ++#define MPI_TRIGGER_FILE_NAME "diag_trigger_mpi" ++ ++/* master trigger bitmask */ ++#define MASTER_TRIGGER_FW_FAULT (0x00000001) ++#define MASTER_TRIGGER_ADAPTER_RESET (0x00000002) ++#define MASTER_TRIGGER_TASK_MANAGMENT (0x00000004) ++#define MASTER_TRIGGER_DEVICE_REMOVAL (0x00000008) ++ ++/* fake firmware event for tigger */ ++#define MPI3_EVENT_DIAGNOSTIC_TRIGGER_FIRED (0x6E) ++ ++/** ++ * MasterTrigger is a single U32 passed to/from sysfs. ++ * ++ * Bit Flags (enables) include: ++ * 1. FW Faults ++ * 2. Adapter Reset issued by driver ++ * 3. TMs ++ * 4. Device Remove Event sent by FW ++ */ ++ ++struct SL_WH_MASTER_TRIGGER_T { ++ uint32_t MasterData; ++}; ++ ++/** ++ * struct SL_WH_EVENT_TRIGGER_T - Definition of an event trigger element ++ * @EventValue: Event Code to trigger on ++ * @LogEntryQualifier: Type of FW event that logged (Log Entry Added Event only) ++ * ++ * Defines an event that should induce a DIAG_TRIGGER driver event if observed. ++ */ ++struct SL_WH_EVENT_TRIGGER_T { ++ uint16_t EventValue; ++ uint16_t LogEntryQualifier; ++}; ++ ++/** ++ * struct SL_WH_EVENT_TRIGGERS_T - Structure passed to/from sysfs containing a ++ * list of Event Triggers to be monitored for. ++ * @ValidEntries: Number of _SL_WH_EVENT_TRIGGER_T structures contained in this ++ * structure. ++ * @EventTriggerEntry: List of Event trigger elements. ++ * ++ * This binary structure is transferred via sysfs to get/set Event Triggers ++ * in the Linux Driver. ++ */ ++ ++struct SL_WH_EVENT_TRIGGERS_T { ++ uint32_t ValidEntries; ++ struct SL_WH_EVENT_TRIGGER_T EventTriggerEntry[NUM_VALID_ENTRIES]; ++}; ++ ++/** ++ * struct SL_WH_SCSI_TRIGGER_T - Definition of a SCSI trigger element ++ * @ASCQ: Additional Sense Code Qualifier. Can be specific or 0xFF for ++ * wildcard. ++ * @ASC: Additional Sense Code. Can be specific or 0xFF for wildcard ++ * @SenseKey: SCSI Sense Key ++ * ++ * Defines a sense key (single or many variants) that should induce a ++ * DIAG_TRIGGER driver event if observed. ++ */ ++struct SL_WH_SCSI_TRIGGER_T { ++ U8 ASCQ; ++ U8 ASC; ++ U8 SenseKey; ++ U8 Reserved; ++}; ++ ++/** ++ * struct SL_WH_SCSI_TRIGGERS_T - Structure passed to/from sysfs containing a ++ * list of SCSI sense codes that should trigger a DIAG_SERVICE event when ++ * observed. ++ * @ValidEntries: Number of _SL_WH_SCSI_TRIGGER_T structures contained in this ++ * structure. ++ * @SCSITriggerEntry: List of SCSI Sense Code trigger elements. ++ * ++ * This binary structure is transferred via sysfs to get/set SCSI Sense Code ++ * Triggers in the Linux Driver. ++ */ ++struct SL_WH_SCSI_TRIGGERS_T { ++ uint32_t ValidEntries; ++ struct SL_WH_SCSI_TRIGGER_T SCSITriggerEntry[NUM_VALID_ENTRIES]; ++}; ++ ++/** ++ * struct SL_WH_MPI_TRIGGER_T - Definition of an MPI trigger element ++ * @IOCStatus: MPI IOCStatus ++ * @IocLogInfo: MPI IocLogInfo. Can be specific or 0xFFFFFFFF for wildcard ++ * ++ * Defines a MPI IOCStatus/IocLogInfo pair that should induce a DIAG_TRIGGER ++ * driver event if observed. ++ */ ++struct SL_WH_MPI_TRIGGER_T { ++ uint16_t IOCStatus; ++ uint16_t Reserved; ++ uint32_t IocLogInfo; ++}; ++ ++/** ++ * struct SL_WH_MPI_TRIGGERS_T - Structure passed to/from sysfs containing a ++ * list of MPI IOCStatus/IocLogInfo pairs that should trigger a DIAG_SERVICE ++ * event when observed. ++ * @ValidEntries: Number of _SL_WH_MPI_TRIGGER_T structures contained in this ++ * structure. ++ * @MPITriggerEntry: List of MPI IOCStatus/IocLogInfo trigger elements. ++ * ++ * This binary structure is transferred via sysfs to get/set MPI Error Triggers ++ * in the Linux Driver. ++ */ ++struct SL_WH_MPI_TRIGGERS_T { ++ uint32_t ValidEntries; ++ struct SL_WH_MPI_TRIGGER_T MPITriggerEntry[NUM_VALID_ENTRIES]; ++}; ++ ++/** ++ * struct SL_WH_TRIGGERS_EVENT_DATA_T - event data for trigger ++ * @trigger_type: trigger type (see MPT3SAS_TRIGGER_XXXX) ++ * @u: trigger condition that caused trigger to be sent ++ */ ++struct SL_WH_TRIGGERS_EVENT_DATA_T { ++ uint32_t trigger_type; ++ union { ++ struct SL_WH_MASTER_TRIGGER_T master; ++ struct SL_WH_EVENT_TRIGGER_T event; ++ struct SL_WH_SCSI_TRIGGER_T scsi; ++ struct SL_WH_MPI_TRIGGER_T mpi; ++ } u; ++}; ++#endif /* MPT3SAS_TRIGGER_DIAG_H_INCLUDED */ +diff --git a/drivers/scsi/mpt2sas/mpt3sas_warpdrive.c b/drivers/scsi/mpt2sas/mpt3sas_warpdrive.c +new file mode 100644 +index 0000000..2542263 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_warpdrive.c +@@ -0,0 +1,344 @@ ++/* ++ * Scsi Host Layer for MPT (Message Passing Technology) based controllers ++ * ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2015 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program. ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include "mpt3sas_base.h" ++ ++/** ++ * _warpdrive_disable_ddio - Disable direct I/O for all the volumes ++ * @ioc: per adapter object ++ */ ++static void ++_warpdrive_disable_ddio(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2RaidVolPage1_t vol_pg1; ++ Mpi2ConfigReply_t mpi_reply; ++ struct _raid_device *raid_device; ++ u16 handle; ++ u16 ioc_status; ++ unsigned long flags; ++ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, ++ &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) ++ break; ++ handle = le16_to_cpu(vol_pg1.DevHandle); ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = mpt2sas_raid_device_find_by_handle(ioc, handle); ++ if (raid_device) ++ raid_device->direct_io_enabled = 0; ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ } ++ return; ++} ++ ++ ++/** ++ * mpt2sas_get_num_volumes - Get number of volumes in the ioc ++ * @ioc: per adapter object ++ */ ++u8 ++mpt2sas_get_num_volumes(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2RaidVolPage1_t vol_pg1; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 handle; ++ u8 vol_cnt = 0; ++ u16 ioc_status; ++ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, ++ &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) ++ break; ++ vol_cnt++; ++ handle = le16_to_cpu(vol_pg1.DevHandle); ++ } ++ return vol_cnt; ++} ++ ++ ++/** ++ * mpt2sas_init_warpdrive_properties - Set properties for warpdrive direct I/O. ++ * @ioc: per adapter object ++ * @raid_device: the raid_device object ++ */ ++void ++mpt2sas_init_warpdrive_properties(struct MPT3SAS_ADAPTER *ioc, ++ struct _raid_device *raid_device) ++{ ++ Mpi2RaidVolPage0_t *vol_pg0; ++ Mpi2RaidPhysDiskPage0_t pd_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 sz; ++ u8 num_pds, count; ++ unsigned long stripe_sz, block_sz; ++ u8 stripe_exp, block_exp; ++ u64 dev_max_lba; ++ ++ if (!ioc->is_warpdrive) ++ return; ++ ++ if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "globally as drives are exposed\n", ioc->name); ++ return; ++ } ++ if (mpt2sas_get_num_volumes(ioc) > 1) { ++ _warpdrive_disable_ddio(ioc); ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "globally as number of drives > 1\n", ioc->name); ++ return; ++ } ++ if ((mpt2sas_config_get_number_pds(ioc, raid_device->handle, ++ &num_pds)) || !num_pds) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "Failure in computing number of drives\n", ioc->name); ++ return; ++ } ++ ++ sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds * ++ sizeof(Mpi2RaidVol0PhysDisk_t)); ++ vol_pg0 = kzalloc(sz, GFP_KERNEL); ++ if (!vol_pg0) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "Memory allocation failure for RVPG0\n", ioc->name); ++ return; ++ } ++ ++ if ((mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0, ++ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "Failure in retrieving RVPG0\n", ioc->name); ++ kfree(vol_pg0); ++ return; ++ } ++ ++ /* ++ * WARPDRIVE:If number of physical disks in a volume exceeds the max pds ++ * assumed for WARPDRIVE, disable direct I/O ++ */ ++ if (num_pds > MPT_MAX_WARPDRIVE_PDS) { ++ pr_warn(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "for the drive with handle(0x%04x): num_mem=%d, " ++ "max_mem_allowed=%d\n", ioc->name, raid_device->handle, ++ num_pds, MPT_MAX_WARPDRIVE_PDS); ++ kfree(vol_pg0); ++ return; ++ } ++ for (count = 0; count < num_pds; count++) { ++ if (mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, ++ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM, ++ vol_pg0->PhysDisk[count].PhysDiskNum) || ++ pd_pg0.DevHandle == MPT3SAS_INVALID_DEVICE_HANDLE) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is " ++ "disabled for the drive with handle(0x%04x) member" ++ "handle retrieval failed for member number=%d\n", ++ ioc->name, raid_device->handle, ++ vol_pg0->PhysDisk[count].PhysDiskNum); ++ goto out_error; ++ } ++ /* Disable direct I/O if member drive lba exceeds 4 bytes */ ++ dev_max_lba = le64_to_cpu(pd_pg0.DeviceMaxLBA); ++ if (dev_max_lba >> 32) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is " ++ "disabled for the drive with handle(0x%04x) member" ++ " handle (0x%04x) unsupported max lba 0x%016llx\n", ++ ioc->name, raid_device->handle, ++ le16_to_cpu(pd_pg0.DevHandle), ++ (unsigned long long)dev_max_lba); ++ goto out_error; ++ } ++ ++ raid_device->pd_handle[count] = le16_to_cpu(pd_pg0.DevHandle); ++ } ++ ++ /* ++ * Assumption for WD: Direct I/O is not supported if the volume is ++ * not RAID0 ++ */ ++ if (raid_device->volume_type != MPI2_RAID_VOL_TYPE_RAID0) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "for the drive with handle(0x%04x): type=%d, " ++ "s_sz=%uK, blk_size=%u\n", ioc->name, ++ raid_device->handle, raid_device->volume_type, ++ (le32_to_cpu(vol_pg0->StripeSize) * ++ le16_to_cpu(vol_pg0->BlockSize)) / 1024, ++ le16_to_cpu(vol_pg0->BlockSize)); ++ goto out_error; ++ } ++ ++ stripe_sz = le32_to_cpu(vol_pg0->StripeSize); ++ stripe_exp = find_first_bit(&stripe_sz, 32); ++ if (stripe_exp == 32) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "for the drive with handle(0x%04x) invalid stripe sz %uK\n", ++ ioc->name, raid_device->handle, ++ (le32_to_cpu(vol_pg0->StripeSize) * ++ le16_to_cpu(vol_pg0->BlockSize)) / 1024); ++ goto out_error; ++ } ++ raid_device->stripe_exponent = stripe_exp; ++ block_sz = le16_to_cpu(vol_pg0->BlockSize); ++ block_exp = find_first_bit(&block_sz, 16); ++ if (block_exp == 16) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "for the drive with handle(0x%04x) invalid block sz %u\n", ++ ioc->name, raid_device->handle, ++ le16_to_cpu(vol_pg0->BlockSize)); ++ goto out_error; ++ } ++ raid_device->block_exponent = block_exp; ++ raid_device->direct_io_enabled = 1; ++ ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is Enabled for the drive" ++ " with handle(0x%04x)\n", ioc->name, raid_device->handle); ++ /* ++ * WARPDRIVE: Though the following fields are not used for direct IO, ++ * stored for future purpose: ++ */ ++ raid_device->max_lba = le64_to_cpu(vol_pg0->MaxLBA); ++ raid_device->stripe_sz = le32_to_cpu(vol_pg0->StripeSize); ++ raid_device->block_sz = le16_to_cpu(vol_pg0->BlockSize); ++ ++ ++ kfree(vol_pg0); ++ return; ++ ++out_error: ++ raid_device->direct_io_enabled = 0; ++ for (count = 0; count < num_pds; count++) ++ raid_device->pd_handle[count] = 0; ++ kfree(vol_pg0); ++ return; ++} ++ ++/** ++ * mpt2sas_scsi_direct_io_get - returns direct io flag ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Returns the smid stored scmd pointer. ++ */ ++inline u8 ++mpt2sas_scsi_direct_io_get(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ return ioc->scsi_lookup[smid - 1].direct_io; ++} ++ ++/** ++ * mpt2sas_scsi_direct_io_set - sets direct io flag ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @direct_io: Zero or non-zero value to set in the direct_io flag ++ * ++ * Returns Nothing. ++ */ ++inline void ++mpt2sas_scsi_direct_io_set(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 direct_io) ++{ ++ ioc->scsi_lookup[smid - 1].direct_io = direct_io; ++} ++ ++/** ++ * mpt2sas_setup_direct_io - setup MPI request for WARPDRIVE Direct I/O ++ * @ioc: per adapter object ++ * @scmd: pointer to scsi command object ++ * @raid_device: pointer to raid device data structure ++ * @mpi_request: pointer to the SCSI_IO reqest message frame ++ * @smid: system request message index ++ * ++ * Returns nothing ++ */ ++void ++mpt2sas_setup_direct_io(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, ++ struct _raid_device *raid_device, Mpi2SCSIIORequest_t *mpi_request, ++ u16 smid) ++{ ++ sector_t v_lba, p_lba, stripe_off, column, io_size; ++ u32 stripe_sz, stripe_exp; ++ u8 num_pds, cmd = scmd->cmnd[0]; ++ ++ if (cmd != READ_10 && cmd != WRITE_10 && ++ cmd != READ_16 && cmd != WRITE_16) ++ return; ++ ++ if (cmd == READ_10 || cmd == WRITE_10) ++ v_lba = get_unaligned_be32(&mpi_request->CDB.CDB32[2]); ++ else ++ v_lba = get_unaligned_be64(&mpi_request->CDB.CDB32[2]); ++ ++ io_size = scsi_bufflen(scmd) >> raid_device->block_exponent; ++ ++ if (v_lba + io_size - 1 > raid_device->max_lba) ++ return; ++ ++ stripe_sz = raid_device->stripe_sz; ++ stripe_exp = raid_device->stripe_exponent; ++ stripe_off = v_lba & (stripe_sz - 1); ++ ++ /* Return unless IO falls within a stripe */ ++ if (stripe_off + io_size > stripe_sz) ++ return; ++ ++ num_pds = raid_device->num_pds; ++ p_lba = v_lba >> stripe_exp; ++ column = sector_div(p_lba, num_pds); ++ p_lba = (p_lba << stripe_exp) + stripe_off; ++ mpi_request->DevHandle = cpu_to_le16(raid_device->pd_handle[column]); ++ ++ if (cmd == READ_10 || cmd == WRITE_10) ++ put_unaligned_be32(lower_32_bits(p_lba), ++ &mpi_request->CDB.CDB32[2]); ++ else ++ put_unaligned_be64(p_lba, &mpi_request->CDB.CDB32[2]); ++ ++ mpt2sas_scsi_direct_io_set(ioc, smid, 1); ++} +diff --git a/drivers/scsi/mpt2sas/wrapper_mpt3sas_scsih.c b/drivers/scsi/mpt2sas/wrapper_mpt3sas_scsih.c +new file mode 100644 +index 0000000..7852050 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/wrapper_mpt3sas_scsih.c +@@ -0,0 +1,4 @@ ++#define MPT2SAS_SCSI ++/* This directive is used to create the mpt2sas driver from the mpt3sas sources */ ++ ++#include "mpt3sas_scsih.c" +diff --git a/drivers/scsi/mpt3sas/Kconfig b/drivers/scsi/mpt3sas/Kconfig +index 5743420..9aa67e2 100644 +--- a/drivers/scsi/mpt3sas/Kconfig ++++ b/drivers/scsi/mpt3sas/Kconfig +@@ -40,14 +40,6 @@ + # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + # USA. + +-config SCSI_MPT2SAS +- tristate "LSI MPT Fusion SAS 2.0 Device Driver" +- depends on PCI && SCSI +- select SCSI_SAS_ATTRS +- select RAID_ATTRS +- ---help--- +- This driver supports PCI-Express SAS 6Gb/s Host Adapters. +- + config SCSI_MPT3SAS + tristate "LSI MPT Fusion SAS 3.0 Device Driver" + depends on PCI && SCSI +@@ -56,18 +48,6 @@ config SCSI_MPT3SAS + ---help--- + This driver supports PCI-Express SAS 12Gb/s Host Adapters. + +-config SCSI_MPT2SAS_MAX_SGE +- int "LSI MPT Fusion SAS 2.0 Max number of SG Entries (16 - 256)" +- depends on PCI && SCSI && SCSI_MPT3SAS +- default "128" +- range 16 256 +- ---help--- +- This option allows you to specify the maximum number of scatter- +- gather entries per I/O. The driver default is 128, which matches +- MAX_PHYS_SEGMENTS in most kernels. However in SuSE kernels this +- can be 256. However, it may decreased down to 16. Decreasing this +- parameter will reduce memory requirements on a per controller instance. +- + config SCSI_MPT3SAS_MAX_SGE + int "LSI MPT Fusion SAS 3.0 Max number of SG Entries (16 - 256)" + depends on PCI && SCSI && SCSI_MPT3SAS +diff --git a/drivers/scsi/mpt3sas/Makefile b/drivers/scsi/mpt3sas/Makefile +index 0b90877..450b84b 100644 +--- a/drivers/scsi/mpt3sas/Makefile ++++ b/drivers/scsi/mpt3sas/Makefile +@@ -1,6 +1,5 @@ + # mpt2-3sas makefile + +-obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas.o + obj-$(CONFIG_SCSI_MPT3SAS) += mpt3sas.o + + obj-m += mpt3sas.o +@@ -12,11 +11,3 @@ mpt3sas-y += mpt3sas_base.o \ + mpt3sas_trigger_diag.o \ + mpt3sas_warpdrive.o + +-obj-m += mpt2sas.o +-mpt2sas-y += mpt3sas_base.o \ +- mpt3sas_config.o \ +- wrapper_mpt3sas_scsih.o \ +- mpt3sas_transport.o \ +- mpt3sas_ctl.o \ +- mpt3sas_trigger_diag.o \ +- mpt3sas_warpdrive.o +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/Fix-cacheinfo-compilation-issues-for-3.10.patch b/kernel-rt/centos/patches/Fix-cacheinfo-compilation-issues-for-3.10.patch new file mode 100644 index 00000000..23b6d17b --- /dev/null +++ b/kernel-rt/centos/patches/Fix-cacheinfo-compilation-issues-for-3.10.patch @@ -0,0 +1,114 @@ +From 8eea2ba32882bcbcaf10588c99f7fec0104e9854 Mon Sep 17 00:00:00 2001 +Message-Id: <8eea2ba32882bcbcaf10588c99f7fec0104e9854.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Alex Kozyrev +Date: Wed, 19 Jul 2017 02:25:15 -0500 +Subject: [PATCH 20/32] Fix cacheinfo compilation issues for 3.10 + +Had to revert commit 7cc277b489b4fe91f42eb596b282879c2d13152e: +"Install the callbacks via the state machine and let the core invoke +the callbacks on the already online CPUs. No functional change." +There is no hotplug state machine in 3.10 kernel. +Also implemented cpumap_print_to_pagebuf() function in place. + +Signed-off-by: Jim Somerville +--- + drivers/base/cacheinfo.c | 65 ++++++++++++++++++++++++++++++++++++------------ + 1 file changed, 49 insertions(+), 16 deletions(-) + +diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c +index eb3af27..c924f7e 100644 +--- a/drivers/base/cacheinfo.c ++++ b/drivers/base/cacheinfo.c +@@ -383,7 +383,12 @@ static ssize_t shared_cpumap_show_func(struct device *dev, bool list, char *buf) + struct cacheinfo *this_leaf = dev_get_drvdata(dev); + const struct cpumask *mask = &this_leaf->shared_cpu_map; + +- return cpumap_print_to_pagebuf(list, buf, mask); ++ int len = list? ++ cpulist_scnprintf(buf, PAGE_SIZE-2, mask) : ++ cpumask_scnprintf(buf, PAGE_SIZE-2, mask); ++ buf[len++] = '\n'; ++ buf[len] = '\0'; ++ return len; + } + + static ssize_t shared_cpu_map_show(struct device *dev, +@@ -633,30 +638,58 @@ err: + return rc; + } + +-static int cacheinfo_cpu_online(unsigned int cpu) ++static void cache_remove_dev(unsigned int cpu) + { +- int rc = detect_cache_attributes(cpu); ++ if (!cpumask_test_cpu(cpu, &cache_dev_map)) ++ return; ++ cpumask_clear_cpu(cpu, &cache_dev_map); + +- if (rc) +- return rc; +- rc = cache_add_dev(cpu); +- if (rc) +- free_cache_attributes(cpu); +- return rc; ++ cpu_cache_sysfs_exit(cpu); + } + +-static int cacheinfo_cpu_pre_down(unsigned int cpu) ++static int cacheinfo_cpu_callback(struct notifier_block *nfb, ++ unsigned long action, void *hcpu) + { +- if (cpumask_test_and_clear_cpu(cpu, &cache_dev_map)) +- cpu_cache_sysfs_exit(cpu); ++ unsigned int cpu = (unsigned long)hcpu; ++ int rc = 0; + +- free_cache_attributes(cpu); +- return 0; ++ switch (action & ~CPU_TASKS_FROZEN) { ++ case CPU_ONLINE: ++ rc = detect_cache_attributes(cpu); ++ if (!rc) ++ rc = cache_add_dev(cpu); ++ break; ++ case CPU_DEAD: ++ cache_remove_dev(cpu); ++ if (per_cpu_cacheinfo(cpu)) ++ free_cache_attributes(cpu); ++ break; ++ } ++ return notifier_from_errno(rc); + } + + static int __init cacheinfo_sysfs_init(void) + { +- return cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "base/cacheinfo:online", +- cacheinfo_cpu_online, cacheinfo_cpu_pre_down); ++ int cpu, rc = 0; ++ ++ cpu_notifier_register_begin(); ++ ++ for_each_online_cpu(cpu) { ++ rc = detect_cache_attributes(cpu); ++ if (rc) ++ goto out; ++ rc = cache_add_dev(cpu); ++ if (rc) { ++ free_cache_attributes(cpu); ++ pr_err("error populating cacheinfo..cpu%d\n", cpu); ++ goto out; ++ } ++ } ++ __hotcpu_notifier(cacheinfo_cpu_callback, 0); ++ ++out: ++ cpu_notifier_register_done(); ++ return rc; + } ++ + device_initcall(cacheinfo_sysfs_init); +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/Make-kernel-start-eth-devices-at-offset.patch b/kernel-rt/centos/patches/Make-kernel-start-eth-devices-at-offset.patch new file mode 100644 index 00000000..2f41a1f1 --- /dev/null +++ b/kernel-rt/centos/patches/Make-kernel-start-eth-devices-at-offset.patch @@ -0,0 +1,37 @@ +From 97a7d3c050d7996f6a630184fa428a4d170c2ea8 Mon Sep 17 00:00:00 2001 +Message-Id: <97a7d3c050d7996f6a630184fa428a4d170c2ea8.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Chris Friesen +Date: Thu, 12 May 2016 18:00:00 -0400 +Subject: [PATCH 09/32] Make kernel start eth devices at offset + +In order to avoid naming collisions, we want to make the kernel +start naming its "ethX" devices at eth1000 instead of eth0. This +will let us rename to a range starting at eth0. + +Signed-off-by: Jim Somerville +--- + net/core/dev.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/net/core/dev.c b/net/core/dev.c +index 1425f9d..db04f3b 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -1092,6 +1092,12 @@ static int __dev_alloc_name(struct net *net, const char *name, char *buf) + set_bit(i, inuse); + } + ++ /* WRS extension, want kernel to start at eth1000 */ ++ if (strcmp(name, "eth%d") == 0) { ++ for (i=0; i < 1000; i++) ++ set_bit(i, inuse); ++ } ++ + i = find_first_zero_bit(inuse, max_netdevices); + free_page((unsigned long) inuse); + } +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/Notification-of-death-of-arbitrary-processes.patch b/kernel-rt/centos/patches/Notification-of-death-of-arbitrary-processes.patch new file mode 100644 index 00000000..fd0e17b7 --- /dev/null +++ b/kernel-rt/centos/patches/Notification-of-death-of-arbitrary-processes.patch @@ -0,0 +1,539 @@ +From 81c2c9a73d99e3df9c96daf65fec42997428af3e Mon Sep 17 00:00:00 2001 +Message-Id: <81c2c9a73d99e3df9c96daf65fec42997428af3e.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Chris Friesen +Date: Thu, 7 Apr 2016 11:16:19 -0600 +Subject: [PATCH 11/32] Notification of death of arbitrary processes + +Note: this commit was copied from Titanium Cloud Rel2 + +This exposes a new feature which may be called to request +notification when an arbitrary process changes state. The +caller specifies a pid, signal number, and event mask, and +when that pid dies, or is stopped, or anything else that +would normally cause a SIGCHLD, the kernel will send the +specified signal to the caller if the event is in the event +mask originally passed down. The siginfo_t struct will +contain the same information as would be included with SIGCHLD. + +This is exposed to userspace via the prctl() call with the +PR_DO_NOTIFY_TASK_STATE option. + +Signed-off-by: Jim Somerville +--- + include/linux/init_task.h | 9 ++ + include/linux/sched.h | 6 ++ + include/uapi/linux/prctl.h | 18 ++++ + init/Kconfig | 15 +++ + kernel/Makefile | 1 + + kernel/death_notify.c | 227 +++++++++++++++++++++++++++++++++++++++++++++ + kernel/death_notify.h | 45 +++++++++ + kernel/exit.c | 6 ++ + kernel/fork.c | 4 + + kernel/signal.c | 11 +++ + kernel/sys.c | 9 ++ + 11 files changed, 351 insertions(+) + create mode 100644 kernel/death_notify.c + create mode 100644 kernel/death_notify.h + +diff --git a/include/linux/init_task.h b/include/linux/init_task.h +index d8c82e0..ba0c12e 100644 +--- a/include/linux/init_task.h ++++ b/include/linux/init_task.h +@@ -77,6 +77,14 @@ extern struct nsproxy init_nsproxy; + .signalfd_wqh = __WAIT_QUEUE_HEAD_INITIALIZER(sighand.signalfd_wqh), \ + } + ++#ifdef CONFIG_SIGEXIT ++#define INIT_SIGEXIT(tsk) \ ++ .notify = LIST_HEAD_INIT(tsk.notify), \ ++ .monitor = LIST_HEAD_INIT(tsk.monitor), ++#else ++#define INIT_SIGEXIT(tsk) ++#endif ++ + extern struct group_info init_groups; + + #define INIT_STRUCT_PID { \ +@@ -231,6 +239,7 @@ extern struct task_group root_task_group; + .alloc_lock = __SPIN_LOCK_UNLOCKED(tsk.alloc_lock), \ + .journal_info = NULL, \ + .cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), \ ++ INIT_SIGEXIT(tsk) \ + .pi_lock = __RAW_SPIN_LOCK_UNLOCKED(tsk.pi_lock), \ + .timer_slack_ns = 50000, /* 50 usec default slack */ \ + INIT_TIMER_LIST \ +diff --git a/include/linux/sched.h b/include/linux/sched.h +index f8294d9..ab9c0f1 100644 +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -1664,6 +1664,12 @@ struct task_struct { + short il_next; + short pref_node_fork; + #endif ++#ifdef CONFIG_SIGEXIT ++ /* list of processes to notify on death */ ++ struct list_head notify; ++ /* list of outstanding monitor requests */ ++ struct list_head monitor; ++#endif + #ifdef CONFIG_NUMA_BALANCING + int numa_scan_seq; + unsigned int numa_scan_period; +diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h +index 8ddaa82..53d6392 100644 +--- a/include/uapi/linux/prctl.h ++++ b/include/uapi/linux/prctl.h +@@ -55,6 +55,24 @@ + #define PR_SET_NAME 15 /* Set process name */ + #define PR_GET_NAME 16 /* Get process name */ + ++#ifdef CONFIG_SIGEXIT ++#define PR_DO_NOTIFY_TASK_STATE 17 /* Set/get notification for task ++ state changes */ ++ ++/* This is the data structure for requestion process death ++ * (and other state change) information. Sig of -1 means ++ * query, sig of 0 means deregistration, positive sig means ++ * that you want to set it. sig and events are value-result ++ * and will be updated with the previous values on every ++ * successful call. ++ */ ++struct task_state_notify_info { ++ pid_t pid; ++ int sig; ++ unsigned int events; ++}; ++#endif ++ + /* Get/set process endian */ + #define PR_GET_ENDIAN 19 + #define PR_SET_ENDIAN 20 +diff --git a/init/Kconfig b/init/Kconfig +index 1d645a1..37e48c0 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1539,6 +1539,21 @@ config VM_EVENT_COUNTERS + on EXPERT systems. /proc/vmstat will only show page counts + if VM event counters are disabled. + ++config SIGEXIT ++ bool "Notification of death of arbitrary processes" ++ default n ++ help ++ When enabled this exposes a new feature which may be called to request ++ notification when an arbitrary process changes state. The caller specifies ++ a pid, signal number, and event mask, and when that pid dies, or is ++ stopped, or anything else that would normally cause a SIGCHLD, the ++ kernel will send the specified signal to the caller if the event is in ++ the event mask originally passed down. The siginfo_t struct will ++ contain the same information as would be included with SIGCHLD. ++ ++ This is exposed to userspace via the prctl() ++ call with the PR_DO_NOTIFY_TASK_STATE option ++ + config SLUB_DEBUG + default y + bool "Enable SLUB debugging support" if EXPERT +diff --git a/kernel/Makefile b/kernel/Makefile +index 762218c..d357e7d 100644 +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -117,6 +117,7 @@ obj-$(CONFIG_RING_BUFFER) += trace/ + obj-$(CONFIG_TRACEPOINTS) += trace/ + obj-$(CONFIG_IRQ_WORK) += irq_work.o + obj-$(CONFIG_CPU_PM) += cpu_pm.o ++obj-$(CONFIG_SIGEXIT) += death_notify.o + + obj-$(CONFIG_PERF_EVENTS) += events/ + +diff --git a/kernel/death_notify.c b/kernel/death_notify.c +new file mode 100644 +index 0000000..5eb8bfc +--- /dev/null ++++ b/kernel/death_notify.c +@@ -0,0 +1,227 @@ ++/* ++ * kernel/death_notify.c, Process death notification support ++ * ++ * Copyright (c) 2006-2014 Wind River Systems, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "death_notify.h" ++ ++static void unlink_status_notifier(struct signotifier *n) ++{ ++ list_del(&n->monitor_list); ++ list_del(&n->notify_list); ++ kfree(n); ++} ++ ++static void handle_already_monitoring(struct signotifier *node, ++ struct task_state_notify_info *args, ++ struct task_state_notify_info *oldargs) ++{ ++ /* Store the old values */ ++ oldargs->sig = node->sig; ++ oldargs->events = node->events; ++ ++ /* We know that args->sig is 0 or a valid signal. */ ++ if (args->sig > 0) { ++ /* Update the new values */ ++ node->sig = args->sig; ++ node->events = args->events; ++ } else if (!args->sig) { ++ /* args->sig of 0 means to deregister */ ++ unlink_status_notifier(node); ++ } ++} ++ ++static void setup_new_node(struct task_struct *p, ++ struct signotifier *node, ++ struct task_state_notify_info *args) ++{ ++ node->notify_tsk = current; ++ node->sig = args->sig; ++ node->events = args->events; ++ ++ /* Add this node to the list of notification requests ++ * for the specified process. ++ */ ++ list_add_tail(&node->notify_list, &p->notify); ++ ++ /* Also add this node to the list of monitor requests ++ * for the current process. ++ */ ++ list_add_tail(&node->monitor_list, ¤t->monitor); ++} ++ ++ ++/* Returns 0 if arguments are valid, 1 if they are not. */ ++static int invalid_args(struct task_state_notify_info *args) ++{ ++ int ret = 1; ++ ++ if (args->pid <= 0) ++ goto out; ++ ++ /* Sig of -1 implies query, sig of 0 implies deregistration. ++ * Otherwise sig must be positive and within range. ++ */ ++ if ((args->sig < -1) || (args->sig > _NSIG)) ++ goto out; ++ ++ /* If positive sig, must have valid events. */ ++ if (args->sig > 0) { ++ if (!args->events || (args->events >= (1 << (NSIGCHLD+1)))) ++ goto out; ++ } ++ ++ ret = 0; ++out: ++ return ret; ++} ++ ++/* Notify those registered for process state updates via do_notify_task_state(). ++ * If "del" is nonzero, the process is dying and we want to free ++ * the nodes in the list as we go. ++ * ++ * Note: we only notify processes for events in which they have registered ++ * interest. ++ * ++ * Must be called holding a lock on tasklist_lock. ++ */ ++void do_notify_others(struct task_struct *tsk, struct siginfo *info) ++{ ++ struct signotifier *node; ++ unsigned int events; ++ ++ /* This method of generating the event bit must be ++ * matched in the userspace library. ++ */ ++ events = 1 << (info->si_code & 0xFF); ++ ++ list_for_each_entry(node, &tsk->notify, notify_list) { ++ if (events & node->events) { ++ info->si_signo = node->sig; ++ group_send_sig_info(node->sig, info, node->notify_tsk); ++ } ++ } ++} ++ ++void release_notify_others(struct task_struct *p) ++{ ++ struct signotifier *n, *t; ++ ++ /* Need to clean up any outstanding requests where we ++ * wanted to be notified when others died. ++ */ ++ list_for_each_entry_safe(n, t, &p->monitor, monitor_list) { ++ unlink_status_notifier(n); ++ } ++ ++ /* Also need to clean up any outstanding requests where others ++ * wanted to be notified when we died. ++ */ ++ list_for_each_entry_safe(n, t, &p->notify, notify_list) { ++ unlink_status_notifier(n); ++ } ++} ++ ++/* If the config is defined, then processes can call this routine ++ * to request notification when the specified task's state changes. ++ * On the death (or other state change) of the specified process, ++ * we will send them the specified signal if the event is listed ++ * in their event bitfield. ++ * ++ * A sig of 0 means that we want to deregister. ++ * ++ * The sig/events fields are value/result. On success we update them ++ * to reflect what they were before the call. ++ * ++ * Returns error code on error, on success we return 0. ++ */ ++int do_notify_task_state(unsigned long arg) ++{ ++ int err; ++ struct task_struct *p; ++ struct signotifier *node, *tmp; ++ struct task_state_notify_info args, oldargs; ++ ++ if (copy_from_user(&args, (struct task_state_notify_info __user *)arg, ++ sizeof(args))) ++ return -EFAULT; ++ oldargs.pid = args.pid; ++ ++ /* Validate the arguments passed in. */ ++ err = -EINVAL; ++ if (invalid_args(&args)) ++ goto out; ++ ++ /* We must hold a write lock on tasklist_lock to add the notification ++ * later on, and we need some lock on tasklist_lock for ++ * find_task_by_pid(), so may as well take the write lock now. ++ * Must use write_lock_irq(). ++ */ ++ write_lock_irq(&tasklist_lock); ++ ++ err = -ESRCH; ++ p = find_task_by_vpid(args.pid); ++ if (!p) ++ goto unlock_out; ++ ++ /* Now we know pid exists, unlikely to fail. */ ++ err = 0; ++ ++ /* Check if we're already monitoring the specified pid. If so, update ++ * the monitoring parameters and return the old ones. ++ */ ++ list_for_each_entry(tmp, &p->notify, notify_list) { ++ if (tmp->notify_tsk == current) { ++ handle_already_monitoring(tmp, &args, &oldargs); ++ goto unlock_out; ++ } ++ } ++ ++ /* If we get here, we're not currently monitoring the process. */ ++ oldargs.sig = 0; ++ oldargs.events = 0; ++ ++ /* If we wanted to set up a new monitor, do it now. If we didn't ++ * manage to allocate memory for the new node, then we return ++ * an appropriate error. ++ */ ++ if (args.sig > 0) { ++ node = kmalloc(sizeof(*node), GFP_ATOMIC); ++ if (node) ++ setup_new_node(p, node, &args); ++ else ++ err = -ENOMEM; ++ } ++ ++unlock_out: ++ write_unlock_irq(&tasklist_lock); ++ ++ /* Copy the old values back to caller. */ ++ if (copy_to_user((struct task_state_notify_info __user *)arg, ++ &oldargs, sizeof(oldargs))) ++ err = -EFAULT; ++ ++out: ++ return err; ++} +diff --git a/kernel/death_notify.h b/kernel/death_notify.h +new file mode 100644 +index 0000000..b2b8e8c +--- /dev/null ++++ b/kernel/death_notify.h +@@ -0,0 +1,45 @@ ++/* ++ * kernel/death_notify.h, Process death notification support ++ * ++ * Copyright (c) 2006-2014 Wind River Systems, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++#ifndef _KERNEL_DEATH_NOTIFY_H ++#define _KERNEL_DEATH_NOTIFY_H ++ ++#ifdef CONFIG_SIGEXIT ++ ++struct signotifier { ++ struct task_struct *notify_tsk; ++ struct list_head notify_list; ++ struct list_head monitor_list; ++ int sig; ++ unsigned int events; ++}; ++ ++extern int do_notify_task_state(unsigned long arg); ++extern void do_notify_others(struct task_struct *tsk, ++ struct siginfo *info); ++extern void release_notify_others(struct task_struct *p); ++ ++#else /* !CONFIG_SIGEXIT */ ++ ++static inline void do_notify_others(struct task_struct *tsk, ++ struct siginfo *info) {} ++static inline void release_notify_others(struct task_struct *p) {} ++ ++#endif /* CONFIG_SIGEXIT */ ++#endif +diff --git a/kernel/exit.c b/kernel/exit.c +index 8a908ea..448a3c3 100644 +--- a/kernel/exit.c ++++ b/kernel/exit.c +@@ -59,6 +59,9 @@ + #include + #include + #include ++#ifdef CONFIG_SIGEXIT ++#include "death_notify.h" ++#endif + + static void exit_mm(struct task_struct * tsk); + +@@ -184,6 +187,9 @@ repeat: + proc_flush_task(p); + + tasklist_write_lock_irq(); ++#ifdef CONFIG_SIGEXIT ++ release_notify_others(p); ++#endif + ptrace_release_task(p); + __exit_signal(p); + +diff --git a/kernel/fork.c b/kernel/fork.c +index c390b02..6340c77 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -1512,6 +1512,10 @@ static struct task_struct *copy_process(unsigned long clone_flags, + p->sequential_io = 0; + p->sequential_io_avg = 0; + #endif ++#ifdef CONFIG_SIGEXIT ++ INIT_LIST_HEAD(&p->notify); ++ INIT_LIST_HEAD(&p->monitor); ++#endif + + /* Perform scheduler related setup. Assign this task to a CPU. */ + retval = sched_fork(clone_flags, p); +diff --git a/kernel/signal.c b/kernel/signal.c +index f1ecca9..daaa6ab 100644 +--- a/kernel/signal.c ++++ b/kernel/signal.c +@@ -47,6 +47,9 @@ + #include + #include + #include "audit.h" /* audit_signal_info() */ ++#ifdef CONFIG_SIGEXIT ++#include "death_notify.h" ++#endif + + /* + * SLAB caches for signal bits. +@@ -1849,6 +1852,10 @@ bool do_notify_parent(struct task_struct *tsk, int sig) + __wake_up_parent(tsk, tsk->parent); + spin_unlock_irqrestore(&psig->siglock, flags); + ++#ifdef CONFIG_SIGEXIT ++ do_notify_others(tsk, &info); ++#endif ++ + return autoreap; + } + +@@ -1920,6 +1927,10 @@ static void do_notify_parent_cldstop(struct task_struct *tsk, + */ + __wake_up_parent(tsk, parent); + spin_unlock_irqrestore(&sighand->siglock, flags); ++ ++#ifdef CONFIG_SIGEXIT ++ do_notify_others(tsk, &info); ++#endif + } + + static inline int may_ptrace_stop(void) +diff --git a/kernel/sys.c b/kernel/sys.c +index 9c2a4ed..54538e3 100644 +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -64,6 +64,10 @@ + #include + #include + ++#ifdef CONFIG_SIGEXIT ++#include "death_notify.h" ++#endif ++ + #ifndef SET_UNALIGN_CTL + # define SET_UNALIGN_CTL(a,b) (-EINVAL) + #endif +@@ -2474,6 +2478,11 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, + else + error = PR_MCE_KILL_DEFAULT; + break; ++#ifdef CONFIG_SIGEXIT ++ case PR_DO_NOTIFY_TASK_STATE: ++ error = do_notify_task_state(arg2); ++ break; ++#endif + case PR_SET_MM: + error = prctl_set_mm(arg2, arg3, arg4, arg5); + break; +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/PCI-Add-ACS-quirk-for-Intel-Fortville-NICs.patch b/kernel-rt/centos/patches/PCI-Add-ACS-quirk-for-Intel-Fortville-NICs.patch new file mode 100644 index 00000000..22e0fec6 --- /dev/null +++ b/kernel-rt/centos/patches/PCI-Add-ACS-quirk-for-Intel-Fortville-NICs.patch @@ -0,0 +1,34 @@ +From a0da95b0152227a9a80d98edf5fc0af345479dce Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Dahir Osman +Date: Wed, 13 Jan 2016 10:01:11 -0500 +Subject: [PATCH 12/32] PCI: Add ACS quirk for Intel Fortville NICs + +Use quirks to determine isolation for now until a later kernel can +properly read the Fortville ACS capabilities. + +Signed-off-by: Jim Somerville +--- + drivers/pci/quirks.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c +index 5614e3f..4a0bfed 100644 +--- a/drivers/pci/quirks.c ++++ b/drivers/pci/quirks.c +@@ -4289,6 +4289,10 @@ static const struct pci_dev_acs_enabled { + /* I219 */ + { PCI_VENDOR_ID_INTEL, 0x15b7, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x15b8, pci_quirk_mf_endpoint_acs }, ++ /* I40 */ ++ { PCI_VENDOR_ID_INTEL, 0x1572, pci_quirk_mf_endpoint_acs }, ++ { PCI_VENDOR_ID_INTEL, 0x1586, pci_quirk_mf_endpoint_acs }, ++ { PCI_VENDOR_ID_INTEL, 0x1583, pci_quirk_mf_endpoint_acs }, + /* Intel PCH root ports */ + { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs }, + { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_spt_pch_acs }, +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/Porting-Cacheinfo-from-Kernel-4.10.17.patch b/kernel-rt/centos/patches/Porting-Cacheinfo-from-Kernel-4.10.17.patch new file mode 100644 index 00000000..6b76290a --- /dev/null +++ b/kernel-rt/centos/patches/Porting-Cacheinfo-from-Kernel-4.10.17.patch @@ -0,0 +1,2124 @@ +From 3b5212f6cf0d7746dd48324fa99030f49073ca8c Mon Sep 17 00:00:00 2001 +Message-Id: <3b5212f6cf0d7746dd48324fa99030f49073ca8c.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Alex Kozyrev +Date: Wed, 19 Jul 2017 02:21:59 -0500 +Subject: [PATCH 19/32] Porting Cacheinfo from Kernel 4.10.17 + +Original source code from tag v4.10.17 in Linux stable tree for: +intel_cacheinfo.c, cacheinfo.c and cacheinfo.h. +Main commit that we are interested for is 246246cbde5e840012f853e27630ebb59f409486: +This patch adds initial support for providing processor cache information +to userspace through sysfs interface. This is based on already existing +implementations(x86, ia64, s390 and powerpc) and hence the interface is +intended to be fully compatible. + +The main purpose of this generic support is to avoid further code +duplication to support new architectures and also to unify all the existing +different implementations. + +This implementation maintains the hierarchy of cache objects which reflects +the system's cache topology. Cache devices are instantiated as needed as +CPUs come online. The cache information is replicated per-cpu even if they are +shared. A per-cpu array of cache information maintained is used mainly for +sysfs-related book keeping. + +It also implements the shared_cpu_map attribute, which is essential for +enabling both kernel and user-space to discover the system's overall cache +topology. + +This patch also add the missing ABI documentation for the cacheinfo sysfs +interface already, which is well defined and widely used. + +sysfs-devices-system-cpu was nodified by taking commit 1d78dc59f5ab6f467e49882518453adc7e4caa44: +Add an ABI document entry for /sys/devices/system/cpu/cpu*/cache/index*/id. + +cpu.h and cpu.c was enhanced with commit 3d52943b3a51497a777e6d7d840a38596a92cee9: +This patch adds a new function to create per-cpu devices. +This helps in: +1. reusing the device infrastructure to create any cpu related + attributes and corresponding sysfs instead of creating and + dealing with raw kobjects directly +2. retaining the legacy path(/sys/devices/system/cpu/..) to support + existing sysfs ABI +3. avoiding to create links in the bus directory pointing to the + device as there would be per-cpu instance of these devices with + the same name since dev->bus is not populated to cpu_sysbus on + purpose + +Signed-off-by: Jim Somerville +--- + Documentation/ABI/testing/sysfs-devices-system-cpu | 65 ++ + arch/x86/kernel/cpu/intel_cacheinfo.c | 830 +++++++-------------- + drivers/base/Makefile | 2 +- + drivers/base/cacheinfo.c | 662 ++++++++++++++++ + drivers/base/cpu.c | 54 ++ + include/linux/cacheinfo.h | 104 +++ + include/linux/cpu.h | 3 + + 7 files changed, 1147 insertions(+), 573 deletions(-) + create mode 100644 drivers/base/cacheinfo.c + create mode 100644 include/linux/cacheinfo.h + +diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu +index 55338e6..eaf8da1 100644 +--- a/Documentation/ABI/testing/sysfs-devices-system-cpu ++++ b/Documentation/ABI/testing/sysfs-devices-system-cpu +@@ -201,6 +201,71 @@ Description: address and size of the percpu note. + + crash_notes_size: size of the note of cpu#. + ++ ++What: /sys/devices/system/cpu/cpu*/cache/index*/ ++Date: July 2014(documented, existed before August 2008) ++Contact: Sudeep Holla ++ Linux kernel mailing list ++Description: Parameters for the CPU cache attributes ++ ++ allocation_policy: ++ - WriteAllocate: allocate a memory location to a cache line ++ on a cache miss because of a write ++ - ReadAllocate: allocate a memory location to a cache line ++ on a cache miss because of a read ++ - ReadWriteAllocate: both writeallocate and readallocate ++ ++ attributes: LEGACY used only on IA64 and is same as write_policy ++ ++ coherency_line_size: the minimum amount of data in bytes that gets ++ transferred from memory to cache ++ ++ level: the cache hierarchy in the multi-level cache configuration ++ ++ number_of_sets: total number of sets in the cache, a set is a ++ collection of cache lines with the same cache index ++ ++ physical_line_partition: number of physical cache line per cache tag ++ ++ shared_cpu_list: the list of logical cpus sharing the cache ++ ++ shared_cpu_map: logical cpu mask containing the list of cpus sharing ++ the cache ++ ++ size: the total cache size in kB ++ ++ type: ++ - Instruction: cache that only holds instructions ++ - Data: cache that only caches data ++ - Unified: cache that holds both data and instructions ++ ++ ways_of_associativity: degree of freedom in placing a particular block ++ of memory in the cache ++ ++ write_policy: ++ - WriteThrough: data is written to both the cache line ++ and to the block in the lower-level memory ++ - WriteBack: data is written only to the cache line and ++ the modified cache line is written to main ++ memory only when it is replaced ++ ++ ++What: /sys/devices/system/cpu/cpu*/cache/index*/id ++Date: September 2016 ++Contact: Linux kernel mailing list ++Description: Cache id ++ ++ The id provides a unique number for a specific instance of ++ a cache of a particular type. E.g. there may be a level ++ 3 unified cache on each socket in a server and we may ++ assign them ids 0, 1, 2, ... ++ ++ Note that id value can be non-contiguous. E.g. level 1 ++ caches typically exist per core, but there may not be a ++ power of two cores on a socket, so these caches may be ++ numbered 0, 1, 2, 3, 4, 5, 8, 9, 10, ... ++ ++ + What: /sys/devices/system/cpu/cpuX/cpufreq/throttle_stats + /sys/devices/system/cpu/cpuX/cpufreq/throttle_stats/turbo_stat + /sys/devices/system/cpu/cpuX/cpufreq/throttle_stats/sub_turbo_stat +diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c +index d529019..bf23bd2 100644 +--- a/arch/x86/kernel/cpu/intel_cacheinfo.c ++++ b/arch/x86/kernel/cpu/intel_cacheinfo.c +@@ -1,5 +1,5 @@ + /* +- * Routines to indentify caches on Intel CPU. ++ * Routines to identify caches on Intel CPU. + * + * Changes: + * Venkatesh Pallipadi : Adding cache identification through cpuid(4) +@@ -7,16 +7,14 @@ + * Andi Kleen / Andreas Herrmann : CPUID4 emulation on AMD. + */ + +-#include + #include +-#include +-#include ++#include + #include + #include ++#include + #include + + #include +-#include + #include + #include + +@@ -116,10 +114,10 @@ static const struct _cache_table cache_table[] = + + + enum _cache_type { +- CACHE_TYPE_NULL = 0, +- CACHE_TYPE_DATA = 1, +- CACHE_TYPE_INST = 2, +- CACHE_TYPE_UNIFIED = 3 ++ CTYPE_NULL = 0, ++ CTYPE_DATA = 1, ++ CTYPE_INST = 2, ++ CTYPE_UNIFIED = 3 + }; + + union _cpuid4_leaf_eax { +@@ -160,12 +158,7 @@ struct _cpuid4_info_regs { + struct amd_northbridge *nb; + }; + +-struct _cpuid4_info { +- struct _cpuid4_info_regs base; +- DECLARE_BITMAP(shared_cpu_map, NR_CPUS); +-}; +- +-unsigned short num_cache_leaves; ++static unsigned short num_cache_leaves; + + /* AMD doesn't have CPUID4. Emulate it here to report the same + information to the user. This makes some assumptions about the machine: +@@ -221,6 +214,13 @@ static const unsigned short assocs[] = { + static const unsigned char levels[] = { 1, 1, 2, 3 }; + static const unsigned char types[] = { 1, 2, 3, 3 }; + ++static const enum cache_type cache_type_map[] = { ++ [CTYPE_NULL] = CACHE_TYPE_NOCACHE, ++ [CTYPE_DATA] = CACHE_TYPE_DATA, ++ [CTYPE_INST] = CACHE_TYPE_INST, ++ [CTYPE_UNIFIED] = CACHE_TYPE_UNIFIED, ++}; ++ + static void + amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax, + union _cpuid4_leaf_ebx *ebx, +@@ -292,14 +292,8 @@ amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax, + (ebx->split.ways_of_associativity + 1) - 1; + } + +-struct _cache_attr { +- struct attribute attr; +- ssize_t (*show)(struct _cpuid4_info *, char *, unsigned int); +- ssize_t (*store)(struct _cpuid4_info *, const char *, size_t count, +- unsigned int); +-}; +- + #if defined(CONFIG_AMD_NB) && defined(CONFIG_SYSFS) ++ + /* + * L3 cache descriptors + */ +@@ -326,20 +320,6 @@ static void amd_calc_l3_indices(struct amd_northbridge *nb) + l3->indices = (max(max3(sc0, sc1, sc2), sc3) << 10) - 1; + } + +-static void amd_init_l3_cache(struct _cpuid4_info_regs *this_leaf, int index) +-{ +- int node; +- +- /* only for L3, and not in virtualized environments */ +- if (index < 3) +- return; +- +- node = amd_get_nb_id(smp_processor_id()); +- this_leaf->nb = node_to_amd_nb(node); +- if (this_leaf->nb && !this_leaf->nb->l3_cache.indices) +- amd_calc_l3_indices(this_leaf->nb); +-} +- + /* + * check whether a slot used for disabling an L3 index is occupied. + * @l3: L3 cache descriptor +@@ -347,7 +327,7 @@ static void amd_init_l3_cache(struct _cpuid4_info_regs *this_leaf, int index) + * + * @returns: the disabled index if used or negative value if slot free. + */ +-int amd_get_l3_disable_slot(struct amd_northbridge *nb, unsigned slot) ++static int amd_get_l3_disable_slot(struct amd_northbridge *nb, unsigned slot) + { + unsigned int reg = 0; + +@@ -360,15 +340,13 @@ int amd_get_l3_disable_slot(struct amd_northbridge *nb, unsigned slot) + return -1; + } + +-static ssize_t show_cache_disable(struct _cpuid4_info *this_leaf, char *buf, ++static ssize_t show_cache_disable(struct cacheinfo *this_leaf, char *buf, + unsigned int slot) + { + int index; ++ struct amd_northbridge *nb = this_leaf->priv; + +- if (!this_leaf->base.nb || !amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) +- return -EINVAL; +- +- index = amd_get_l3_disable_slot(this_leaf->base.nb, slot); ++ index = amd_get_l3_disable_slot(nb, slot); + if (index >= 0) + return sprintf(buf, "%d\n", index); + +@@ -377,9 +355,10 @@ static ssize_t show_cache_disable(struct _cpuid4_info *this_leaf, char *buf, + + #define SHOW_CACHE_DISABLE(slot) \ + static ssize_t \ +-show_cache_disable_##slot(struct _cpuid4_info *this_leaf, char *buf, \ +- unsigned int cpu) \ ++cache_disable_##slot##_show(struct device *dev, \ ++ struct device_attribute *attr, char *buf) \ + { \ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); \ + return show_cache_disable(this_leaf, buf, slot); \ + } + SHOW_CACHE_DISABLE(0) +@@ -425,8 +404,8 @@ static void amd_l3_disable_index(struct amd_northbridge *nb, int cpu, + * + * @return: 0 on success, error status on failure + */ +-int amd_set_l3_disable_slot(struct amd_northbridge *nb, int cpu, unsigned slot, +- unsigned long index) ++static int amd_set_l3_disable_slot(struct amd_northbridge *nb, int cpu, ++ unsigned slot, unsigned long index) + { + int ret = 0; + +@@ -447,28 +426,26 @@ int amd_set_l3_disable_slot(struct amd_northbridge *nb, int cpu, unsigned slot, + return 0; + } + +-static ssize_t store_cache_disable(struct _cpuid4_info *this_leaf, +- const char *buf, size_t count, +- unsigned int slot) ++static ssize_t store_cache_disable(struct cacheinfo *this_leaf, ++ const char *buf, size_t count, ++ unsigned int slot) + { + unsigned long val = 0; + int cpu, err = 0; ++ struct amd_northbridge *nb = this_leaf->priv; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + +- if (!this_leaf->base.nb || !amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) +- return -EINVAL; +- +- cpu = cpumask_first(to_cpumask(this_leaf->shared_cpu_map)); ++ cpu = cpumask_first(&this_leaf->shared_cpu_map); + + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + +- err = amd_set_l3_disable_slot(this_leaf->base.nb, cpu, slot, val); ++ err = amd_set_l3_disable_slot(nb, cpu, slot, val); + if (err) { + if (err == -EEXIST) +- pr_warning("L3 slot %d in use/index already disabled!\n", ++ pr_warn("L3 slot %d in use/index already disabled!\n", + slot); + return err; + } +@@ -477,41 +454,36 @@ static ssize_t store_cache_disable(struct _cpuid4_info *this_leaf, + + #define STORE_CACHE_DISABLE(slot) \ + static ssize_t \ +-store_cache_disable_##slot(struct _cpuid4_info *this_leaf, \ +- const char *buf, size_t count, \ +- unsigned int cpu) \ ++cache_disable_##slot##_store(struct device *dev, \ ++ struct device_attribute *attr, \ ++ const char *buf, size_t count) \ + { \ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); \ + return store_cache_disable(this_leaf, buf, count, slot); \ + } + STORE_CACHE_DISABLE(0) + STORE_CACHE_DISABLE(1) + +-static struct _cache_attr cache_disable_0 = __ATTR(cache_disable_0, 0644, +- show_cache_disable_0, store_cache_disable_0); +-static struct _cache_attr cache_disable_1 = __ATTR(cache_disable_1, 0644, +- show_cache_disable_1, store_cache_disable_1); +- +-static ssize_t +-show_subcaches(struct _cpuid4_info *this_leaf, char *buf, unsigned int cpu) ++static ssize_t subcaches_show(struct device *dev, ++ struct device_attribute *attr, char *buf) + { +- if (!this_leaf->base.nb || !amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) +- return -EINVAL; ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ int cpu = cpumask_first(&this_leaf->shared_cpu_map); + + return sprintf(buf, "%x\n", amd_get_subcaches(cpu)); + } + +-static ssize_t +-store_subcaches(struct _cpuid4_info *this_leaf, const char *buf, size_t count, +- unsigned int cpu) ++static ssize_t subcaches_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) + { ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ int cpu = cpumask_first(&this_leaf->shared_cpu_map); + unsigned long val; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + +- if (!this_leaf->base.nb || !amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) +- return -EINVAL; +- + if (kstrtoul(buf, 16, &val) < 0) + return -EINVAL; + +@@ -521,9 +493,92 @@ store_subcaches(struct _cpuid4_info *this_leaf, const char *buf, size_t count, + return count; + } + +-static struct _cache_attr subcaches = +- __ATTR(subcaches, 0644, show_subcaches, store_subcaches); ++static DEVICE_ATTR_RW(cache_disable_0); ++static DEVICE_ATTR_RW(cache_disable_1); ++static DEVICE_ATTR_RW(subcaches); ++ ++static umode_t ++cache_private_attrs_is_visible(struct kobject *kobj, ++ struct attribute *attr, int unused) ++{ ++ struct device *dev = kobj_to_dev(kobj); ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ umode_t mode = attr->mode; ++ ++ if (!this_leaf->priv) ++ return 0; ++ ++ if ((attr == &dev_attr_subcaches.attr) && ++ amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) ++ return mode; ++ ++ if ((attr == &dev_attr_cache_disable_0.attr || ++ attr == &dev_attr_cache_disable_1.attr) && ++ amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) ++ return mode; ++ ++ return 0; ++} ++ ++static struct attribute_group cache_private_group = { ++ .is_visible = cache_private_attrs_is_visible, ++}; ++ ++static void init_amd_l3_attrs(void) ++{ ++ int n = 1; ++ static struct attribute **amd_l3_attrs; ++ ++ if (amd_l3_attrs) /* already initialized */ ++ return; ++ ++ if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) ++ n += 2; ++ if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) ++ n += 1; ++ ++ amd_l3_attrs = kcalloc(n, sizeof(*amd_l3_attrs), GFP_KERNEL); ++ if (!amd_l3_attrs) ++ return; ++ ++ n = 0; ++ if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) { ++ amd_l3_attrs[n++] = &dev_attr_cache_disable_0.attr; ++ amd_l3_attrs[n++] = &dev_attr_cache_disable_1.attr; ++ } ++ if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) ++ amd_l3_attrs[n++] = &dev_attr_subcaches.attr; ++ ++ cache_private_group.attrs = amd_l3_attrs; ++} ++ ++const struct attribute_group * ++cache_get_priv_group(struct cacheinfo *this_leaf) ++{ ++ struct amd_northbridge *nb = this_leaf->priv; ++ ++ if (this_leaf->level < 3 || !nb) ++ return NULL; ++ ++ if (nb && nb->l3_cache.indices) ++ init_amd_l3_attrs(); ++ ++ return &cache_private_group; ++} ++ ++static void amd_init_l3_cache(struct _cpuid4_info_regs *this_leaf, int index) ++{ ++ int node; ++ ++ /* only for L3, and not in virtualized environments */ ++ if (index < 3) ++ return; + ++ node = amd_get_nb_id(smp_processor_id()); ++ this_leaf->nb = node_to_amd_nb(node); ++ if (this_leaf->nb && !this_leaf->nb->l3_cache.indices) ++ amd_calc_l3_indices(this_leaf->nb); ++} + #else + #define amd_init_l3_cache(x, y) + #endif /* CONFIG_AMD_NB && CONFIG_SYSFS */ +@@ -537,7 +592,7 @@ cpuid4_cache_lookup_regs(int index, struct _cpuid4_info_regs *this_leaf) + unsigned edx; + + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) { +- if (cpu_has_topoext) ++ if (boot_cpu_has(X86_FEATURE_TOPOEXT)) + cpuid_count(0x8000001d, index, &eax.full, + &ebx.full, &ecx.full, &edx); + else +@@ -547,7 +602,7 @@ cpuid4_cache_lookup_regs(int index, struct _cpuid4_info_regs *this_leaf) + cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &edx); + } + +- if (eax.split.type == CACHE_TYPE_NULL) ++ if (eax.split.type == CTYPE_NULL) + return -EIO; /* better error ? */ + + this_leaf->eax = eax; +@@ -576,14 +631,14 @@ static int find_num_cache_leaves(struct cpuinfo_x86 *c) + /* Do cpuid(op) loop to find out num_cache_leaves */ + cpuid_count(op, i, &eax, &ebx, &ecx, &edx); + cache_eax.full = eax; +- } while (cache_eax.split.type != CACHE_TYPE_NULL); ++ } while (cache_eax.split.type != CTYPE_NULL); + return i; + } + + void init_amd_cacheinfo(struct cpuinfo_x86 *c) + { + +- if (cpu_has_topoext) { ++ if (boot_cpu_has(X86_FEATURE_TOPOEXT)) { + num_cache_leaves = find_num_cache_leaves(c); + } else if (c->extended_cpuid_level >= 0x80000006) { + if (cpuid_edx(0x80000006) & 0xf000) +@@ -600,7 +655,7 @@ unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c) + unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */ + unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */ + unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb; +-#ifdef CONFIG_X86_HT ++#ifdef CONFIG_SMP + unsigned int cpu = c->cpu_index; + #endif + +@@ -618,36 +673,34 @@ unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c) + * parameters cpuid leaf to find the cache details + */ + for (i = 0; i < num_cache_leaves; i++) { +- struct _cpuid4_info_regs this_leaf; ++ struct _cpuid4_info_regs this_leaf = {}; + int retval; + + retval = cpuid4_cache_lookup_regs(i, &this_leaf); +- if (retval >= 0) { +- switch (this_leaf.eax.split.level) { +- case 1: +- if (this_leaf.eax.split.type == +- CACHE_TYPE_DATA) +- new_l1d = this_leaf.size/1024; +- else if (this_leaf.eax.split.type == +- CACHE_TYPE_INST) +- new_l1i = this_leaf.size/1024; +- break; +- case 2: +- new_l2 = this_leaf.size/1024; +- num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing; +- index_msb = get_count_order(num_threads_sharing); +- l2_id = c->apicid & ~((1 << index_msb) - 1); +- break; +- case 3: +- new_l3 = this_leaf.size/1024; +- num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing; +- index_msb = get_count_order( +- num_threads_sharing); +- l3_id = c->apicid & ~((1 << index_msb) - 1); +- break; +- default: +- break; +- } ++ if (retval < 0) ++ continue; ++ ++ switch (this_leaf.eax.split.level) { ++ case 1: ++ if (this_leaf.eax.split.type == CTYPE_DATA) ++ new_l1d = this_leaf.size/1024; ++ else if (this_leaf.eax.split.type == CTYPE_INST) ++ new_l1i = this_leaf.size/1024; ++ break; ++ case 2: ++ new_l2 = this_leaf.size/1024; ++ num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing; ++ index_msb = get_count_order(num_threads_sharing); ++ l2_id = c->apicid & ~((1 << index_msb) - 1); ++ break; ++ case 3: ++ new_l3 = this_leaf.size/1024; ++ num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing; ++ index_msb = get_count_order(num_threads_sharing); ++ l3_id = c->apicid & ~((1 << index_msb) - 1); ++ break; ++ default: ++ break; + } + } + } +@@ -721,34 +774,40 @@ unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c) + + if (new_l2) { + l2 = new_l2; +-#ifdef CONFIG_X86_HT ++#ifdef CONFIG_SMP + per_cpu(cpu_llc_id, cpu) = l2_id; + #endif + } + + if (new_l3) { + l3 = new_l3; +-#ifdef CONFIG_X86_HT ++#ifdef CONFIG_SMP + per_cpu(cpu_llc_id, cpu) = l3_id; + #endif + } + ++#ifdef CONFIG_SMP ++ /* ++ * If cpu_llc_id is not yet set, this means cpuid_level < 4 which in ++ * turns means that the only possibility is SMT (as indicated in ++ * cpuid1). Since cpuid2 doesn't specify shared caches, and we know ++ * that SMT shares all caches, we can unconditionally set cpu_llc_id to ++ * c->phys_proc_id. ++ */ ++ if (per_cpu(cpu_llc_id, cpu) == BAD_APICID) ++ per_cpu(cpu_llc_id, cpu) = c->phys_proc_id; ++#endif ++ + c->x86_cache_size = l3 ? l3 : (l2 ? l2 : (l1i+l1d)); + + return l2; + } + +-#ifdef CONFIG_SYSFS +- +-/* pointer to _cpuid4_info array (for each cache leaf) */ +-static DEFINE_PER_CPU(struct _cpuid4_info *, ici_cpuid4_info); +-#define CPUID4_INFO_IDX(x, y) (&((per_cpu(ici_cpuid4_info, x))[y])) +- +-#ifdef CONFIG_SMP +- +-static int cache_shared_amd_cpu_map_setup(unsigned int cpu, int index) ++static int __cache_amd_cpumap_setup(unsigned int cpu, int index, ++ struct _cpuid4_info_regs *base) + { +- struct _cpuid4_info *this_leaf; ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ struct cacheinfo *this_leaf; + int i, sibling; + + /* +@@ -757,40 +816,43 @@ static int cache_shared_amd_cpu_map_setup(unsigned int cpu, int index) + */ + if (index == 3) { + for_each_cpu(i, cpu_llc_shared_mask(cpu)) { +- if (!per_cpu(ici_cpuid4_info, i)) ++ this_cpu_ci = get_cpu_cacheinfo(i); ++ if (!this_cpu_ci->info_list) + continue; +- this_leaf = CPUID4_INFO_IDX(i, index); ++ this_leaf = this_cpu_ci->info_list + index; + for_each_cpu(sibling, cpu_llc_shared_mask(cpu)) { + if (!cpu_online(sibling)) + continue; +- set_bit(sibling, this_leaf->shared_cpu_map); ++ cpumask_set_cpu(sibling, ++ &this_leaf->shared_cpu_map); + } + } +- } else if (cpu_has_topoext) { ++ } else if (boot_cpu_has(X86_FEATURE_TOPOEXT)) { + unsigned int apicid, nshared, first, last; + +- if (!per_cpu(ici_cpuid4_info, cpu)) +- return 0; +- +- this_leaf = CPUID4_INFO_IDX(cpu, index); +- nshared = this_leaf->base.eax.split.num_threads_sharing + 1; ++ this_leaf = this_cpu_ci->info_list + index; ++ nshared = base->eax.split.num_threads_sharing + 1; + apicid = cpu_data(cpu).apicid; + first = apicid - (apicid % nshared); + last = first + nshared - 1; + + for_each_online_cpu(i) { ++ this_cpu_ci = get_cpu_cacheinfo(i); ++ if (!this_cpu_ci->info_list) ++ continue; ++ + apicid = cpu_data(i).apicid; + if ((apicid < first) || (apicid > last)) + continue; +- if (!per_cpu(ici_cpuid4_info, i)) +- continue; +- this_leaf = CPUID4_INFO_IDX(i, index); ++ ++ this_leaf = this_cpu_ci->info_list + index; + + for_each_online_cpu(sibling) { + apicid = cpu_data(sibling).apicid; + if ((apicid < first) || (apicid > last)) + continue; +- set_bit(sibling, this_leaf->shared_cpu_map); ++ cpumask_set_cpu(sibling, ++ &this_leaf->shared_cpu_map); + } + } + } else +@@ -799,72 +861,70 @@ static int cache_shared_amd_cpu_map_setup(unsigned int cpu, int index) + return 1; + } + +-static void cache_shared_cpu_map_setup(unsigned int cpu, int index) ++static void __cache_cpumap_setup(unsigned int cpu, int index, ++ struct _cpuid4_info_regs *base) + { +- struct _cpuid4_info *this_leaf, *sibling_leaf; ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ struct cacheinfo *this_leaf, *sibling_leaf; + unsigned long num_threads_sharing; + int index_msb, i; + struct cpuinfo_x86 *c = &cpu_data(cpu); + + if (c->x86_vendor == X86_VENDOR_AMD) { +- if (cache_shared_amd_cpu_map_setup(cpu, index)) ++ if (__cache_amd_cpumap_setup(cpu, index, base)) + return; + } + +- this_leaf = CPUID4_INFO_IDX(cpu, index); +- num_threads_sharing = 1 + this_leaf->base.eax.split.num_threads_sharing; ++ this_leaf = this_cpu_ci->info_list + index; ++ num_threads_sharing = 1 + base->eax.split.num_threads_sharing; + ++ cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map); + if (num_threads_sharing == 1) +- cpumask_set_cpu(cpu, to_cpumask(this_leaf->shared_cpu_map)); +- else { +- index_msb = get_count_order(num_threads_sharing); ++ return; + +- for_each_online_cpu(i) { +- if (cpu_data(i).apicid >> index_msb == +- c->apicid >> index_msb) { +- cpumask_set_cpu(i, +- to_cpumask(this_leaf->shared_cpu_map)); +- if (i != cpu && per_cpu(ici_cpuid4_info, i)) { +- sibling_leaf = +- CPUID4_INFO_IDX(i, index); +- cpumask_set_cpu(cpu, to_cpumask( +- sibling_leaf->shared_cpu_map)); +- } +- } ++ index_msb = get_count_order(num_threads_sharing); ++ ++ for_each_online_cpu(i) ++ if (cpu_data(i).apicid >> index_msb == c->apicid >> index_msb) { ++ struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i); ++ ++ if (i == cpu || !sib_cpu_ci->info_list) ++ continue;/* skip if itself or no cacheinfo */ ++ sibling_leaf = sib_cpu_ci->info_list + index; ++ cpumask_set_cpu(i, &this_leaf->shared_cpu_map); ++ cpumask_set_cpu(cpu, &sibling_leaf->shared_cpu_map); + } +- } +-} +-static void cache_remove_shared_cpu_map(unsigned int cpu, int index) +-{ +- struct _cpuid4_info *this_leaf, *sibling_leaf; +- int sibling; +- +- this_leaf = CPUID4_INFO_IDX(cpu, index); +- for_each_cpu(sibling, to_cpumask(this_leaf->shared_cpu_map)) { +- sibling_leaf = CPUID4_INFO_IDX(sibling, index); +- cpumask_clear_cpu(cpu, +- to_cpumask(sibling_leaf->shared_cpu_map)); +- } +-} +-#else +-static void cache_shared_cpu_map_setup(unsigned int cpu, int index) +-{ + } + +-static void cache_remove_shared_cpu_map(unsigned int cpu, int index) ++static void ci_leaf_init(struct cacheinfo *this_leaf, ++ struct _cpuid4_info_regs *base) + { ++ this_leaf->id = base->id; ++ this_leaf->attributes = CACHE_ID; ++ this_leaf->level = base->eax.split.level; ++ this_leaf->type = cache_type_map[base->eax.split.type]; ++ this_leaf->coherency_line_size = ++ base->ebx.split.coherency_line_size + 1; ++ this_leaf->ways_of_associativity = ++ base->ebx.split.ways_of_associativity + 1; ++ this_leaf->size = base->size; ++ this_leaf->number_of_sets = base->ecx.split.number_of_sets + 1; ++ this_leaf->physical_line_partition = ++ base->ebx.split.physical_line_partition + 1; ++ this_leaf->priv = base->nb; + } +-#endif + +-static void free_cache_attributes(unsigned int cpu) ++static int __init_cache_level(unsigned int cpu) + { +- int i; +- +- for (i = 0; i < num_cache_leaves; i++) +- cache_remove_shared_cpu_map(cpu, i); ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); + +- kfree(per_cpu(ici_cpuid4_info, cpu)); +- per_cpu(ici_cpuid4_info, cpu) = NULL; ++ if (!num_cache_leaves) ++ return -ENOENT; ++ if (!this_cpu_ci) ++ return -EINVAL; ++ this_cpu_ci->num_levels = 3; ++ this_cpu_ci->num_leaves = num_cache_leaves; ++ return 0; + } + + /* +@@ -886,411 +946,37 @@ static void get_cache_id(int cpu, struct _cpuid4_info_regs *id4_regs) + int get_cpu_cache_id(int cpu, int level) + { + int i; ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); + +- for (i = 0; i < num_cache_leaves; i++) { +- struct _cpuid4_info *this_leaf = CPUID4_INFO_IDX(cpu, i); ++ for (i = 0; i < this_cpu_ci->num_leaves; i++) { ++ struct cacheinfo *this_leaf = this_cpu_ci->info_list + i; + +- if (this_leaf->base.eax.split.level == level) +- return this_leaf->base.id; ++ if (this_leaf->level == level) ++ return this_leaf->id; + } + + return -1; + } + +-static void get_cpu_leaves(void *_retval) +-{ +- int j, *retval = _retval, cpu = smp_processor_id(); +- +- /* Do cpuid and store the results */ +- for (j = 0; j < num_cache_leaves; j++) { +- struct _cpuid4_info *this_leaf = CPUID4_INFO_IDX(cpu, j); +- +- *retval = cpuid4_cache_lookup_regs(j, &this_leaf->base); +- if (unlikely(*retval < 0)) { +- int i; +- +- for (i = 0; i < j; i++) +- cache_remove_shared_cpu_map(cpu, i); +- break; +- } +- cache_shared_cpu_map_setup(cpu, j); +- get_cache_id(cpu, &this_leaf->base); +- } +-} +- +-static int detect_cache_attributes(unsigned int cpu) +-{ +- int retval; +- +- if (num_cache_leaves == 0) +- return -ENOENT; +- +- per_cpu(ici_cpuid4_info, cpu) = kzalloc( +- sizeof(struct _cpuid4_info) * num_cache_leaves, GFP_KERNEL); +- if (per_cpu(ici_cpuid4_info, cpu) == NULL) +- return -ENOMEM; +- +- smp_call_function_single(cpu, get_cpu_leaves, &retval, true); +- if (retval) { +- kfree(per_cpu(ici_cpuid4_info, cpu)); +- per_cpu(ici_cpuid4_info, cpu) = NULL; +- } +- +- return retval; +-} +- +-#include +-#include +-#include +- +-/* pointer to kobject for cpuX/cache */ +-static DEFINE_PER_CPU(struct kobject *, ici_cache_kobject); +- +-struct _index_kobject { +- struct kobject kobj; +- unsigned int cpu; +- unsigned short index; +-}; +- +-/* pointer to array of kobjects for cpuX/cache/indexY */ +-static DEFINE_PER_CPU(struct _index_kobject *, ici_index_kobject); +-#define INDEX_KOBJECT_PTR(x, y) (&((per_cpu(ici_index_kobject, x))[y])) +- +-#define show_one_plus(file_name, object, val) \ +-static ssize_t show_##file_name(struct _cpuid4_info *this_leaf, char *buf, \ +- unsigned int cpu) \ +-{ \ +- return sprintf(buf, "%lu\n", (unsigned long)this_leaf->object + val); \ +-} +- +-show_one_plus(level, base.eax.split.level, 0); +-show_one_plus(coherency_line_size, base.ebx.split.coherency_line_size, 1); +-show_one_plus(physical_line_partition, base.ebx.split.physical_line_partition, 1); +-show_one_plus(ways_of_associativity, base.ebx.split.ways_of_associativity, 1); +-show_one_plus(number_of_sets, base.ecx.split.number_of_sets, 1); +- +-static ssize_t show_id(struct _cpuid4_info *this_leaf, char *buf, +- unsigned int cpu) +-{ +- return sprintf(buf, "%u\n", this_leaf->base.id); +-} +- +-static ssize_t show_size(struct _cpuid4_info *this_leaf, char *buf, +- unsigned int cpu) +-{ +- return sprintf(buf, "%luK\n", this_leaf->base.size / 1024); +-} +- +-static ssize_t show_shared_cpu_map_func(struct _cpuid4_info *this_leaf, +- int type, char *buf) +-{ +- ptrdiff_t len = PTR_ALIGN(buf + PAGE_SIZE - 1, PAGE_SIZE) - buf; +- int n = 0; +- +- if (len > 1) { +- const struct cpumask *mask; +- +- mask = to_cpumask(this_leaf->shared_cpu_map); +- n = type ? +- cpulist_scnprintf(buf, len-2, mask) : +- cpumask_scnprintf(buf, len-2, mask); +- buf[n++] = '\n'; +- buf[n] = '\0'; +- } +- return n; +-} +- +-static inline ssize_t show_shared_cpu_map(struct _cpuid4_info *leaf, char *buf, +- unsigned int cpu) +-{ +- return show_shared_cpu_map_func(leaf, 0, buf); +-} +- +-static inline ssize_t show_shared_cpu_list(struct _cpuid4_info *leaf, char *buf, +- unsigned int cpu) +-{ +- return show_shared_cpu_map_func(leaf, 1, buf); +-} +- +-static ssize_t show_type(struct _cpuid4_info *this_leaf, char *buf, +- unsigned int cpu) +-{ +- switch (this_leaf->base.eax.split.type) { +- case CACHE_TYPE_DATA: +- return sprintf(buf, "Data\n"); +- case CACHE_TYPE_INST: +- return sprintf(buf, "Instruction\n"); +- case CACHE_TYPE_UNIFIED: +- return sprintf(buf, "Unified\n"); +- default: +- return sprintf(buf, "Unknown\n"); +- } +-} +- +-#define to_object(k) container_of(k, struct _index_kobject, kobj) +-#define to_attr(a) container_of(a, struct _cache_attr, attr) +- +-#define define_one_ro(_name) \ +-static struct _cache_attr _name = \ +- __ATTR(_name, 0444, show_##_name, NULL) +- +-define_one_ro(id); +-define_one_ro(level); +-define_one_ro(type); +-define_one_ro(coherency_line_size); +-define_one_ro(physical_line_partition); +-define_one_ro(ways_of_associativity); +-define_one_ro(number_of_sets); +-define_one_ro(size); +-define_one_ro(shared_cpu_map); +-define_one_ro(shared_cpu_list); +- +-static struct attribute *default_attrs[] = { +- &id.attr, +- &type.attr, +- &level.attr, +- &coherency_line_size.attr, +- &physical_line_partition.attr, +- &ways_of_associativity.attr, +- &number_of_sets.attr, +- &size.attr, +- &shared_cpu_map.attr, +- &shared_cpu_list.attr, +- NULL +-}; +- +-#ifdef CONFIG_AMD_NB +-static struct attribute **amd_l3_attrs(void) +-{ +- static struct attribute **attrs; +- int n; +- +- if (attrs) +- return attrs; +- +- n = ARRAY_SIZE(default_attrs); +- +- if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) +- n += 2; +- +- if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) +- n += 1; +- +- attrs = kzalloc(n * sizeof (struct attribute *), GFP_KERNEL); +- if (attrs == NULL) +- return attrs = default_attrs; +- +- for (n = 0; default_attrs[n]; n++) +- attrs[n] = default_attrs[n]; +- +- if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) { +- attrs[n++] = &cache_disable_0.attr; +- attrs[n++] = &cache_disable_1.attr; +- } +- +- if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) +- attrs[n++] = &subcaches.attr; +- +- return attrs; +-} +-#endif +- +-static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf) +-{ +- struct _cache_attr *fattr = to_attr(attr); +- struct _index_kobject *this_leaf = to_object(kobj); +- ssize_t ret; +- +- ret = fattr->show ? +- fattr->show(CPUID4_INFO_IDX(this_leaf->cpu, this_leaf->index), +- buf, this_leaf->cpu) : +- 0; +- return ret; +-} +- +-static ssize_t store(struct kobject *kobj, struct attribute *attr, +- const char *buf, size_t count) +-{ +- struct _cache_attr *fattr = to_attr(attr); +- struct _index_kobject *this_leaf = to_object(kobj); +- ssize_t ret; +- +- ret = fattr->store ? +- fattr->store(CPUID4_INFO_IDX(this_leaf->cpu, this_leaf->index), +- buf, count, this_leaf->cpu) : +- 0; +- return ret; +-} +- +-static const struct sysfs_ops sysfs_ops = { +- .show = show, +- .store = store, +-}; +- +-static struct kobj_type ktype_cache = { +- .sysfs_ops = &sysfs_ops, +- .default_attrs = default_attrs, +-}; +- +-static struct kobj_type ktype_percpu_entry = { +- .sysfs_ops = &sysfs_ops, +-}; +- +-static void cpuid4_cache_sysfs_exit(unsigned int cpu) +-{ +- kfree(per_cpu(ici_cache_kobject, cpu)); +- kfree(per_cpu(ici_index_kobject, cpu)); +- per_cpu(ici_cache_kobject, cpu) = NULL; +- per_cpu(ici_index_kobject, cpu) = NULL; +- free_cache_attributes(cpu); +-} +- +-static int cpuid4_cache_sysfs_init(unsigned int cpu) +-{ +- int err; +- +- if (num_cache_leaves == 0) +- return -ENOENT; +- +- err = detect_cache_attributes(cpu); +- if (err) +- return err; +- +- /* Allocate all required memory */ +- per_cpu(ici_cache_kobject, cpu) = +- kzalloc(sizeof(struct kobject), GFP_KERNEL); +- if (unlikely(per_cpu(ici_cache_kobject, cpu) == NULL)) +- goto err_out; +- +- per_cpu(ici_index_kobject, cpu) = kzalloc( +- sizeof(struct _index_kobject) * num_cache_leaves, GFP_KERNEL); +- if (unlikely(per_cpu(ici_index_kobject, cpu) == NULL)) +- goto err_out; +- +- return 0; +- +-err_out: +- cpuid4_cache_sysfs_exit(cpu); +- return -ENOMEM; +-} +- +-static DECLARE_BITMAP(cache_dev_map, NR_CPUS); +- +-/* Add/Remove cache interface for CPU device */ +-static int cache_add_dev(struct device *dev) ++static int __populate_cache_leaves(unsigned int cpu) + { +- unsigned int cpu = dev->id; +- unsigned long i, j; +- struct _index_kobject *this_object; +- struct _cpuid4_info *this_leaf; +- int retval; +- +- retval = cpuid4_cache_sysfs_init(cpu); +- if (unlikely(retval < 0)) +- return retval; +- +- retval = kobject_init_and_add(per_cpu(ici_cache_kobject, cpu), +- &ktype_percpu_entry, +- &dev->kobj, "%s", "cache"); +- if (retval < 0) { +- cpuid4_cache_sysfs_exit(cpu); +- return retval; +- } +- +- for (i = 0; i < num_cache_leaves; i++) { +- this_object = INDEX_KOBJECT_PTR(cpu, i); +- this_object->cpu = cpu; +- this_object->index = i; ++ unsigned int idx, ret; ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ struct cacheinfo *this_leaf = this_cpu_ci->info_list; ++ struct _cpuid4_info_regs id4_regs = {}; + +- this_leaf = CPUID4_INFO_IDX(cpu, i); +- +- ktype_cache.default_attrs = default_attrs; +-#ifdef CONFIG_AMD_NB +- if (this_leaf->base.nb) +- ktype_cache.default_attrs = amd_l3_attrs(); +-#endif +- retval = kobject_init_and_add(&(this_object->kobj), +- &ktype_cache, +- per_cpu(ici_cache_kobject, cpu), +- "index%1lu", i); +- if (unlikely(retval)) { +- for (j = 0; j < i; j++) +- kobject_put(&(INDEX_KOBJECT_PTR(cpu, j)->kobj)); +- kobject_put(per_cpu(ici_cache_kobject, cpu)); +- cpuid4_cache_sysfs_exit(cpu); +- return retval; +- } +- kobject_uevent(&(this_object->kobj), KOBJ_ADD); ++ for (idx = 0; idx < this_cpu_ci->num_leaves; idx++) { ++ ret = cpuid4_cache_lookup_regs(idx, &id4_regs); ++ if (ret) ++ return ret; ++ get_cache_id(cpu, &id4_regs); ++ ci_leaf_init(this_leaf++, &id4_regs); ++ __cache_cpumap_setup(cpu, idx, &id4_regs); + } +- cpumask_set_cpu(cpu, to_cpumask(cache_dev_map)); ++ this_cpu_ci->cpu_map_populated = true; + +- kobject_uevent(per_cpu(ici_cache_kobject, cpu), KOBJ_ADD); + return 0; + } + +-static void cache_remove_dev(struct device *dev) +-{ +- unsigned int cpu = dev->id; +- unsigned long i; +- +- if (per_cpu(ici_cpuid4_info, cpu) == NULL) +- return; +- if (!cpumask_test_cpu(cpu, to_cpumask(cache_dev_map))) +- return; +- cpumask_clear_cpu(cpu, to_cpumask(cache_dev_map)); +- +- for (i = 0; i < num_cache_leaves; i++) +- kobject_put(&(INDEX_KOBJECT_PTR(cpu, i)->kobj)); +- kobject_put(per_cpu(ici_cache_kobject, cpu)); +- cpuid4_cache_sysfs_exit(cpu); +-} +- +-static int cacheinfo_cpu_callback(struct notifier_block *nfb, +- unsigned long action, void *hcpu) +-{ +- unsigned int cpu = (unsigned long)hcpu; +- struct device *dev; +- +- dev = get_cpu_device(cpu); +- switch (action) { +- case CPU_ONLINE: +- case CPU_ONLINE_FROZEN: +- cache_add_dev(dev); +- break; +- case CPU_DEAD: +- case CPU_DEAD_FROZEN: +- cache_remove_dev(dev); +- break; +- } +- return NOTIFY_OK; +-} +- +-static struct notifier_block cacheinfo_cpu_notifier = { +- .notifier_call = cacheinfo_cpu_callback, +-}; +- +-static int __init cache_sysfs_init(void) +-{ +- int i, err = 0; +- +- if (num_cache_leaves == 0) +- return 0; +- +- cpu_notifier_register_begin(); +- for_each_online_cpu(i) { +- struct device *dev = get_cpu_device(i); +- +- err = cache_add_dev(dev); +- if (err) +- goto out; +- } +- __register_hotcpu_notifier(&cacheinfo_cpu_notifier); +- +-out: +- cpu_notifier_register_done(); +- return err; +-} +- +-device_initcall(cache_sysfs_init); +- +-#endif ++DEFINE_SMP_CALL_CACHE_FUNCTION(init_cache_level) ++DEFINE_SMP_CALL_CACHE_FUNCTION(populate_cache_leaves) +diff --git a/drivers/base/Makefile b/drivers/base/Makefile +index 53c3fe1..527d291 100644 +--- a/drivers/base/Makefile ++++ b/drivers/base/Makefile +@@ -4,7 +4,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \ + driver.o class.o platform.o \ + cpu.o firmware.o init.o map.o devres.o \ + attribute_container.o transport_class.o \ +- topology.o container.o property.o ++ topology.o container.o property.o cacheinfo.o + obj-$(CONFIG_DEVTMPFS) += devtmpfs.o + obj-$(CONFIG_DMA_CMA) += dma-contiguous.o + obj-y += power/ +diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c +new file mode 100644 +index 0000000..eb3af27 +--- /dev/null ++++ b/drivers/base/cacheinfo.c +@@ -0,0 +1,662 @@ ++/* ++ * cacheinfo support - processor cache information via sysfs ++ * ++ * Based on arch/x86/kernel/cpu/intel_cacheinfo.c ++ * Author: Sudeep Holla ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed "as is" WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* pointer to per cpu cacheinfo */ ++static DEFINE_PER_CPU(struct cpu_cacheinfo, ci_cpu_cacheinfo); ++#define ci_cacheinfo(cpu) (&per_cpu(ci_cpu_cacheinfo, cpu)) ++#define cache_leaves(cpu) (ci_cacheinfo(cpu)->num_leaves) ++#define per_cpu_cacheinfo(cpu) (ci_cacheinfo(cpu)->info_list) ++ ++struct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu) ++{ ++ return ci_cacheinfo(cpu); ++} ++ ++#ifdef CONFIG_OF ++static int cache_setup_of_node(unsigned int cpu) ++{ ++ struct device_node *np; ++ struct cacheinfo *this_leaf; ++ struct device *cpu_dev = get_cpu_device(cpu); ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ unsigned int index = 0; ++ ++ /* skip if of_node is already populated */ ++ if (this_cpu_ci->info_list->of_node) ++ return 0; ++ ++ if (!cpu_dev) { ++ pr_err("No cpu device for CPU %d\n", cpu); ++ return -ENODEV; ++ } ++ np = cpu_dev->of_node; ++ if (!np) { ++ pr_err("Failed to find cpu%d device node\n", cpu); ++ return -ENOENT; ++ } ++ ++ while (index < cache_leaves(cpu)) { ++ this_leaf = this_cpu_ci->info_list + index; ++ if (this_leaf->level != 1) ++ np = of_find_next_cache_node(np); ++ else ++ np = of_node_get(np);/* cpu node itself */ ++ if (!np) ++ break; ++ this_leaf->of_node = np; ++ index++; ++ } ++ ++ if (index != cache_leaves(cpu)) /* not all OF nodes populated */ ++ return -ENOENT; ++ ++ return 0; ++} ++ ++static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, ++ struct cacheinfo *sib_leaf) ++{ ++ return sib_leaf->of_node == this_leaf->of_node; ++} ++ ++/* OF properties to query for a given cache type */ ++struct cache_type_info { ++ const char *size_prop; ++ const char *line_size_props[2]; ++ const char *nr_sets_prop; ++}; ++ ++static const struct cache_type_info cache_type_info[] = { ++ { ++ .size_prop = "cache-size", ++ .line_size_props = { "cache-line-size", ++ "cache-block-size", }, ++ .nr_sets_prop = "cache-sets", ++ }, { ++ .size_prop = "i-cache-size", ++ .line_size_props = { "i-cache-line-size", ++ "i-cache-block-size", }, ++ .nr_sets_prop = "i-cache-sets", ++ }, { ++ .size_prop = "d-cache-size", ++ .line_size_props = { "d-cache-line-size", ++ "d-cache-block-size", }, ++ .nr_sets_prop = "d-cache-sets", ++ }, ++}; ++ ++static inline int get_cacheinfo_idx(enum cache_type type) ++{ ++ if (type == CACHE_TYPE_UNIFIED) ++ return 0; ++ return type; ++} ++ ++static void cache_size(struct cacheinfo *this_leaf) ++{ ++ const char *propname; ++ const __be32 *cache_size; ++ int ct_idx; ++ ++ ct_idx = get_cacheinfo_idx(this_leaf->type); ++ propname = cache_type_info[ct_idx].size_prop; ++ ++ cache_size = of_get_property(this_leaf->of_node, propname, NULL); ++ if (cache_size) ++ this_leaf->size = of_read_number(cache_size, 1); ++} ++ ++/* not cache_line_size() because that's a macro in include/linux/cache.h */ ++static void cache_get_line_size(struct cacheinfo *this_leaf) ++{ ++ const __be32 *line_size; ++ int i, lim, ct_idx; ++ ++ ct_idx = get_cacheinfo_idx(this_leaf->type); ++ lim = ARRAY_SIZE(cache_type_info[ct_idx].line_size_props); ++ ++ for (i = 0; i < lim; i++) { ++ const char *propname; ++ ++ propname = cache_type_info[ct_idx].line_size_props[i]; ++ line_size = of_get_property(this_leaf->of_node, propname, NULL); ++ if (line_size) ++ break; ++ } ++ ++ if (line_size) ++ this_leaf->coherency_line_size = of_read_number(line_size, 1); ++} ++ ++static void cache_nr_sets(struct cacheinfo *this_leaf) ++{ ++ const char *propname; ++ const __be32 *nr_sets; ++ int ct_idx; ++ ++ ct_idx = get_cacheinfo_idx(this_leaf->type); ++ propname = cache_type_info[ct_idx].nr_sets_prop; ++ ++ nr_sets = of_get_property(this_leaf->of_node, propname, NULL); ++ if (nr_sets) ++ this_leaf->number_of_sets = of_read_number(nr_sets, 1); ++} ++ ++static void cache_associativity(struct cacheinfo *this_leaf) ++{ ++ unsigned int line_size = this_leaf->coherency_line_size; ++ unsigned int nr_sets = this_leaf->number_of_sets; ++ unsigned int size = this_leaf->size; ++ ++ /* ++ * If the cache is fully associative, there is no need to ++ * check the other properties. ++ */ ++ if (!(nr_sets == 1) && (nr_sets > 0 && size > 0 && line_size > 0)) ++ this_leaf->ways_of_associativity = (size / nr_sets) / line_size; ++} ++ ++static void cache_of_override_properties(unsigned int cpu) ++{ ++ int index; ++ struct cacheinfo *this_leaf; ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ ++ for (index = 0; index < cache_leaves(cpu); index++) { ++ this_leaf = this_cpu_ci->info_list + index; ++ cache_size(this_leaf); ++ cache_get_line_size(this_leaf); ++ cache_nr_sets(this_leaf); ++ cache_associativity(this_leaf); ++ } ++} ++#else ++static void cache_of_override_properties(unsigned int cpu) { } ++static inline int cache_setup_of_node(unsigned int cpu) { return 0; } ++static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, ++ struct cacheinfo *sib_leaf) ++{ ++ /* ++ * For non-DT systems, assume unique level 1 cache, system-wide ++ * shared caches for all other levels. This will be used only if ++ * arch specific code has not populated shared_cpu_map ++ */ ++ return !(this_leaf->level == 1); ++} ++#endif ++ ++static int cache_shared_cpu_map_setup(unsigned int cpu) ++{ ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ struct cacheinfo *this_leaf, *sib_leaf; ++ unsigned int index; ++ int ret = 0; ++ ++ if (this_cpu_ci->cpu_map_populated) ++ return 0; ++ ++ if (of_have_populated_dt()) ++ ret = cache_setup_of_node(cpu); ++ else if (!acpi_disabled) ++ /* No cache property/hierarchy support yet in ACPI */ ++ ret = -ENOTSUPP; ++ if (ret) ++ return ret; ++ ++ for (index = 0; index < cache_leaves(cpu); index++) { ++ unsigned int i; ++ ++ this_leaf = this_cpu_ci->info_list + index; ++ /* skip if shared_cpu_map is already populated */ ++ if (!cpumask_empty(&this_leaf->shared_cpu_map)) ++ continue; ++ ++ cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map); ++ for_each_online_cpu(i) { ++ struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i); ++ ++ if (i == cpu || !sib_cpu_ci->info_list) ++ continue;/* skip if itself or no cacheinfo */ ++ sib_leaf = sib_cpu_ci->info_list + index; ++ if (cache_leaves_are_shared(this_leaf, sib_leaf)) { ++ cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map); ++ cpumask_set_cpu(i, &this_leaf->shared_cpu_map); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static void cache_shared_cpu_map_remove(unsigned int cpu) ++{ ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ struct cacheinfo *this_leaf, *sib_leaf; ++ unsigned int sibling, index; ++ ++ for (index = 0; index < cache_leaves(cpu); index++) { ++ this_leaf = this_cpu_ci->info_list + index; ++ for_each_cpu(sibling, &this_leaf->shared_cpu_map) { ++ struct cpu_cacheinfo *sib_cpu_ci; ++ ++ if (sibling == cpu) /* skip itself */ ++ continue; ++ ++ sib_cpu_ci = get_cpu_cacheinfo(sibling); ++ if (!sib_cpu_ci->info_list) ++ continue; ++ ++ sib_leaf = sib_cpu_ci->info_list + index; ++ cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map); ++ cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map); ++ } ++ of_node_put(this_leaf->of_node); ++ } ++} ++ ++static void cache_override_properties(unsigned int cpu) ++{ ++ if (of_have_populated_dt()) ++ return cache_of_override_properties(cpu); ++} ++ ++static void free_cache_attributes(unsigned int cpu) ++{ ++ if (!per_cpu_cacheinfo(cpu)) ++ return; ++ ++ cache_shared_cpu_map_remove(cpu); ++ ++ kfree(per_cpu_cacheinfo(cpu)); ++ per_cpu_cacheinfo(cpu) = NULL; ++} ++ ++int __weak init_cache_level(unsigned int cpu) ++{ ++ return -ENOENT; ++} ++ ++int __weak populate_cache_leaves(unsigned int cpu) ++{ ++ return -ENOENT; ++} ++ ++static int detect_cache_attributes(unsigned int cpu) ++{ ++ int ret; ++ ++ if (init_cache_level(cpu) || !cache_leaves(cpu)) ++ return -ENOENT; ++ ++ per_cpu_cacheinfo(cpu) = kcalloc(cache_leaves(cpu), ++ sizeof(struct cacheinfo), GFP_KERNEL); ++ if (per_cpu_cacheinfo(cpu) == NULL) ++ return -ENOMEM; ++ ++ ret = populate_cache_leaves(cpu); ++ if (ret) ++ goto free_ci; ++ /* ++ * For systems using DT for cache hierarchy, of_node and shared_cpu_map ++ * will be set up here only if they are not populated already ++ */ ++ ret = cache_shared_cpu_map_setup(cpu); ++ if (ret) { ++ pr_warn("Unable to detect cache hierarchy for CPU %d\n", cpu); ++ goto free_ci; ++ } ++ ++ cache_override_properties(cpu); ++ return 0; ++ ++free_ci: ++ free_cache_attributes(cpu); ++ return ret; ++} ++ ++/* pointer to cpuX/cache device */ ++static DEFINE_PER_CPU(struct device *, ci_cache_dev); ++#define per_cpu_cache_dev(cpu) (per_cpu(ci_cache_dev, cpu)) ++ ++static cpumask_t cache_dev_map; ++ ++/* pointer to array of devices for cpuX/cache/indexY */ ++static DEFINE_PER_CPU(struct device **, ci_index_dev); ++#define per_cpu_index_dev(cpu) (per_cpu(ci_index_dev, cpu)) ++#define per_cache_index_dev(cpu, idx) ((per_cpu_index_dev(cpu))[idx]) ++ ++#define show_one(file_name, object) \ ++static ssize_t file_name##_show(struct device *dev, \ ++ struct device_attribute *attr, char *buf) \ ++{ \ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); \ ++ return sprintf(buf, "%u\n", this_leaf->object); \ ++} ++ ++show_one(id, id); ++show_one(level, level); ++show_one(coherency_line_size, coherency_line_size); ++show_one(number_of_sets, number_of_sets); ++show_one(physical_line_partition, physical_line_partition); ++show_one(ways_of_associativity, ways_of_associativity); ++ ++static ssize_t size_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%uK\n", this_leaf->size >> 10); ++} ++ ++static ssize_t shared_cpumap_show_func(struct device *dev, bool list, char *buf) ++{ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ const struct cpumask *mask = &this_leaf->shared_cpu_map; ++ ++ return cpumap_print_to_pagebuf(list, buf, mask); ++} ++ ++static ssize_t shared_cpu_map_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return shared_cpumap_show_func(dev, false, buf); ++} ++ ++static ssize_t shared_cpu_list_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return shared_cpumap_show_func(dev, true, buf); ++} ++ ++static ssize_t type_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ ++ switch (this_leaf->type) { ++ case CACHE_TYPE_DATA: ++ return sprintf(buf, "Data\n"); ++ case CACHE_TYPE_INST: ++ return sprintf(buf, "Instruction\n"); ++ case CACHE_TYPE_UNIFIED: ++ return sprintf(buf, "Unified\n"); ++ default: ++ return -EINVAL; ++ } ++} ++ ++static ssize_t allocation_policy_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ unsigned int ci_attr = this_leaf->attributes; ++ int n = 0; ++ ++ if ((ci_attr & CACHE_READ_ALLOCATE) && (ci_attr & CACHE_WRITE_ALLOCATE)) ++ n = sprintf(buf, "ReadWriteAllocate\n"); ++ else if (ci_attr & CACHE_READ_ALLOCATE) ++ n = sprintf(buf, "ReadAllocate\n"); ++ else if (ci_attr & CACHE_WRITE_ALLOCATE) ++ n = sprintf(buf, "WriteAllocate\n"); ++ return n; ++} ++ ++static ssize_t write_policy_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ unsigned int ci_attr = this_leaf->attributes; ++ int n = 0; ++ ++ if (ci_attr & CACHE_WRITE_THROUGH) ++ n = sprintf(buf, "WriteThrough\n"); ++ else if (ci_attr & CACHE_WRITE_BACK) ++ n = sprintf(buf, "WriteBack\n"); ++ return n; ++} ++ ++static DEVICE_ATTR_RO(id); ++static DEVICE_ATTR_RO(level); ++static DEVICE_ATTR_RO(type); ++static DEVICE_ATTR_RO(coherency_line_size); ++static DEVICE_ATTR_RO(ways_of_associativity); ++static DEVICE_ATTR_RO(number_of_sets); ++static DEVICE_ATTR_RO(size); ++static DEVICE_ATTR_RO(allocation_policy); ++static DEVICE_ATTR_RO(write_policy); ++static DEVICE_ATTR_RO(shared_cpu_map); ++static DEVICE_ATTR_RO(shared_cpu_list); ++static DEVICE_ATTR_RO(physical_line_partition); ++ ++static struct attribute *cache_default_attrs[] = { ++ &dev_attr_id.attr, ++ &dev_attr_type.attr, ++ &dev_attr_level.attr, ++ &dev_attr_shared_cpu_map.attr, ++ &dev_attr_shared_cpu_list.attr, ++ &dev_attr_coherency_line_size.attr, ++ &dev_attr_ways_of_associativity.attr, ++ &dev_attr_number_of_sets.attr, ++ &dev_attr_size.attr, ++ &dev_attr_allocation_policy.attr, ++ &dev_attr_write_policy.attr, ++ &dev_attr_physical_line_partition.attr, ++ NULL ++}; ++ ++static umode_t ++cache_default_attrs_is_visible(struct kobject *kobj, ++ struct attribute *attr, int unused) ++{ ++ struct device *dev = kobj_to_dev(kobj); ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ const struct cpumask *mask = &this_leaf->shared_cpu_map; ++ umode_t mode = attr->mode; ++ ++ if ((attr == &dev_attr_id.attr) && (this_leaf->attributes & CACHE_ID)) ++ return mode; ++ if ((attr == &dev_attr_type.attr) && this_leaf->type) ++ return mode; ++ if ((attr == &dev_attr_level.attr) && this_leaf->level) ++ return mode; ++ if ((attr == &dev_attr_shared_cpu_map.attr) && !cpumask_empty(mask)) ++ return mode; ++ if ((attr == &dev_attr_shared_cpu_list.attr) && !cpumask_empty(mask)) ++ return mode; ++ if ((attr == &dev_attr_coherency_line_size.attr) && ++ this_leaf->coherency_line_size) ++ return mode; ++ if ((attr == &dev_attr_ways_of_associativity.attr) && ++ this_leaf->size) /* allow 0 = full associativity */ ++ return mode; ++ if ((attr == &dev_attr_number_of_sets.attr) && ++ this_leaf->number_of_sets) ++ return mode; ++ if ((attr == &dev_attr_size.attr) && this_leaf->size) ++ return mode; ++ if ((attr == &dev_attr_write_policy.attr) && ++ (this_leaf->attributes & CACHE_WRITE_POLICY_MASK)) ++ return mode; ++ if ((attr == &dev_attr_allocation_policy.attr) && ++ (this_leaf->attributes & CACHE_ALLOCATE_POLICY_MASK)) ++ return mode; ++ if ((attr == &dev_attr_physical_line_partition.attr) && ++ this_leaf->physical_line_partition) ++ return mode; ++ ++ return 0; ++} ++ ++static const struct attribute_group cache_default_group = { ++ .attrs = cache_default_attrs, ++ .is_visible = cache_default_attrs_is_visible, ++}; ++ ++static const struct attribute_group *cache_default_groups[] = { ++ &cache_default_group, ++ NULL, ++}; ++ ++static const struct attribute_group *cache_private_groups[] = { ++ &cache_default_group, ++ NULL, /* Place holder for private group */ ++ NULL, ++}; ++ ++const struct attribute_group * ++__weak cache_get_priv_group(struct cacheinfo *this_leaf) ++{ ++ return NULL; ++} ++ ++static const struct attribute_group ** ++cache_get_attribute_groups(struct cacheinfo *this_leaf) ++{ ++ const struct attribute_group *priv_group = ++ cache_get_priv_group(this_leaf); ++ ++ if (!priv_group) ++ return cache_default_groups; ++ ++ if (!cache_private_groups[1]) ++ cache_private_groups[1] = priv_group; ++ ++ return cache_private_groups; ++} ++ ++/* Add/Remove cache interface for CPU device */ ++static void cpu_cache_sysfs_exit(unsigned int cpu) ++{ ++ int i; ++ struct device *ci_dev; ++ ++ if (per_cpu_index_dev(cpu)) { ++ for (i = 0; i < cache_leaves(cpu); i++) { ++ ci_dev = per_cache_index_dev(cpu, i); ++ if (!ci_dev) ++ continue; ++ device_unregister(ci_dev); ++ } ++ kfree(per_cpu_index_dev(cpu)); ++ per_cpu_index_dev(cpu) = NULL; ++ } ++ device_unregister(per_cpu_cache_dev(cpu)); ++ per_cpu_cache_dev(cpu) = NULL; ++} ++ ++static int cpu_cache_sysfs_init(unsigned int cpu) ++{ ++ struct device *dev = get_cpu_device(cpu); ++ ++ if (per_cpu_cacheinfo(cpu) == NULL) ++ return -ENOENT; ++ ++ per_cpu_cache_dev(cpu) = cpu_device_create(dev, NULL, NULL, "cache"); ++ if (IS_ERR(per_cpu_cache_dev(cpu))) ++ return PTR_ERR(per_cpu_cache_dev(cpu)); ++ ++ /* Allocate all required memory */ ++ per_cpu_index_dev(cpu) = kcalloc(cache_leaves(cpu), ++ sizeof(struct device *), GFP_KERNEL); ++ if (unlikely(per_cpu_index_dev(cpu) == NULL)) ++ goto err_out; ++ ++ return 0; ++ ++err_out: ++ cpu_cache_sysfs_exit(cpu); ++ return -ENOMEM; ++} ++ ++static int cache_add_dev(unsigned int cpu) ++{ ++ unsigned int i; ++ int rc; ++ struct device *ci_dev, *parent; ++ struct cacheinfo *this_leaf; ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ const struct attribute_group **cache_groups; ++ ++ rc = cpu_cache_sysfs_init(cpu); ++ if (unlikely(rc < 0)) ++ return rc; ++ ++ parent = per_cpu_cache_dev(cpu); ++ for (i = 0; i < cache_leaves(cpu); i++) { ++ this_leaf = this_cpu_ci->info_list + i; ++ if (this_leaf->disable_sysfs) ++ continue; ++ cache_groups = cache_get_attribute_groups(this_leaf); ++ ci_dev = cpu_device_create(parent, this_leaf, cache_groups, ++ "index%1u", i); ++ if (IS_ERR(ci_dev)) { ++ rc = PTR_ERR(ci_dev); ++ goto err; ++ } ++ per_cache_index_dev(cpu, i) = ci_dev; ++ } ++ cpumask_set_cpu(cpu, &cache_dev_map); ++ ++ return 0; ++err: ++ cpu_cache_sysfs_exit(cpu); ++ return rc; ++} ++ ++static int cacheinfo_cpu_online(unsigned int cpu) ++{ ++ int rc = detect_cache_attributes(cpu); ++ ++ if (rc) ++ return rc; ++ rc = cache_add_dev(cpu); ++ if (rc) ++ free_cache_attributes(cpu); ++ return rc; ++} ++ ++static int cacheinfo_cpu_pre_down(unsigned int cpu) ++{ ++ if (cpumask_test_and_clear_cpu(cpu, &cache_dev_map)) ++ cpu_cache_sysfs_exit(cpu); ++ ++ free_cache_attributes(cpu); ++ return 0; ++} ++ ++static int __init cacheinfo_sysfs_init(void) ++{ ++ return cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "base/cacheinfo:online", ++ cacheinfo_cpu_online, cacheinfo_cpu_pre_down); ++} ++device_initcall(cacheinfo_sysfs_init); +diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c +index bd82212..803d2a0 100644 +--- a/drivers/base/cpu.c ++++ b/drivers/base/cpu.c +@@ -338,6 +338,60 @@ struct device *get_cpu_device(unsigned cpu) + } + EXPORT_SYMBOL_GPL(get_cpu_device); + ++static void device_create_release(struct device *dev) ++{ ++ kfree(dev); ++} ++ ++static struct device * ++__cpu_device_create(struct device *parent, void *drvdata, ++ const struct attribute_group **groups, ++ const char *fmt, va_list args) ++{ ++ struct device *dev = NULL; ++ int retval = -ENODEV; ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (!dev) { ++ retval = -ENOMEM; ++ goto error; ++ } ++ ++ device_initialize(dev); ++ dev->parent = parent; ++ dev->groups = groups; ++ dev->release = device_create_release; ++ dev_set_drvdata(dev, drvdata); ++ ++ retval = kobject_set_name_vargs(&dev->kobj, fmt, args); ++ if (retval) ++ goto error; ++ ++ retval = device_add(dev); ++ if (retval) ++ goto error; ++ ++ return dev; ++ ++error: ++ put_device(dev); ++ return ERR_PTR(retval); ++} ++ ++struct device *cpu_device_create(struct device *parent, void *drvdata, ++ const struct attribute_group **groups, ++ const char *fmt, ...) ++{ ++ va_list vargs; ++ struct device *dev; ++ ++ va_start(vargs, fmt); ++ dev = __cpu_device_create(parent, drvdata, groups, fmt, vargs); ++ va_end(vargs); ++ return dev; ++} ++EXPORT_SYMBOL_GPL(cpu_device_create); ++ + #ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE + static DEVICE_ATTR(modalias, 0444, arch_print_cpu_modalias, NULL); + #endif +diff --git a/include/linux/cacheinfo.h b/include/linux/cacheinfo.h +new file mode 100644 +index 0000000..6a524bf +--- /dev/null ++++ b/include/linux/cacheinfo.h +@@ -0,0 +1,104 @@ ++#ifndef _LINUX_CACHEINFO_H ++#define _LINUX_CACHEINFO_H ++ ++#include ++#include ++#include ++ ++struct device_node; ++struct attribute; ++ ++enum cache_type { ++ CACHE_TYPE_NOCACHE = 0, ++ CACHE_TYPE_INST = BIT(0), ++ CACHE_TYPE_DATA = BIT(1), ++ CACHE_TYPE_SEPARATE = CACHE_TYPE_INST | CACHE_TYPE_DATA, ++ CACHE_TYPE_UNIFIED = BIT(2), ++}; ++ ++/** ++ * struct cacheinfo - represent a cache leaf node ++ * @id: This cache's id. It is unique among caches with the same (type, level). ++ * @type: type of the cache - data, inst or unified ++ * @level: represents the hierarchy in the multi-level cache ++ * @coherency_line_size: size of each cache line usually representing ++ * the minimum amount of data that gets transferred from memory ++ * @number_of_sets: total number of sets, a set is a collection of cache ++ * lines sharing the same index ++ * @ways_of_associativity: number of ways in which a particular memory ++ * block can be placed in the cache ++ * @physical_line_partition: number of physical cache lines sharing the ++ * same cachetag ++ * @size: Total size of the cache ++ * @shared_cpu_map: logical cpumask representing all the cpus sharing ++ * this cache node ++ * @attributes: bitfield representing various cache attributes ++ * @of_node: if devicetree is used, this represents either the cpu node in ++ * case there's no explicit cache node or the cache node itself in the ++ * device tree ++ * @disable_sysfs: indicates whether this node is visible to the user via ++ * sysfs or not ++ * @priv: pointer to any private data structure specific to particular ++ * cache design ++ * ++ * While @of_node, @disable_sysfs and @priv are used for internal book ++ * keeping, the remaining members form the core properties of the cache ++ */ ++struct cacheinfo { ++ unsigned int id; ++ enum cache_type type; ++ unsigned int level; ++ unsigned int coherency_line_size; ++ unsigned int number_of_sets; ++ unsigned int ways_of_associativity; ++ unsigned int physical_line_partition; ++ unsigned int size; ++ cpumask_t shared_cpu_map; ++ unsigned int attributes; ++#define CACHE_WRITE_THROUGH BIT(0) ++#define CACHE_WRITE_BACK BIT(1) ++#define CACHE_WRITE_POLICY_MASK \ ++ (CACHE_WRITE_THROUGH | CACHE_WRITE_BACK) ++#define CACHE_READ_ALLOCATE BIT(2) ++#define CACHE_WRITE_ALLOCATE BIT(3) ++#define CACHE_ALLOCATE_POLICY_MASK \ ++ (CACHE_READ_ALLOCATE | CACHE_WRITE_ALLOCATE) ++#define CACHE_ID BIT(4) ++ ++ struct device_node *of_node; ++ bool disable_sysfs; ++ void *priv; ++}; ++ ++struct cpu_cacheinfo { ++ struct cacheinfo *info_list; ++ unsigned int num_levels; ++ unsigned int num_leaves; ++ bool cpu_map_populated; ++}; ++ ++/* ++ * Helpers to make sure "func" is executed on the cpu whose cache ++ * attributes are being detected ++ */ ++#define DEFINE_SMP_CALL_CACHE_FUNCTION(func) \ ++static inline void _##func(void *ret) \ ++{ \ ++ int cpu = smp_processor_id(); \ ++ *(int *)ret = __##func(cpu); \ ++} \ ++ \ ++int func(unsigned int cpu) \ ++{ \ ++ int ret; \ ++ smp_call_function_single(cpu, _##func, &ret, true); \ ++ return ret; \ ++} ++ ++struct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu); ++int init_cache_level(unsigned int cpu); ++int populate_cache_leaves(unsigned int cpu); ++ ++const struct attribute_group *cache_get_priv_group(struct cacheinfo *this_leaf); ++ ++#endif /* _LINUX_CACHEINFO_H */ +diff --git a/include/linux/cpu.h b/include/linux/cpu.h +index 80e2b1d..06f9893 100644 +--- a/include/linux/cpu.h ++++ b/include/linux/cpu.h +@@ -43,6 +43,9 @@ extern ssize_t cpu_show_spectre_v1(struct device *dev, + extern ssize_t cpu_show_spectre_v2(struct device *dev, + struct device_attribute *attr, char *buf); + ++extern struct device *cpu_device_create(struct device *parent, void *drvdata, ++ const struct attribute_group **groups, ++ const char *fmt, ...); + #ifdef CONFIG_HOTPLUG_CPU + extern void unregister_cpu(struct cpu *cpu); + extern ssize_t arch_cpu_probe(const char *, size_t); +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/US101216-IMA-support-in-Titanium-kernel.patch b/kernel-rt/centos/patches/US101216-IMA-support-in-Titanium-kernel.patch new file mode 100644 index 00000000..3d814096 --- /dev/null +++ b/kernel-rt/centos/patches/US101216-IMA-support-in-Titanium-kernel.patch @@ -0,0 +1,373 @@ +From 7e592781c3f5635f8b455cfcc2daaca572c633da Mon Sep 17 00:00:00 2001 +Message-Id: <7e592781c3f5635f8b455cfcc2daaca572c633da.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Kam Nasim +Date: Wed, 23 Aug 2017 17:58:12 -0400 +Subject: [PATCH 25/32] US101216: IMA support in Titanium kernel + +facilitate building the IMA subsytem out-of-the-kernel tree as a Kernel +module (for which CONFIG_IMA and CONFIG_INTEGRITY will be undefined) by: +- exporting certain function symbols which will be linked to the kernel + module. This includes redefining the export symbols for kernel +functions such that when the kernel module loads, it dynamically points +to those new function definations and reverts to Kernel default +definitions on module deinit +- enabling inode readcount +- modification to ima_file_check to pass in file OPEN status + +Signed-off-by: Jim Somerville +--- + fs/namei.c | 2 +- + fs/nfsd/vfs.c | 2 +- + fs/xattr.c | 1 + + include/linux/fs.h | 15 +------ + include/linux/ima.h | 77 +++++++------------------------- + include/linux/integrity.h | 22 ++++----- + security/security.c | 111 +++++++++++++++++++++++++++++++++++++++++++++- + 7 files changed, 140 insertions(+), 90 deletions(-) + +diff --git a/fs/namei.c b/fs/namei.c +index 9f90b63..bf91ea0 100644 +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -3236,7 +3236,7 @@ opened: + error = open_check_o_direct(file); + if (error) + goto exit_fput; +- error = ima_file_check(file, op->acc_mode); ++ error = ima_file_check(file, op->acc_mode, *opened); + if (error) + goto exit_fput; + +diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c +index 00e98c3..cb9250e 100644 +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -883,7 +883,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, + goto out_nfserr; + } + +- host_err = ima_file_check(file, may_flags); ++ host_err = ima_file_check(file, may_flags, 0); + if (host_err) { + fput(file); + goto out_nfserr; +diff --git a/fs/xattr.c b/fs/xattr.c +index e540aca..cc307ec 100644 +--- a/fs/xattr.c ++++ b/fs/xattr.c +@@ -207,6 +207,7 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value, + *xattr_value = value; + return error; + } ++EXPORT_SYMBOL_GPL(vfs_getxattr_alloc); + + /* Compare an extended attribute value with the given value */ + int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name, +diff --git a/include/linux/fs.h b/include/linux/fs.h +index eb6f994..2dbaf80 100644 +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -677,9 +677,8 @@ struct inode { + struct fsnotify_mark_connector __rcu *i_fsnotify_marks) + #endif + +-#ifdef CONFIG_IMA + atomic_t i_readcount; /* struct files open RO */ +-#endif ++ + void *i_private; /* fs or device private pointer */ + }; + +@@ -2827,7 +2826,6 @@ static inline bool inode_is_open_for_write(const struct inode *inode) + return atomic_read(&inode->i_writecount) > 0; + } + +-#ifdef CONFIG_IMA + static inline void i_readcount_dec(struct inode *inode) + { + BUG_ON(!atomic_read(&inode->i_readcount)); +@@ -2837,16 +2835,7 @@ static inline void i_readcount_inc(struct inode *inode) + { + atomic_inc(&inode->i_readcount); + } +-#else +-static inline void i_readcount_dec(struct inode *inode) +-{ +- return; +-} +-static inline void i_readcount_inc(struct inode *inode) +-{ +- return; +-} +-#endif ++ + extern int do_pipe_flags(int *, int); + + extern int kernel_read(struct file *, loff_t, char *, unsigned long); +diff --git a/include/linux/ima.h b/include/linux/ima.h +index 1b7f268..9fee45c 100644 +--- a/include/linux/ima.h ++++ b/include/linux/ima.h +@@ -13,64 +13,21 @@ + #include + struct linux_binprm; + +-#ifdef CONFIG_IMA +-extern int ima_bprm_check(struct linux_binprm *bprm); +-extern int ima_file_check(struct file *file, int mask); +-extern void ima_file_free(struct file *file); +-extern int ima_file_mmap(struct file *file, unsigned long prot); +-extern int ima_module_check(struct file *file); +- +-#else +-static inline int ima_bprm_check(struct linux_binprm *bprm) +-{ +- return 0; +-} +- +-static inline int ima_file_check(struct file *file, int mask) +-{ +- return 0; +-} +- +-static inline void ima_file_free(struct file *file) +-{ +- return; +-} +- +-static inline int ima_file_mmap(struct file *file, unsigned long prot) +-{ +- return 0; +-} +- +-static inline int ima_module_check(struct file *file) +-{ +- return 0; +-} +- +-#endif /* CONFIG_IMA */ +- +-#ifdef CONFIG_IMA_APPRAISE +-extern void ima_inode_post_setattr(struct dentry *dentry); +-extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, ++/* ++ * The IMA Kernel module has to redefine these symbols so that ++ * the kernel module can link a dynamic function, as a hook into ++ * the Kernel FS calls (which use these) ++ */ ++/* ifdef CONFIG_IMA */ ++extern int (*ima_bprm_check)(struct linux_binprm *bprm); ++extern int (*ima_file_check)(struct file *file, int mask, int opened); ++extern void (*ima_file_free)(struct file *file); ++extern int (*ima_file_mmap)(struct file *file, unsigned long prot); ++extern int (*ima_module_check)(struct file *file); ++ ++/* ifdef CONFIG_IMA_APPRAISE */ ++extern void (*ima_inode_post_setattr)(struct dentry *dentry); ++extern int (*ima_inode_setxattr)(struct dentry *dentry, const char *xattr_name, + const void *xattr_value, size_t xattr_value_len); +-extern int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name); +-#else +-static inline void ima_inode_post_setattr(struct dentry *dentry) +-{ +- return; +-} +- +-static inline int ima_inode_setxattr(struct dentry *dentry, +- const char *xattr_name, +- const void *xattr_value, +- size_t xattr_value_len) +-{ +- return 0; +-} +- +-static inline int ima_inode_removexattr(struct dentry *dentry, +- const char *xattr_name) +-{ +- return 0; +-} +-#endif /* CONFIG_IMA_APPRAISE */ +-#endif /* _LINUX_IMA_H */ ++extern int (*ima_inode_removexattr)(struct dentry *dentry, const char *xattr_name); ++#endif +diff --git a/include/linux/integrity.h b/include/linux/integrity.h +index 83222ce..a5040b6 100644 +--- a/include/linux/integrity.h ++++ b/include/linux/integrity.h +@@ -21,20 +21,14 @@ enum integrity_status { + }; + + /* List of EVM protected security xattrs */ +-#ifdef CONFIG_INTEGRITY +-extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode); +-extern void integrity_inode_free(struct inode *inode); ++/* ++ * The Integrity Kernel module has to redefine these symbols so that ++ * the kernel module can link a dynamic function, as a hook into ++ * the Kernel Security subsystem (which use these) ++ */ + +-#else +-static inline struct integrity_iint_cache * +- integrity_inode_get(struct inode *inode) +-{ +- return NULL; +-} ++/* #ifdef CONFIG_INTEGRITY */ ++extern struct integrity_iint_cache *(*integrity_inode_get)(struct inode *inode); ++extern void (*integrity_inode_free)(struct inode *inode); + +-static inline void integrity_inode_free(struct inode *inode) +-{ +- return; +-} +-#endif /* CONFIG_INTEGRITY */ + #endif /* _LINUX_INTEGRITY_H */ +diff --git a/security/security.c b/security/security.c +index f069482..646a0e3 100644 +--- a/security/security.c ++++ b/security/security.c +@@ -156,6 +156,110 @@ EXPORT_SYMBOL(unregister_lsm_notifier); + + /* Security operations */ + ++/* ++ * Export these symbols since the IMA and Integrity ++ * modules will redefine it. We do this EXPORT in ++ * the security endpoint as this is the last Kernel ++ * hook into the Integrity / IMA modules ++ */ ++#ifndef CONFIG_INTEGRITY ++static struct integrity_iint_cache* integrity_inode_get_kmod(struct inode *inode) ++{ ++ return NULL; ++} ++ ++static void integrity_inode_free_kmod(struct inode *inode) ++{ ++ return; ++} ++ ++struct integrity_iint_cache * ++ (*integrity_inode_get)(struct inode *) = &integrity_inode_get_kmod; ++void ++ (*integrity_inode_free)(struct inode*) = &integrity_inode_free_kmod; ++ ++EXPORT_SYMBOL_GPL(integrity_inode_get); ++EXPORT_SYMBOL_GPL(integrity_inode_free); ++#endif ++ ++#ifndef CONFIG_IMA ++static int ima_bprm_check_kmod(struct linux_binprm *bprm) ++{ ++ return 0; ++} ++ ++static int ima_file_check_kmod(struct file *file, int mask, int opened) ++{ ++ return 0; ++} ++ ++static void ima_file_free_kmod(struct file *file) ++{ ++ return; ++} ++ ++static int ima_file_mmap_kmod(struct file *file, unsigned long prot) ++{ ++ return 0; ++} ++ ++static int ima_module_check_kmod(struct file *file) ++{ ++ return 0; ++} ++ ++int ++ (*ima_bprm_check)(struct linux_binprm *) = &ima_bprm_check_kmod; ++int ++ (*ima_file_check)(struct file *, int, int) = &ima_file_check_kmod; ++void ++ (*ima_file_free)(struct file *) = &ima_file_free_kmod; ++int ++ (*ima_file_mmap)(struct file*, unsigned long) = &ima_file_mmap_kmod; ++int ++ (*ima_module_check)(struct file *) = &ima_module_check_kmod; ++ ++EXPORT_SYMBOL_GPL(ima_bprm_check); ++EXPORT_SYMBOL_GPL(ima_file_check); ++EXPORT_SYMBOL_GPL(ima_file_free); ++EXPORT_SYMBOL_GPL(ima_file_mmap); ++EXPORT_SYMBOL_GPL(ima_module_check); ++#endif ++ ++#ifndef CONFIG_IMA_APPRAISE ++static void ima_inode_post_setattr_kmod(struct dentry *dentry) ++{ ++ return; ++} ++ ++static int ima_inode_setxattr_kmod(struct dentry *dentry, ++ const char *xattr_name, ++ const void *xattr_value, ++ size_t xattr_value_len) ++{ ++ return 0; ++} ++ ++static int ima_inode_removexattr_kmod(struct dentry *dentry, ++ const char *xattr_name) ++{ ++ return 0; ++} ++ ++void ++ (*ima_inode_post_setattr)(struct dentry *) = &ima_inode_post_setattr_kmod; ++int ++ (*ima_inode_setxattr)(struct dentry *, const char *, ++ const void *, size_t) = &ima_inode_setxattr_kmod; ++int ++ (*ima_inode_removexattr)(struct dentry *, ++ const char *) = &ima_inode_removexattr_kmod; ++ ++EXPORT_SYMBOL_GPL(ima_inode_post_setattr); ++EXPORT_SYMBOL_GPL(ima_inode_setxattr); ++EXPORT_SYMBOL_GPL(ima_inode_removexattr); ++#endif ++ + int security_ptrace_access_check(struct task_struct *child, unsigned int mode) + { + #ifdef CONFIG_SECURITY_YAMA_STACKED +@@ -715,8 +819,11 @@ EXPORT_SYMBOL(security_inode_listsecurity); + + void security_inode_getsecid(struct inode *inode, u32 *secid) + { +- security_ops->inode_getsecid(inode, secid); ++ if (unlikely(IS_PRIVATE(inode))) ++ return; ++ security_ops->inode_getsecid(inode, secid); + } ++EXPORT_SYMBOL_GPL(security_inode_getsecid); + + int security_inode_copy_up(struct dentry *src, struct cred **new) + { +@@ -1525,6 +1632,7 @@ int security_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule) + { + return security_ops->audit_rule_init(field, op, rulestr, lsmrule); + } ++EXPORT_SYMBOL_GPL(security_audit_rule_init); + + int security_audit_rule_known(struct audit_krule *krule) + { +@@ -1541,5 +1649,6 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule, + { + return security_ops->audit_rule_match(secid, field, op, lsmrule, actx); + } ++EXPORT_SYMBOL_GPL(security_audit_rule_match); + + #endif /* CONFIG_AUDIT */ +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/US103091-IMA-System-Configuration.patch b/kernel-rt/centos/patches/US103091-IMA-System-Configuration.patch new file mode 100644 index 00000000..1e6c79c4 --- /dev/null +++ b/kernel-rt/centos/patches/US103091-IMA-System-Configuration.patch @@ -0,0 +1,236 @@ +From d84a5bd81b7ef68f35c3bbd845e71aee10e4e9b7 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Kam Nasim +Date: Wed, 4 Oct 2017 14:02:10 -0400 +Subject: [PATCH 26/32] US103091: IMA: System Configuration + +Normally (if trusted integrity keyring is disabled), the _ima keyring +needs to be created by user space (specifically systemd), but that has +the added disadvantage of requiring the IMA public key to reside on the +file system as opposed to being compiled in. Somebody could render some +serious Grade A damage by corrupting this public key on the FS. +Crippling the system if IMA 'enforce' action is enabled. + +We will therefore create the IMA keyring inside the kernel and load the +IMA public key as a compiled data blob, similar to how the Kernel loads +trusted X509 keys into the system truststore (.system_keyring) + +Signed-off-by: Jim Somerville +--- + include/keys/system_keyring.h | 2 ++ + kernel/Makefile | 42 ++++++++++++++++++++-- + kernel/ima_certificate.S | 20 +++++++++++ + kernel/system_keyring.c | 82 +++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 144 insertions(+), 2 deletions(-) + create mode 100644 kernel/ima_certificate.S + +diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h +index 0e49b3c..6b2da90 100644 +--- a/include/keys/system_keyring.h ++++ b/include/keys/system_keyring.h +@@ -34,4 +34,6 @@ static inline struct key *get_system_trusted_keyring(void) + + #endif /* CONFIG_SYSTEM_TRUSTED_KEYRING */ + ++extern struct key *ima_keyring; ++ + #endif /* _KEYS_SYSTEM_KEYRING_H */ +diff --git a/kernel/Makefile b/kernel/Makefile +index d357e7d..f333b29 100644 +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -62,7 +62,7 @@ obj-$(CONFIG_QUEUED_SPINLOCKS) += qspinlock.o + obj-$(CONFIG_QUEUED_RWLOCKS) += qrwlock.o + obj-$(CONFIG_LOCK_SPIN_ON_OWNER) += osq_lock.o + obj-$(CONFIG_UID16) += uid16.o +-obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o ++obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o ima_certificate.o + obj-$(CONFIG_MODULES) += module.o + obj-$(CONFIG_MODULE_SIG) += module_signing.o + obj-$(CONFIG_MODULE_SIG_UEFI) += modsign_uefi.o +@@ -202,7 +202,45 @@ targets += $(obj)/.x509.list + $(obj)/.x509.list: + @echo $(X509_CERTIFICATES) >$@ + +-clean-files := x509_certificate_list .x509.list ++ ++############################################################################### ++# ++# We will roll in the IMA X.509 certificate and pull it in the the kernel ++# so that it gets loaded into the _ima keyring during boot. ++# ++# Ideally, this should have been treated similar to other .x509 certificates ++# (X509_CERTIFICATES), but those all get loaded into the system trusted keyring ++# and since the canonical pathnames are not available in the x509_certificate_list ++# compiled data blob, there is no way to isolate the IMA certificate from the ++# rest. Therefore we treat the IMA certificate as a seperate blob all together. ++# ++# We look in the source root for the IMA certificate, of name "ima_signing_key.pub" ++# ++############################################################################### ++IMA_X509_CERTIFICATE := $(srctree)/ima_signing_key.pub ++ ++ifneq ($(wildcard $(obj)/.x509.ima),) ++ifneq ($(shell cat $(obj)/.x509.ima),$(IMA_X509_CERTIFICATE)) ++$(info IMA: X.509 certificate changed) ++$(shell rm $(obj)/.x509.ima) ++endif ++endif ++ ++kernel/ima_certificate.o: $(obj)/ima_x509_certificate ++ ++quiet_cmd_imacert = CERTS $@ ++ cmd_imacert = cat $(IMA_X509_CERTIFICATE) >$@ $(foreach IMA_X509,$(IMA_X509_CERTIFICATE),; echo " - Including cert $(IMA_X509)") ++ ++targets += $(obj)/ima_x509_certificate ++$(obj)/ima_x509_certificate: $(IMA_X509_CERTIFICATE) $(obj)/.x509.ima ++ $(call if_changed,imacert) ++ ++targets += $(obj)/.x509.ima ++$(obj)/.x509.ima: ++ @echo $(IMA_X509_CERTIFICATE) >$@ ++ ++ ++clean-files := x509_certificate_list .x509.list ima_x509_certificate .x509.ima + endif + + ifeq ($(CONFIG_MODULE_SIG),y) +diff --git a/kernel/ima_certificate.S b/kernel/ima_certificate.S +new file mode 100644 +index 0000000..0c665dd +--- /dev/null ++++ b/kernel/ima_certificate.S +@@ -0,0 +1,20 @@ ++#include ++#include ++ ++ __INITRODATA ++ ++ .align 8 ++ .globl VMLINUX_SYMBOL(ima_system_certificate) ++VMLINUX_SYMBOL(ima_system_certificate): ++__cert_list_start: ++ .incbin "kernel/ima_x509_certificate" ++__cert_list_end: ++ ++ .align 8 ++ .globl VMLINUX_SYMBOL(ima_system_certificate_size) ++VMLINUX_SYMBOL(ima_system_certificate_size): ++#ifdef CONFIG_64BIT ++ .quad __cert_list_end - __cert_list_start ++#else ++ .long __cert_list_end - __cert_list_start ++#endif +diff --git a/kernel/system_keyring.c b/kernel/system_keyring.c +index c15e93f..92beb15 100644 +--- a/kernel/system_keyring.c ++++ b/kernel/system_keyring.c +@@ -23,10 +23,15 @@ EXPORT_SYMBOL_GPL(system_trusted_keyring); + #ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING + struct key *system_blacklist_keyring; + #endif ++struct key *ima_keyring; ++EXPORT_SYMBOL_GPL(ima_keyring); + + extern __initconst const u8 system_certificate_list[]; + extern __initconst const unsigned long system_certificate_list_size; + ++extern __initconst const u8 ima_system_certificate[]; ++extern __initconst const unsigned long ima_system_certificate_size; ++ + /* + * Load the compiled-in keys + */ +@@ -57,6 +62,27 @@ static __init int system_trusted_keyring_init(void) + + set_bit(KEY_FLAG_TRUSTED_ONLY, &system_blacklist_keyring->flags); + #endif ++ /* Normally (if trusted integrity keyring is disabled), the _ima ++ * keyring needs to be created by user space but that has the ++ * added disadvantage of requiring the IMA public key to reside on ++ * the file system as opposed to being compiled in. ++ * We will therefore form a _ima keyring here and load build ++ * the IMA X.509 certificate ++ * ++ * N.B: The IMA keyring only allows root userspace view & read ops ++ */ ++ pr_notice("Initializing system IMA keyring\n"); ++ ++ ima_keyring = keyring_alloc("_ima", ++ KUIDT_INIT(0), KGIDT_INIT(0), ++ current_cred(), ++ ((KEY_POS_ALL & ~KEY_POS_SETATTR) | ++ KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH), ++ KEY_ALLOC_NOT_IN_QUOTA, NULL); ++ if (IS_ERR(ima_keyring)) ++ panic("Can't allocate system IMA keyring\n"); ++ ++ set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_keyring->flags); + + return 0; + } +@@ -121,3 +147,59 @@ dodgy_cert: + return 0; + } + late_initcall(load_system_certificate_list); ++ ++/* ++ * Load the compiled-in IMA certificate. ++ */ ++static __init int load_ima_system_certificate(void) ++{ ++ key_ref_t key; ++ const u8 *p, *end; ++ size_t plen; ++ ++ pr_notice("Loading compiled-in X.509 IMA certificate\n"); ++ ++ p = ima_system_certificate; ++ end = p + ima_system_certificate_size; ++ while (p < end) { ++ /* Each cert begins with an ASN.1 SEQUENCE tag and must be more ++ * than 256 bytes in size. ++ */ ++ if (end - p < 4) ++ goto dodgy_cert; ++ if (p[0] != 0x30 && ++ p[1] != 0x82) ++ goto dodgy_cert; ++ plen = (p[2] << 8) | p[3]; ++ plen += 4; ++ if (plen > end - p) ++ goto dodgy_cert; ++ ++ key = key_create_or_update(make_key_ref(ima_keyring, 1), ++ "asymmetric", ++ NULL, ++ p, ++ plen, ++ ((KEY_POS_ALL & ~KEY_POS_SETATTR) | ++ KEY_USR_VIEW | KEY_USR_READ), ++ KEY_ALLOC_NOT_IN_QUOTA | ++ KEY_ALLOC_TRUSTED); ++ if (IS_ERR(key)) { ++ pr_err("Problem loading in-kernel X.509 certificate (%ld)\n", ++ PTR_ERR(key)); ++ } else { ++ set_bit(KEY_FLAG_BUILTIN, &key_ref_to_ptr(key)->flags); ++ pr_notice("Loaded X.509 cert '%s'\n", ++ key_ref_to_ptr(key)->description); ++ key_ref_put(key); ++ } ++ p += plen; ++ } ++ ++ return 0; ++ ++dodgy_cert: ++ pr_err("Problem parsing in-kernel X.509 IMA certificate\n"); ++ return 0; ++} ++late_initcall(load_ima_system_certificate); +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/affine-compute-kernel-threads.patch b/kernel-rt/centos/patches/affine-compute-kernel-threads.patch new file mode 100644 index 00000000..8603e7c6 --- /dev/null +++ b/kernel-rt/centos/patches/affine-compute-kernel-threads.patch @@ -0,0 +1,168 @@ +From a15588101329965ad3974bd571a9207d6a5e154a Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Chris Friesen +Date: Tue, 24 Nov 2015 16:27:28 -0500 +Subject: [PATCH 04/32] affine compute kernel threads + +This is a kernel enhancement to configure the cpu affinity of kernel +threads via kernel boot option kthread_cpus=. The compute +kickstart file and compute-huge.sh scripts will update grub with the +new option. + +With kthread_cpus specified, the cpumask is immediately applied upon +thread launch. This does not affect kernel threads that specify cpu +and node. + +Note: this is based off of Christoph Lameter's patch at +https://lwn.net/Articles/565932/ with the only difference being +the kernel parameter changed from kthread to kthread_cpus. + +Signed-off-by: Christoph Lameter +Signed-off-by: Chris Friesen +[VT: The existing "isolcpus" + kernel bootarg, cgroup/cpuset, and taskset might provide the some + way to have cpu isolation. However none of them satisfies the requirements. + Replacing spaces with tabs. Combine two calls of set_cpus_allowed_ptr() + in kernel_init_freeable() in init/main.c into one. Performed tests] +Signed-off-by: Vu Tran + +Signed-off-by: Jim Somerville +--- + Documentation/kernel-parameters.txt | 10 ++++++++++ + include/linux/cpumask.h | 2 ++ + init/main.c | 6 ++---- + kernel/cpu.c | 13 +++++++++++++ + kernel/kmod.c | 3 +++ + kernel/kthread.c | 4 ++-- + 6 files changed, 32 insertions(+), 6 deletions(-) + +diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt +index 02cfdf6..4eeda61 100644 +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -1539,6 +1539,16 @@ bytes respectively. Such letter suffixes can also be entirely omitted. + + kpti [X86-64] Enable kernel page table isolation. + ++ kthread_cpus= [KNL, SMP] Only run kernel threads on the specified ++ list of processors. The kernel will start threads ++ on the indicated processors only (unless there ++ are specific reasons to run a thread with ++ different affinities). This can be used to make ++ init start on certain processors and also to ++ control where kmod and other user space threads ++ are being spawned. Allows to keep kernel threads ++ away from certain cores unless absoluteluy necessary. ++ + kvm.ignore_msrs=[KVM] Ignore guest accesses to unhandled MSRs. + Default is 0 (don't ignore, but inject #GP) + +diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h +index ee335c6..46ae77a 100644 +--- a/include/linux/cpumask.h ++++ b/include/linux/cpumask.h +@@ -52,6 +52,7 @@ extern int nr_cpu_ids; + * cpu_present_mask - has bit 'cpu' set iff cpu is populated + * cpu_online_mask - has bit 'cpu' set iff cpu available to scheduler + * cpu_active_mask - has bit 'cpu' set iff cpu available to migration ++ * cpu_kthread_mask - has bit 'cpu' set iff general kernel threads allowed + * + * If !CONFIG_HOTPLUG_CPU, present == possible, and active == online. + * +@@ -88,6 +89,7 @@ extern const struct cpumask *const cpu_possible_mask; + extern const struct cpumask *const cpu_online_mask; + extern const struct cpumask *const cpu_present_mask; + extern const struct cpumask *const cpu_active_mask; ++extern const struct cpumask *const cpu_kthread_mask; + + #if NR_CPUS > 1 + #define num_online_cpus() cpumask_weight(cpu_online_mask) +diff --git a/init/main.c b/init/main.c +index 2e4ecd4..6e265d0 100644 +--- a/init/main.c ++++ b/init/main.c +@@ -958,10 +958,6 @@ static noinline void __init kernel_init_freeable(void) + * init can allocate pages on any node + */ + set_mems_allowed(node_states[N_MEMORY]); +- /* +- * init can run on any cpu. +- */ +- set_cpus_allowed_ptr(current, cpu_all_mask); + + cad_pid = task_pid(current); + +@@ -977,6 +973,8 @@ static noinline void __init kernel_init_freeable(void) + + do_basic_setup(); + ++ set_cpus_allowed_ptr(current, cpu_kthread_mask); ++ + /* Open the /dev/console on the rootfs, this should never fail */ + if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) + pr_err("Warning: unable to open an initial console.\n"); +diff --git a/kernel/cpu.c b/kernel/cpu.c +index 6fe84e4..325a47a 100644 +--- a/kernel/cpu.c ++++ b/kernel/cpu.c +@@ -1073,6 +1073,19 @@ static DECLARE_BITMAP(cpu_active_bits, CONFIG_NR_CPUS) __read_mostly; + const struct cpumask *const cpu_active_mask = to_cpumask(cpu_active_bits); + EXPORT_SYMBOL(cpu_active_mask); + ++static DECLARE_BITMAP(cpu_kthread_bits, CONFIG_NR_CPUS) __read_mostly ++ = CPU_BITS_ALL; ++const struct cpumask *const cpu_kthread_mask = to_cpumask(cpu_kthread_bits); ++EXPORT_SYMBOL(cpu_kthread_mask); ++ ++static int __init kthread_setup(char *str) ++{ ++ cpulist_parse(str, (struct cpumask *)&cpu_kthread_bits); ++ return 1; ++} ++__setup("kthread_cpus=", kthread_setup); ++ ++ + void set_cpu_possible(unsigned int cpu, bool possible) + { + if (possible) +diff --git a/kernel/kmod.c b/kernel/kmod.c +index 86ab754..4bf584b 100644 +--- a/kernel/kmod.c ++++ b/kernel/kmod.c +@@ -204,6 +204,9 @@ static int ____call_usermodehelper(void *data) + flush_signal_handlers(current, 1); + spin_unlock_irq(¤t->sighand->siglock); + ++ /* We can run only where init is allowed to run. */ ++ set_cpus_allowed_ptr(current, cpu_kthread_mask); ++ + /* + * Our parent is keventd, which runs with elevated scheduling priority. + * Avoid propagating that into the userspace child. +diff --git a/kernel/kthread.c b/kernel/kthread.c +index 703d910..7ea32eb 100644 +--- a/kernel/kthread.c ++++ b/kernel/kthread.c +@@ -284,7 +284,7 @@ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data), + * The kernel thread should not inherit these properties. + */ + sched_setscheduler_nocheck(create.result, SCHED_NORMAL, ¶m); +- set_cpus_allowed_ptr(create.result, cpu_all_mask); ++ set_cpus_allowed_ptr(create.result, cpu_kthread_mask); + } + return create.result; + } +@@ -454,7 +454,7 @@ int kthreadd(void *unused) + /* Setup a clean context for our children to inherit. */ + set_task_comm(tsk, "kthreadd"); + ignore_signals(tsk); +- set_cpus_allowed_ptr(tsk, cpu_all_mask); ++ set_cpus_allowed_ptr(tsk, cpu_kthread_mask); + set_mems_allowed(node_states[N_MEMORY]); + + current->flags |= PF_NOFREEZE; +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/aic94xx-Skip-reading-user-settings-if-flash-is-not-f.patch b/kernel-rt/centos/patches/aic94xx-Skip-reading-user-settings-if-flash-is-not-f.patch new file mode 100644 index 00000000..d35ed03d --- /dev/null +++ b/kernel-rt/centos/patches/aic94xx-Skip-reading-user-settings-if-flash-is-not-f.patch @@ -0,0 +1,55 @@ +From 1b9b21d80f85665b29dda49dd52d80058111d811 Mon Sep 17 00:00:00 2001 +Message-Id: <1b9b21d80f85665b29dda49dd52d80058111d811.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Hannes Reinecke +Date: Mon, 6 Jul 2015 13:07:58 +0200 +Subject: [PATCH 29/32] aic94xx: Skip reading user settings if flash is not + found + +If no user settings are found it's pointless trying to +read them from flash. So skip that step. +This also fixes a compilation warning about uninitialized variables in +aic94xx. + +Signed-off-by: Hannes Reinecke +Reviewed-by: Christoph Hellwig +Signed-off-by: James Bottomley +Signed-off-by: Jim Somerville +--- + drivers/scsi/aic94xx/aic94xx_sds.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/scsi/aic94xx/aic94xx_sds.c b/drivers/scsi/aic94xx/aic94xx_sds.c +index edb43fd..c831e30 100644 +--- a/drivers/scsi/aic94xx/aic94xx_sds.c ++++ b/drivers/scsi/aic94xx/aic94xx_sds.c +@@ -983,7 +983,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, + { + int err, i; + u32 offs, size; +- struct asd_ll_el *el; ++ struct asd_ll_el *el = NULL; + struct asd_ctrla_phy_settings *ps; + struct asd_ctrla_phy_settings dflt_ps; + +@@ -1004,6 +1004,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, + + size = sizeof(struct asd_ctrla_phy_settings); + ps = &dflt_ps; ++ goto out_process; + } + + if (size == 0) +@@ -1028,7 +1029,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, + ASD_DPRINTK("couldn't find ctrla phy settings struct\n"); + goto out2; + } +- ++out_process: + err = asd_process_ctrla_phy_settings(asd_ha, ps); + if (err) { + ASD_DPRINTK("couldn't process ctrla phy settings\n"); +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/cma-add-placement-specifier-for-cma-kernel-parameter.patch b/kernel-rt/centos/patches/cma-add-placement-specifier-for-cma-kernel-parameter.patch new file mode 100644 index 00000000..6b73a28c --- /dev/null +++ b/kernel-rt/centos/patches/cma-add-placement-specifier-for-cma-kernel-parameter.patch @@ -0,0 +1,224 @@ +From 0bce5a8a5158d690f232873efa3379bd00dfa9f5 Mon Sep 17 00:00:00 2001 +Message-Id: <0bce5a8a5158d690f232873efa3379bd00dfa9f5.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Akinobu Mita +Date: Tue, 31 May 2016 16:09:04 -0400 +Subject: [PATCH 07/32] cma: add placement specifier for "cma=" kernel + parameter + +Commit 5ea3b1b2f8ad9162684431ce6188102ca4c64b7a upstream +Backported-by: Nam Ninh + +Currently, "cma=" kernel parameter is used to specify the size of CMA, +but we can't specify where it is located. We want to locate CMA below +4GB for devices only supporting 32-bit addressing on 64-bit systems +without iommu. + +This enables to specify the placement of CMA by extending "cma=" kernel +parameter. + +Examples: + 1. locate 64MB CMA below 4GB by "cma=64M@0-4G" + 2. locate 64MB CMA exact at 512MB by "cma=64M@512M" + +Note that the DMA contiguous memory allocator on x86 assumes that +page_address() works for the pages to allocate. So this change requires +to limit end address of contiguous memory area upto max_pfn_mapped to +prevent from locating it on highmem area by the argument of +dma_contiguous_reserve(). + +Signed-off-by: Akinobu Mita +Cc: Marek Szyprowski +Cc: Konrad Rzeszutek Wilk +Cc: David Woodhouse +Cc: Don Dutile +Cc: Thomas Gleixner +Cc: Ingo Molnar +Cc: "H. Peter Anvin" +Cc: Andi Kleen +Cc: Yinghai Lu +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +Signed-off-by: Jim Somerville +--- + Documentation/kernel-parameters.txt | 7 +++++-- + arch/x86/kernel/setup.c | 2 +- + drivers/base/dma-contiguous.c | 42 ++++++++++++++++++++++++++++--------- + include/linux/dma-contiguous.h | 9 +++++--- + 4 files changed, 44 insertions(+), 16 deletions(-) + +diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt +index 4eeda61..685554b 100644 +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -579,8 +579,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. + Also note the kernel might malfunction if you disable + some critical bits. + +- cma=nn[MG] [ARM,KNL] +- Sets the size of kernel global memory area for contiguous ++ cma=nn[MG]@[start[MG][-end[MG]]] ++ [ARM,X86,KNL] ++ Sets the size of kernel global memory area for ++ contiguous memory allocations and optionally the ++ placement constraint by the physical address range of + memory allocations. For more information, see + include/linux/dma-contiguous.h + +diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c +index 9eca4ac..4e39287 100644 +--- a/arch/x86/kernel/setup.c ++++ b/arch/x86/kernel/setup.c +@@ -1283,7 +1283,7 @@ void __init setup_arch(char **cmdline_p) + setup_real_mode(); + + memblock_set_current_limit(get_max_mapped()); +- dma_contiguous_reserve(0); ++ dma_contiguous_reserve(max_pfn_mapped << PAGE_SHIFT); + + /* + * NOTE: On x86-32, only from this point on, fixmaps are ready for use. +diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c +index a0f89fc..a7d5bda 100644 +--- a/drivers/base/dma-contiguous.c ++++ b/drivers/base/dma-contiguous.c +@@ -59,11 +59,22 @@ struct cma *dma_contiguous_default_area; + */ + static const phys_addr_t size_bytes = CMA_SIZE_MBYTES * SZ_1M; + static phys_addr_t size_cmdline = -1; ++static phys_addr_t base_cmdline; ++static phys_addr_t limit_cmdline; + + static int __init early_cma(char *p) + { + pr_debug("%s(%s)\n", __func__, p); + size_cmdline = memparse(p, &p); ++ if (*p != '@') ++ return 0; ++ base_cmdline = memparse(p + 1, &p); ++ if (*p != '-') { ++ limit_cmdline = base_cmdline + size_cmdline; ++ return 0; ++ } ++ limit_cmdline = memparse(p + 1, &p); ++ + return 0; + } + early_param("cma", early_cma); +@@ -107,11 +118,18 @@ static inline __maybe_unused phys_addr_t cma_early_percent_memory(void) + void __init dma_contiguous_reserve(phys_addr_t limit) + { + phys_addr_t selected_size = 0; ++ phys_addr_t selected_base = 0; ++ phys_addr_t selected_limit = limit; ++ bool fixed = false; + + pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit); + + if (size_cmdline != -1) { + selected_size = size_cmdline; ++ selected_base = base_cmdline; ++ selected_limit = min_not_zero(limit_cmdline, limit); ++ if (base_cmdline + size_cmdline == limit_cmdline) ++ fixed = true; + } else { + #ifdef CONFIG_CMA_SIZE_SEL_MBYTES + selected_size = size_bytes; +@@ -128,10 +146,12 @@ void __init dma_contiguous_reserve(phys_addr_t limit) + pr_debug("%s: reserving %ld MiB for global area\n", __func__, + (unsigned long)selected_size / SZ_1M); + +- dma_contiguous_reserve_area(selected_size, 0, limit, +- &dma_contiguous_default_area); ++ dma_contiguous_reserve_area(selected_size, selected_base, ++ selected_limit, ++ &dma_contiguous_default_area, ++ fixed); + } +-}; ++} + + static DEFINE_MUTEX(cma_mutex); + +@@ -187,15 +207,20 @@ core_initcall(cma_init_reserved_areas); + * @base: Base address of the reserved area optional, use 0 for any + * @limit: End address of the reserved memory (optional, 0 for any). + * @res_cma: Pointer to store the created cma region. ++ * @fixed: hint about where to place the reserved area + * + * This function reserves memory from early allocator. It should be + * called by arch specific code once the early allocator (memblock or bootmem) + * has been activated and all other subsystems have already allocated/reserved + * memory. This function allows to create custom reserved areas for specific + * devices. ++ * ++ * If @fixed is true, reserve contiguous area at exactly @base. If false, ++ * reserve in range from @base to @limit. + */ + int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, +- phys_addr_t limit, struct cma **res_cma) ++ phys_addr_t limit, struct cma **res_cma, ++ bool fixed) + { + struct cma *cma = &cma_areas[cma_area_count]; + phys_addr_t alignment; +@@ -221,18 +246,15 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, + limit &= ~(alignment - 1); + + /* Reserve memory */ +- if (base) { ++ if (base && fixed) { + if (memblock_is_region_reserved(base, size) || + memblock_reserve(base, size) < 0) { + ret = -EBUSY; + goto err; + } + } else { +- /* +- * Use __memblock_alloc_base() since +- * memblock_alloc_base() panic()s. +- */ +- phys_addr_t addr = __memblock_alloc_base(size, alignment, limit); ++ phys_addr_t addr = memblock_alloc_range(size, alignment, base, ++ limit); + if (!addr) { + ret = -ENOMEM; + goto err; +diff --git a/include/linux/dma-contiguous.h b/include/linux/dma-contiguous.h +index 1421a95..5e3f586 100644 +--- a/include/linux/dma-contiguous.h ++++ b/include/linux/dma-contiguous.h +@@ -88,7 +88,8 @@ static inline void dma_contiguous_set_default(struct cma *cma) + void dma_contiguous_reserve(phys_addr_t addr_limit); + + int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, +- phys_addr_t limit, struct cma **res_cma); ++ phys_addr_t limit, struct cma **res_cma, ++ bool fixed); + + /** + * dma_declare_contiguous() - reserve area for contiguous memory handling +@@ -108,7 +109,7 @@ static inline int dma_declare_contiguous(struct device *dev, phys_addr_t size, + { + struct cma *cma; + int ret; +- ret = dma_contiguous_reserve_area(size, base, limit, &cma); ++ ret = dma_contiguous_reserve_area(size, base, limit, &cma, true); + if (ret == 0) + dev_set_cma_area(dev, cma); + +@@ -136,7 +137,9 @@ static inline void dma_contiguous_set_default(struct cma *cma) { } + static inline void dma_contiguous_reserve(phys_addr_t limit) { } + + static inline int dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, +- phys_addr_t limit, struct cma **res_cma) { ++ phys_addr_t limit, struct cma **res_cma, ++ bool fixed) ++{ + return -ENOSYS; + } + +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/cpuidle-menu-Avoid-taking-spinlock-for-accessing-QoS.patch b/kernel-rt/centos/patches/cpuidle-menu-Avoid-taking-spinlock-for-accessing-QoS.patch new file mode 100644 index 00000000..8e6ae472 --- /dev/null +++ b/kernel-rt/centos/patches/cpuidle-menu-Avoid-taking-spinlock-for-accessing-QoS.patch @@ -0,0 +1,92 @@ +From 5d7f38974c3279339c7d12d13980471fb50cf6c6 Mon Sep 17 00:00:00 2001 +Message-Id: <5d7f38974c3279339c7d12d13980471fb50cf6c6.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: "Rafael J. Wysocki" +Date: Fri, 24 Feb 2017 13:25:14 +0100 +Subject: [PATCH 24/32] cpuidle: menu: Avoid taking spinlock for accessing QoS + values + +[commit 6dbf5cea05a7098a69f294c96b6d76f08562cae5 from linux-stable ] + +After commit 9908859acaa9 (cpuidle/menu: add per CPU PM QoS resume +latency consideration) the cpuidle menu governor calls +dev_pm_qos_read_value() on CPU devices to read the current resume latency QoS +constraint values for them. That function takes a spinlock to prevent the +device's power.qos pointer from becoming NULL during the access which is a +problem for the RT patchset where spinlocks are converted into mutexes and +the idle loop stops working. + +However, it is not even necessary for the menu governor to take +that spinlock, because the power.qos pointer accessed under it +cannot be modified during the access anyway. + +For this reason, introduce a "raw" routine for accessing device +QoS resume latency constraints without locking and use it in the +menu governor. + +Fixes: 9908859acaa9 (cpuidle/menu: add per CPU PM QoS resume latency consideration) +Acked-by: Alex Shi +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Alex Kozyrev +Signed-off-by: Jim Somerville +--- + drivers/base/power/qos.c | 3 +-- + drivers/cpuidle/governors/menu.c | 2 +- + include/linux/pm_qos.h | 6 ++++++ + 3 files changed, 8 insertions(+), 3 deletions(-) + +diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c +index b2ca302..0cc2a13 100644 +--- a/drivers/base/power/qos.c ++++ b/drivers/base/power/qos.c +@@ -104,8 +104,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_flags); + */ + s32 __dev_pm_qos_read_value(struct device *dev) + { +- return IS_ERR_OR_NULL(dev->power.qos) ? +- 0 : pm_qos_read_value(&dev->power.qos->latency); ++ return dev_pm_qos_raw_read_value(dev); + } + + /** +diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c +index fe2dcb8..f9861fd 100644 +--- a/drivers/cpuidle/governors/menu.c ++++ b/drivers/cpuidle/governors/menu.c +@@ -265,7 +265,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) + int i; + int multiplier; + struct timespec t; +- int resume_latency = dev_pm_qos_read_value(device); ++ int resume_latency = dev_pm_qos_raw_read_value(device); + + if (data->needs_update) { + menu_update(drv, dev); +diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h +index 5281e7f..1d8b629 100644 +--- a/include/linux/pm_qos.h ++++ b/include/linux/pm_qos.h +@@ -217,6 +217,11 @@ static inline s32 dev_pm_qos_requested_flags(struct device *dev) + { + return dev->power.qos->flags_req->data.flr.flags; + } ++static inline s32 dev_pm_qos_raw_read_value(struct device *dev) ++{ ++ return IS_ERR_OR_NULL(dev->power.qos) ? ++ 0 : pm_qos_read_value(&dev->power.qos->latency); ++} + #else + static inline int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) + { return 0; } +@@ -236,6 +241,7 @@ static inline void dev_pm_qos_hide_latency_tolerance(struct device *dev) {} + + static inline s32 dev_pm_qos_requested_latency(struct device *dev) { return 0; } + static inline s32 dev_pm_qos_requested_flags(struct device *dev) { return 0; } ++static inline s32 dev_pm_qos_raw_read_value(struct device *dev) { return 0; } + #endif + + #endif +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/cpuidle-menu-add-per-CPU-PM-QoS-resume-latency-consi.patch b/kernel-rt/centos/patches/cpuidle-menu-add-per-CPU-PM-QoS-resume-latency-consi.patch new file mode 100644 index 00000000..c066990b --- /dev/null +++ b/kernel-rt/centos/patches/cpuidle-menu-add-per-CPU-PM-QoS-resume-latency-consi.patch @@ -0,0 +1,71 @@ +From f5020c58583d04f0c6f8df1cbbd7d2e747fcb537 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Alex Shi +Date: Thu, 12 Jan 2017 21:27:04 +0800 +Subject: [PATCH 22/32] cpuidle/menu: add per CPU PM QoS resume latency + consideration + +[ commit 9908859acaa95640d4a07991a93f7cd5bfc18e02 from linux-stable ] + +There may be special requirements on CPU response time, like if +a interrupt is pinned to a CPU, that CPU should not go into excessively deep +idle states. For this reason, add a mechanism for adding PM QoS resume +latency constraints for individual CPUs and modify the menu governor to take +them into account. + +To that end, extend the device PM QoS pm_qos_resume_latency attribute +to CPUs, which is possible, because the exit latency for CPUs is +effectively equivalent to the resume latency for devices. + +Signed-off-by: Alex Shi +Acked-by: Rik van Riel +[ rjw : Subject & changelog ] +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Alex Kozyrev + +Signed-off-by: Jim Somerville +--- + drivers/cpuidle/governors/menu.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c +index eb9fb0e..fe2dcb8 100644 +--- a/drivers/cpuidle/governors/menu.c ++++ b/drivers/cpuidle/governors/menu.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include + + #define BUCKETS 12 +@@ -259,10 +260,12 @@ again: + static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) + { + struct menu_device *data = &__get_cpu_var(menu_devices); ++ struct device *device = get_cpu_device(dev->cpu); + int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); + int i; + int multiplier; + struct timespec t; ++ int resume_latency = dev_pm_qos_read_value(device); + + if (data->needs_update) { + menu_update(drv, dev); +@@ -271,6 +274,10 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) + + data->exit_us = 0; + ++ /* resume_latency is 0 means no restriction */ ++ if (resume_latency && resume_latency < latency_req) ++ latency_req = resume_latency; ++ + /* Special case when user has set very strict latency requirement */ + if (unlikely(latency_req == 0)) + return 0; +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/cpuidle-menu-stop-seeking-deeper-idle-if-current-sta.patch b/kernel-rt/centos/patches/cpuidle-menu-stop-seeking-deeper-idle-if-current-sta.patch new file mode 100644 index 00000000..bd3bb72c --- /dev/null +++ b/kernel-rt/centos/patches/cpuidle-menu-stop-seeking-deeper-idle-if-current-sta.patch @@ -0,0 +1,52 @@ +From 9218064183a57581be8b7b980ce4b8ac9821555b Mon Sep 17 00:00:00 2001 +Message-Id: <9218064183a57581be8b7b980ce4b8ac9821555b.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Alex Shi +Date: Thu, 12 Jan 2017 21:27:02 +0800 +Subject: [PATCH 21/32] cpuidle/menu: stop seeking deeper idle if current state + is deep enough + +[ commit 8e37e1a2a3295f5d99e6dbe99eca24eca7a034ef from linux-stable ] + +Obsolete commit 71abbbf856a0 (cpuidle: extend cpuidle and menu +governor to handle dynamic states) wanted to introduce dynamic C-states, but +that idea was dropped long ago. The nonsense deeper C-state checking +remained, though. + +Since both target_residency and exit_latency are longer for deeper +idle state, there's no need to waste CPU time on useless checks. + +Signed-off-by: Alex Shi +Acked-by: Rik van Riel +[ rjw: Subject & changelog ] +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Alex Kozyrev + +Signed-off-by: Jim Somerville +--- + drivers/cpuidle/governors/menu.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c +index c99fee9..eb9fb0e 100644 +--- a/drivers/cpuidle/governors/menu.c ++++ b/drivers/cpuidle/governors/menu.c +@@ -323,11 +323,11 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) + if (s->disabled || su->disable) + continue; + if (s->target_residency > data->predicted_us) +- continue; ++ break; + if (s->exit_latency > latency_req) +- continue; ++ break; + if (s->exit_latency * multiplier > data->predicted_us) +- continue; ++ break; + + data->last_state_idx = i; + data->exit_us = s->exit_latency; +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/cpupower.config b/kernel-rt/centos/patches/cpupower.config new file mode 100644 index 00000000..8629a4a3 --- /dev/null +++ b/kernel-rt/centos/patches/cpupower.config @@ -0,0 +1,3 @@ +# See 'cpupower help' and cpupower(1) for more info +CPUPOWER_START_OPTS="frequency-set -g performance" +CPUPOWER_STOP_OPTS="frequency-set -g ondemand" diff --git a/kernel-rt/centos/patches/cpupower.service b/kernel-rt/centos/patches/cpupower.service new file mode 100644 index 00000000..5f10ab7e --- /dev/null +++ b/kernel-rt/centos/patches/cpupower.service @@ -0,0 +1,13 @@ +[Unit] +Description=Configure CPU power related settings +After=syslog.target + +[Service] +Type=oneshot +RemainAfterExit=yes +EnvironmentFile=/etc/sysconfig/cpupower +ExecStart=/usr/bin/cpupower $CPUPOWER_START_OPTS +ExecStop=/usr/bin/cpupower $CPUPOWER_STOP_OPTS + +[Install] +WantedBy=multi-user.target diff --git a/kernel-rt/centos/patches/debrand-rh-i686-cpu.patch b/kernel-rt/centos/patches/debrand-rh-i686-cpu.patch new file mode 100644 index 00000000..930fdb7c --- /dev/null +++ b/kernel-rt/centos/patches/debrand-rh-i686-cpu.patch @@ -0,0 +1,29 @@ +From 7d1684c010cbbc13b80056d53932ef62d280dc3f Mon Sep 17 00:00:00 2001 +Message-Id: <7d1684c010cbbc13b80056d53932ef62d280dc3f.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Jim Somerville +Date: Tue, 3 Apr 2018 18:07:37 -0400 +Subject: [PATCH 03/32] debrand rh i686 cpu + +Signed-off-by: Jim Somerville +--- + arch/x86/boot/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/x86/boot/main.c b/arch/x86/boot/main.c +index 0d12b19..99dcb04 100644 +--- a/arch/x86/boot/main.c ++++ b/arch/x86/boot/main.c +@@ -147,7 +147,7 @@ void main(void) + + /* Make sure we have all the proper CPU support */ + if (validate_cpu()) { +- puts("This processor is unsupported in RHEL7.\n"); ++ puts("This processor is unsupported in CentOS 7.\n"); + die(); + } + +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/debrand-rh_taint.patch b/kernel-rt/centos/patches/debrand-rh_taint.patch new file mode 100644 index 00000000..e86a6b9e --- /dev/null +++ b/kernel-rt/centos/patches/debrand-rh_taint.patch @@ -0,0 +1,29 @@ +From 71f6d30a1acd723dfbb721fb690efb082d9fc2e0 Mon Sep 17 00:00:00 2001 +Message-Id: <71f6d30a1acd723dfbb721fb690efb082d9fc2e0.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Jim Perrin +Date: Thu, 19 Jun 2014 10:05:12 -0500 +Subject: [PATCH 02/32] debrand rh_taint + +Signed-off-by: Jim Somerville +--- + kernel/rh_taint.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/kernel/rh_taint.c b/kernel/rh_taint.c +index 22f0324..28c369f 100644 +--- a/kernel/rh_taint.c ++++ b/kernel/rh_taint.c +@@ -24,7 +24,7 @@ + void mark_hardware_unsupported(const char *msg) + { + /* Print one single message */ +- pr_crit("Warning: %s - this hardware has not undergone testing by Red Hat and might not be certified. Please consult https://hardware.redhat.com for certified hardware.\n", msg); ++ pr_crit("Warning: %s - this hardware has not undergone upstream testing. Please consult http://wiki.centos.org/FAQ for more information\n", msg); + } + EXPORT_SYMBOL(mark_hardware_unsupported); + +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/debrand-single-cpu.patch b/kernel-rt/centos/patches/debrand-single-cpu.patch new file mode 100644 index 00000000..1845131e --- /dev/null +++ b/kernel-rt/centos/patches/debrand-single-cpu.patch @@ -0,0 +1,27 @@ +From c8270e79f6b7008fde44b8d5aa6314d8cf89d3ed Mon Sep 17 00:00:00 2001 +Message-Id: +From: Jim Perrin +Date: Thu, 19 Jun 2014 09:53:13 -0500 +Subject: [PATCH 01/32] debrand single cpu + +Signed-off-by: Jim Somerville +--- + arch/x86/kernel/setup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c +index f27ca00..9eca4ac 100644 +--- a/arch/x86/kernel/setup.c ++++ b/arch/x86/kernel/setup.c +@@ -922,7 +922,7 @@ static void rh_check_supported(void) + if (((boot_cpu_data.x86_max_cores * smp_num_siblings) == 1) && + !x86_hyper && !cpu_has_hypervisor && !is_kdump_kernel()) { + pr_crit("Detected single cpu native boot.\n"); +- pr_crit("Important: In Red Hat Enterprise Linux 7, single threaded, single CPU 64-bit physical systems are unsupported by Red Hat. Please contact your Red Hat support representative for a list of certified and supported systems."); ++ pr_crit("Important: In CentOS 7, single threaded, single CPU 64-bit physical systems are unsupported. Please see http://wiki.centos.org/FAQ for more information"); + } + + /* The RHEL7 kernel does not support this hardware. The kernel will +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/dpt_i2o-fix-build-warning.patch b/kernel-rt/centos/patches/dpt_i2o-fix-build-warning.patch new file mode 100644 index 00000000..ac1b7522 --- /dev/null +++ b/kernel-rt/centos/patches/dpt_i2o-fix-build-warning.patch @@ -0,0 +1,44 @@ +From 143c2720aaa898f6e4cd53598808ff1230c33cd8 Mon Sep 17 00:00:00 2001 +Message-Id: <143c2720aaa898f6e4cd53598808ff1230c33cd8.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Sudip Mukherjee +Date: Thu, 18 Feb 2016 13:59:13 +0530 +Subject: [PATCH 30/32] dpt_i2o: fix build warning + +We were getting build warning about: +drivers/scsi/dpt_i2o.c:183:29: warning: 'dptids' defined but not used + +dptids[] is only used in the MODULE_DEVICE_TABLE so when MODULE is not +defined then dptids[] becomes unused. + +Signed-off-by: Sudip Mukherjee +Reviewed-by: Johannes Thumshirn +Signed-off-by: Martin K. Petersen +Signed-off-by: Jim Somerville +--- + drivers/scsi/dpt_i2o.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c +index 2bce881..cb62223 100644 +--- a/drivers/scsi/dpt_i2o.c ++++ b/drivers/scsi/dpt_i2o.c +@@ -180,11 +180,14 @@ static u8 adpt_read_blink_led(adpt_hba* host) + *============================================================================ + */ + ++#ifdef MODULE + static struct pci_device_id dptids[] = { + { PCI_DPT_VENDOR_ID, PCI_DPT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + { PCI_DPT_VENDOR_ID, PCI_DPT_RAPTOR_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + { 0, } + }; ++#endif ++ + MODULE_DEVICE_TABLE(pci,dptids); + + static int adpt_detect(struct scsi_host_template* sht) +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/fix-compilation-issues.patch b/kernel-rt/centos/patches/fix-compilation-issues.patch new file mode 100644 index 00000000..11b15ce3 --- /dev/null +++ b/kernel-rt/centos/patches/fix-compilation-issues.patch @@ -0,0 +1,27 @@ +From 8b830ee6b0abaae23ca437b0f37e640f9b64582a Mon Sep 17 00:00:00 2001 +Message-Id: <8b830ee6b0abaae23ca437b0f37e640f9b64582a.1528231742.git.Jim.Somerville@windriver.com> +From: Jim Somerville +Date: Wed, 30 May 2018 13:06:58 -0400 +Subject: [PATCH 1/1] fix compilation issues + +Signed-off-by: Jim Somerville +--- + drivers/base/dma-contiguous.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c +index a7d5bda..403101d 100644 +--- a/drivers/base/dma-contiguous.c ++++ b/drivers/base/dma-contiguous.c +@@ -309,7 +309,7 @@ struct page *dma_alloc_from_contiguous(struct device *dev, size_t count, + if (align > CONFIG_CMA_ALIGNMENT) + align = CONFIG_CMA_ALIGNMENT; + +- pr_debug("%s(cma %p, count %d, align %d)\n", __func__, (void *)cma, ++ pr_debug("%s(cma %p, count %zu, align %d)\n", __func__, (void *)cma, + count, align); + + if (!count) +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/intel-iommu-allow-ignoring-Ethernet-device-RMRR-with.patch b/kernel-rt/centos/patches/intel-iommu-allow-ignoring-Ethernet-device-RMRR-with.patch new file mode 100644 index 00000000..89920792 --- /dev/null +++ b/kernel-rt/centos/patches/intel-iommu-allow-ignoring-Ethernet-device-RMRR-with.patch @@ -0,0 +1,120 @@ +From 02f7bfe8cb36d7903bd7904b05071a2c91bc02a1 Mon Sep 17 00:00:00 2001 +Message-Id: <02f7bfe8cb36d7903bd7904b05071a2c91bc02a1.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Matt Peters +Date: Mon, 30 May 2016 10:51:02 -0400 +Subject: [PATCH 08/32] intel-iommu: allow ignoring Ethernet device RMRR with + IOMMU passthrough + +Some BIOS's are reporting DMAR RMRR entries for Ethernet devices +which is causing problems when PCI passthrough is enabled. These +devices should be able to use the static identity map since the +host should not be enforcing specific address ranges when IOMMU +passthrough is enabled. + +Originally-by: Matt Peters +[PG: Added bootarg wrapper and documentation entries.] +Signed-off-by: Paul Gortmaker +Signed-off-by: Nam Ninh + +Signed-off-by: Nam Ninh +Signed-off-by: Jim Somerville +--- + Documentation/Intel-IOMMU.txt | 18 ++++++++++++++++++ + Documentation/kernel-parameters.txt | 5 +++++ + drivers/iommu/intel-iommu.c | 19 +++++++++++++++++++ + 3 files changed, 42 insertions(+) + +diff --git a/Documentation/Intel-IOMMU.txt b/Documentation/Intel-IOMMU.txt +index cf9431d..1dcc349 100644 +--- a/Documentation/Intel-IOMMU.txt ++++ b/Documentation/Intel-IOMMU.txt +@@ -32,6 +32,24 @@ regions will fail. Hence BIOS uses RMRR to specify these regions along with + devices that need to access these regions. OS is expected to setup + unity mappings for these regions for these devices to access these regions. + ++RMRR for other devices? ++----------------------- ++ ++There are reports of BIOS out there that indicate RMRR regions for things ++like ethernet devices. As per mainline commit c875d2c1b8083 ("iommu/vt-d: ++ Exclude devices using RMRRs from IOMMU API domains") such a device is ++"fundamentally incompatible" with the IOMMU API and "we must prevent such ++devices from being used by the IOMMU API." However, in the event that ++the RMRR indicated by the BIOS is assumed to be just a reporting error, ++there is an additional iommu boot arg that can be used to ignore RMRR ++settings for ethernet, i.e. "intel_iommu=on,eth_no_rmrr iommu=pt". ++Note that iommu=pt is required in order to eth_no_rmrr to have effect. ++ ++If you use this setting, you should consult with your hardware vendor to ++confirm that it is just a reporting error, and that it truly is not ++actively using any DMA to/from RMRR, as otherwise system instability ++may result. ++ + How is IOVA generated? + --------------------- + +diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt +index 685554b..0ca635a 100644 +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -1306,6 +1306,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. + than 32-bit addressing. The default is to look + for translation below 32-bit and if not available + then look in the higher range. ++ eth_no_rmrr [Default Off] ++ With this option provided, the kernel will ignore ++ any specified RMRR regions specified by the BIOS ++ for PCI ethernet devices. Confirm with your hardware ++ vendor the RMRR regions are indeed invalid first. + strict [Default Off] + With this option on every unmap_single operation will + result in a hardware IOTLB flush operation as opposed +diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c +index 05b0971..d6f4723 100644 +--- a/drivers/iommu/intel-iommu.c ++++ b/drivers/iommu/intel-iommu.c +@@ -504,6 +504,7 @@ static int dmar_forcedac; + static int intel_iommu_strict; + static int intel_iommu_superpage = 1; + static int intel_iommu_ecs = 1; ++static int intel_iommu_ethrmrr = 1; + + /* We only actually use ECS when PASID support (on the new bit 40) + * is also advertised. Some early implementations — the ones with +@@ -563,6 +564,15 @@ static int __init intel_iommu_setup(char *str) + } else if (!strncmp(str, "forcedac", 8)) { + pr_info("Forcing DAC for PCI devices\n"); + dmar_forcedac = 1; ++ } else if (!strncmp(str, "eth_no_rmrr", 11)) { ++ if (!iommu_pass_through) { ++ printk(KERN_WARNING ++ "Intel-IOMMU: error - eth_no_rmrr requires iommu=pt\n"); ++ } else { ++ printk(KERN_INFO ++ "Intel-IOMMU: ignoring ethernet RMRR values\n"); ++ intel_iommu_ethrmrr = 0; ++ } + } else if (!strncmp(str, "strict", 6)) { + pr_info("Disable batched IOTLB flush\n"); + intel_iommu_strict = 1; +@@ -2733,6 +2743,15 @@ static bool device_is_rmrr_locked(struct device *dev) + + if (IS_USB_DEVICE(pdev) || IS_GFX_DEVICE(pdev)) + return false; ++ /* As a temporary workaround for issues seen on ProLiant DL380p, ++ * allow the operator to ignore the RMRR settings for ethernet ++ * devices. Ideally the end user should contact their vendor ++ * regarding why there are RMRR, as per mainline c875d2c1b8083 ++ * ("iommu/vt-d: Exclude devices using RMRRs from IOMMU API domains") ++ * it seems that these make no sense at all. ++ */ ++ if ((pdev->class >> 8) == PCI_CLASS_NETWORK_ETHERNET && !intel_iommu_ethrmrr) ++ return false; + } + + return true; +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/kernel-3.10.0-x86_64-rt-debug.config.tis_extra b/kernel-rt/centos/patches/kernel-3.10.0-x86_64-rt-debug.config.tis_extra new file mode 100644 index 00000000..d6ec97ca --- /dev/null +++ b/kernel-rt/centos/patches/kernel-3.10.0-x86_64-rt-debug.config.tis_extra @@ -0,0 +1,6 @@ +CONFIG_SIGEXIT=y +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=16 +CONFIG_CMA_SIZE_SEL_MBYTES=y +CONFIG_CMA_ALIGNMENT=8 +CONFIG_CMA_AREAS=7 diff --git a/kernel-rt/centos/patches/kernel-3.10.0-x86_64-rt-trace.config.tis_extra b/kernel-rt/centos/patches/kernel-3.10.0-x86_64-rt-trace.config.tis_extra new file mode 100644 index 00000000..d6ec97ca --- /dev/null +++ b/kernel-rt/centos/patches/kernel-3.10.0-x86_64-rt-trace.config.tis_extra @@ -0,0 +1,6 @@ +CONFIG_SIGEXIT=y +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=16 +CONFIG_CMA_SIZE_SEL_MBYTES=y +CONFIG_CMA_ALIGNMENT=8 +CONFIG_CMA_AREAS=7 diff --git a/kernel-rt/centos/patches/kernel-3.10.0-x86_64-rt.config.tis_extra b/kernel-rt/centos/patches/kernel-3.10.0-x86_64-rt.config.tis_extra new file mode 100644 index 00000000..2756308b --- /dev/null +++ b/kernel-rt/centos/patches/kernel-3.10.0-x86_64-rt.config.tis_extra @@ -0,0 +1,947 @@ +# Add builtin +CONFIG_MAXSMP=n +CONFIG_NR_CPUS=256 +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_SR=y +CONFIG_SCSI_SAS_ATTRS=y +CONFIG_ISCSI_TCP=y +# SCSI Related Drivers +# Let's enable lots of them, pretty much anything RAID capable +CONFIG_ATA=y +CONFIG_SCSI_SAS_LIBSAS=y +CONFIG_SCSI_SAS_ATA=y +CONFIG_BLK_DEV_3W_XXXX_RAID=y +CONFIG_SCSI_3W_9XXX=y +CONFIG_SCSI_3W_SAS=y +CONFIG_SCSI_AACRAID=y +CONFIG_SCSI_DPT_I2O=y +CONFIG_SCSI_ARCMSR=y +CONFIG_SCSI_HPTIOP=y +CONFIG_SCSI_GDTH=y +CONFIG_SCSI_IPS=y +CONFIG_SCSI_STEX=y +CONFIG_SCSI_PMCRAID=y +CONFIG_SCSI_HPSA=y +CONFIG_MEGARAID_SAS=y +CONFIG_SCSI_SMARTPQI=y +CONFIG_SCSI_MPT2SAS=y +CONFIG_SCSI_MPT3SAS=y +CONFIG_SCSI_VIRTIO=y +CONFIG_FUSION_SAS=y +CONFIG_SCSI_AIC94XX=y +CONFIG_SCSI_MVSAS=y +# +CONFIG_SOFT_WATCHDOG=y +CONFIG_VIRTIO=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_MMIO=y +CONFIG_ISO9660_FS=y +CONFIG_VFAT_FS=y +CONFIG_NLS_ISO8859_1=y +#CONFIG_MCORE2=y +CONFIG_SIGEXIT=y +CONFIG_SCHEDSTATS=y + +# Enable runtime Huge TLB support +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=16 +CONFIG_CMA_SIZE_SEL_MBYTES=y +CONFIG_CMA_ALIGNMENT=8 +CONFIG_CMA_AREAS=7 + +# Turn on Intel IOMMU +CONFIG_INTEL_IOMMU_DEFAULT_ON=y + +# Turn off network drivers that we want +# to build out-of-tree +CONFIG_I40E=n +CONFIG_I40EVF=n +CONFIG_IXGB=n +CONFIG_IXGBE=n +CONFIG_IXGBEVF=n +CONFIG_MLX4_EN=n +CONFIG_MLX4_CORE=n +CONFIG_MLX5_EN=n +CONFIG_MLX5_CORE=n + +# Turn off TPM drivers that we want +# to build out-of-tree. This will +# disable the TPM HW-RandomNUmberGenerator(RNG) +# and TrustedKeys modules as well, since +# they require the in-kernel TPM driver. +# Both these modules will also need to be +# built out-of-tree when needed. +CONFIG_TCG_TPM=n +CONFIG_TCG_TIS=n +CONFIG_HW_RANDOM_TPM=n +CONFIG_TRUSTED_KEYS=n +CONFIG_TCG_TIS_I2C_ATMEL=n +CONFIG_TCG_TIS_I2C_INFINEON=n +CONFIG_TCG_TIS_I2C_NUVOTON=n +CONFIG_TCG_NSC=n +CONFIG_TCG_ATMEL=n +CONFIG_TCG_INFINEON=n +CONFIG_TCG_CRB=n +CONFIG_TCG_TIS_ST33ZP24=n +CONFIG_TCG_TIS_ST33ZP24_I2C=n + +# Also disable TPM Integrity Measurement Architecture +# (IMA), as this will be built out of tree but ensure +# that all their dependencies (that comes from the base kernel) +# that are marked as "select" within the Integrity / IMA +# Kconfigs are still enabled +CONFIG_INTEGRITY=n +CONFIG_IMA=n +CONFIG_EVM=n +CONFIG_SIGNATURE=y +CONFIG_KEYS=y +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_PUBLIC_KEY_ALGO_RSA=y +CONFIG_X509_CERTIFICATE_PARSER=y +CONFIG_SECURITYFS=y +CONFIG_CRYPTO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA1=y + +# Remove unneeded stuff (including stuff exposed +# by saying y to new options above. +CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=n +CONFIG_PANIC_ON_OOPS=n +CONFIG_SOUND=n +CONFIG_FIREWIRE=n +CONFIG_KPROBES=n +CONFIG_XEN=n +CONFIG_BT=n +CONFIG_INTEL_MEI=n +CONFIG_USB_USBNET=n +CONFIG_RTL8187=n +CONFIG_MWL8K=n +CONFIG_CFG80211=n +CONFIG_WAN=n +CONFIG_ISDN=n +CONFIG_INPUT_TOUCHSCREEN=n +CONFIG_SSB=n +CONFIG_BCMA=n +CONFIG_MEDIA_SUPPORT=n +CONFIG_ACPI_PROCESSOR_AGGREGATOR=n +CONFIG_ACPI_EXTLOG=n +CONFIG_NET_FOU=n +CONFIG_NET_FOU_IP_TUNNELS=n +CONFIG_GENEVE=n +CONFIG_IPV6_VTI=n +CONFIG_NETFILTER_XT_TARGET_TPROXY=n +CONFIG_NETFILTER_XT_MATCH_CGROUP=n +CONFIG_NETFILTER_XT_MATCH_SOCKET=n +CONFIG_NET_SCH_FQ=n +CONFIG_NET_CLS_BPF=n +CONFIG_BLK_DEV_NULL_BLK=n +CONFIG_GENWQE=n +CONFIG_CXL_BASE=n +CONFIG_DM_ERA=n +CONFIG_DM_RAID=n +CONFIG_DM_SWITCH=n +CONFIG_NLMON=n +CONFIG_FM10K=n +CONFIG_CRASH=n +CONFIG_IPMI_SSIF=n +CONFIG_POWERCAP=n +CONFIG_DRM_BOCHS=n +CONFIG_HID_RMI=n +CONFIG_NET_DMA_RH_KABI=n +CONFIG_HP_WIRELESS=n +CONFIG_NFSD_V4_SECURITY_LABEL=n +CONFIG_DEBUG_SHIRQ=n +CONFIG_PERSISTENT_KEYRINGS=n +CONFIG_BIG_KEYS=n +CONFIG_SECURITY_SECURELEVEL=n +CONFIG_CRYPTO_MCRYPTD=n +CONFIG_CRYPTO_SHA1_MB=n +CONFIG_CRYPTO_DRBG_MENU=n +CONFIG_CRYPTO_DEV_QAT=n +CONFIG_CRYPTO_DEV_QAT_DH895xCCVF=n +CONFIG_CRYPTO_DEV_QAT_DH895xCC=n +CONFIG_CRYPTO_DEV_QAT_C3XXX=n +CONFIG_CRYPTO_DEV_QAT_C62X=n +CONFIG_CRYPTO_DEV_QAT_C3XXXVF=n +CONFIG_CRYPTO_DEV_QAT_C62XVF=n +CONFIG_BPF_JIT=n +CONFIG_PARPORT=n +CONFIG_CDROM_PKTCDVD=n +CONFIG_SENSORS_LIS3LV02D=n +CONFIG_SGI_IOC4=n +CONFIG_TIFM_CORE=n +CONFIG_ENCLOSURE_SERVICES=n +CONFIG_APDS9802ALS=n +CONFIG_ISL29003=n +CONFIG_ISL29020=n +CONFIG_SENSORS_TSL2550=n +CONFIG_SENSORS_BH1770=n +CONFIG_SENSORS_APDS990X=n +CONFIG_PCH_PHUB=n +CONFIG_EEPROM_AT24=n +CONFIG_EEPROM_MAX6875=n +CONFIG_EEPROM_93CX6=n +CONFIG_CB710_CORE=n +CONFIG_SENSORS_LIS3_I2C=n +CONFIG_ALTERA_STAPL=n +CONFIG_VMWARE_VMCI=n +CONFIG_CHR_DEV_ST=n +CONFIG_CHR_DEV_OSST=n +CONFIG_CHR_DEV_SG=n +CONFIG_CHR_DEV_SCH=n +CONFIG_SCSI_CONSTANTS=n +CONFIG_SCSI_LOGGING=n +CONFIG_SCSI_CXGB3_ISCSI=n +CONFIG_SCSI_CXGB4_ISCSI=n +CONFIG_SCSI_AIC79XX=n +CONFIG_SCSI_MVUMI=n +CONFIG_SCSI_MPT2SAS_LOGGING=n +CONFIG_SCSI_MPT3SAS_LOGGING=n +CONFIG_SCSI_UFSHCD=n +CONFIG_VMWARE_PVSCSI=n +CONFIG_FCOE=n +CONFIG_FCOE_FNIC=n +CONFIG_SCSI_INITIO=n +CONFIG_SCSI_PM8001=n +CONFIG_SCSI_SRP=n +CONFIG_SATA_ACARD_AHCI=n +CONFIG_SATA_SIL24=n +CONFIG_PDC_ADMA=n +CONFIG_SATA_QSTOR=n +CONFIG_SATA_SX4=n +CONFIG_SATA_MV=n +CONFIG_SATA_NV=n +CONFIG_SATA_PROMISE=n +CONFIG_SATA_SIL=n +CONFIG_SATA_SIS=n +CONFIG_SATA_SVW=n +CONFIG_SATA_ULI=n +CONFIG_SATA_VIA=n +CONFIG_SATA_VITESSE=n +CONFIG_PATA_ALI=n +CONFIG_PATA_AMD=n +CONFIG_PATA_ARASAN_CF=n +CONFIG_PATA_ARTOP=n +CONFIG_PATA_ATIIXP=n +CONFIG_PATA_ATP867X=n +CONFIG_PATA_CMD64X=n +CONFIG_PATA_CS5536=n +CONFIG_PATA_HPT366=n +CONFIG_PATA_HPT37X=n +CONFIG_PATA_HPT3X2N=n +CONFIG_PATA_HPT3X3=n +CONFIG_PATA_IT8213=n +CONFIG_PATA_IT821X=n +CONFIG_PATA_JMICRON=n +CONFIG_PATA_MARVELL=n +CONFIG_PATA_NETCELL=n +CONFIG_PATA_NINJA32=n +CONFIG_PATA_OLDPIIX=n +CONFIG_PATA_PDC2027X=n +CONFIG_PATA_PDC_OLD=n +CONFIG_PATA_RDC=n +CONFIG_PATA_SCH=n +CONFIG_PATA_SERVERWORKS=n +CONFIG_PATA_SIL680=n +CONFIG_PATA_SIS=n +CONFIG_PATA_TOSHIBA=n +CONFIG_PATA_VIA=n +CONFIG_DM_DEBUG=n +CONFIG_MACINTOSH_DRIVERS=n +CONFIG_NET_FC=n +CONFIG_NET_TEAM=n +CONFIG_ATL2=n +CONFIG_ATL1=n +CONFIG_ATL1E=n +CONFIG_ATL1C=n +CONFIG_ALX=n +CONFIG_ARM_AT91_ETHER=n +CONFIG_MACB=n +CONFIG_B44=n +CONFIG_BNA=n +CONFIG_NET_CALXEDA_XGMAC=n +CONFIG_CHELSIO_T3=n +CONFIG_CHELSIO_T4=n +CONFIG_CHELSIO_T4VF=n +CONFIG_NET_TULIP=n +CONFIG_IP1000=n +CONFIG_JME=n +CONFIG_MVMDIO=n +CONFIG_SKGE=n +CONFIG_SKY2=n +CONFIG_MYRI10GE=n +CONFIG_PCH_GBE=n +CONFIG_ETHOC=n +CONFIG_QLCNIC=n +CONFIG_QLGE=n +CONFIG_NETXEN_NIC=n +CONFIG_SFC=n +CONFIG_EPIC100=n +CONFIG_SMSC9420=n +CONFIG_AT803X_PHY=n +CONFIG_DAVICOM_PHY=n +CONFIG_QSEMI_PHY=n +CONFIG_LXT_PHY=n +CONFIG_CICADA_PHY=n +CONFIG_VITESSE_PHY=n +CONFIG_SMSC_PHY=n +CONFIG_BCM87XX_PHY=n +CONFIG_ICPLUS_PHY=n +CONFIG_REALTEK_PHY=n +CONFIG_NATIONAL_PHY=n +CONFIG_STE10XP=n +CONFIG_LSI_ET1011C_PHY=n +CONFIG_MICREL_PHY=n +CONFIG_MDIO_BITBANG=n +CONFIG_RT_GROUP_SCHED=n +CONFIG_OPROFILE=n +CONFIG_SYSTEM_BLACKLIST_KEYRING=n +CONFIG_OSF_PARTITION=n +CONFIG_AMIGA_PARTITION=n +CONFIG_MAC_PARTITION=n +CONFIG_BSD_DISKLABEL=n +CONFIG_MINIX_SUBPARTITION=n +CONFIG_SOLARIS_X86_PARTITION=n +CONFIG_UNIXWARE_DISKLABEL=n +CONFIG_SGI_PARTITION=n +CONFIG_SUN_PARTITION=n +CONFIG_KARMA_PARTITION=n +CONFIG_X86_UV=n +CONFIG_I8K=n +CONFIG_MICROCODE_AMD=n +CONFIG_MICROCODE_AMD_EARLY=n +CONFIG_MOVABLE_NODE=n +CONFIG_MEMORY_HOTPLUG=n +CONFIG_BOOTPARAM_HOTPLUG_CPU0=n +CONFIG_X86_POWERNOW_K8=n +CONFIG_X86_AMD_FREQ_SENSITIVITY=n +CONFIG_X86_P4_CLOCKMOD=n +CONFIG_PCIE_ECRC=n +CONFIG_PCIEAER_INJECT=n +CONFIG_PCCARD=n +CONFIG_HOTPLUG_PCI_ACPI_IBM=n +CONFIG_HOTPLUG_PCI_SHPC=n +CONFIG_XFRM_STATISTICS=n +CONFIG_IP_FIB_TRIE_STATS=n +CONFIG_PPP_MPPE=n +CONFIG_USB_CATC=n +CONFIG_USB_KAWETH=n +CONFIG_USB_PEGASUS=n +CONFIG_USB_RTL8150=n +CONFIG_USB_RTL8152=n +CONFIG_USB_HSO=n +CONFIG_USB_IPHETH=n +CONFIG_INPUT_FF_MEMLESS=n +CONFIG_INPUT_POLLDEV=n +CONFIG_INPUT_SPARSEKMAP=n +CONFIG_MOUSE_PS2_ELANTECH=n +CONFIG_MOUSE_PS2_SENTELIC=n +CONFIG_MOUSE_APPLETOUCH=n +CONFIG_MOUSE_BCM5974=n +CONFIG_MOUSE_CYAPA=n +CONFIG_MOUSE_VSXXXAA=n +CONFIG_MOUSE_SYNAPTICS_I2C=n +CONFIG_MOUSE_SYNAPTICS_USB=n +CONFIG_INPUT_TABLET=n +CONFIG_INPUT_PCSPKR=n +CONFIG_INPUT_APANEL=n +CONFIG_INPUT_ATLAS_BTNS=n +CONFIG_INPUT_ATI_REMOTE2=n +CONFIG_INPUT_KEYSPAN_REMOTE=n +CONFIG_INPUT_POWERMATE=n +CONFIG_INPUT_YEALINK=n +CONFIG_INPUT_CM109=n +CONFIG_INPUT_UINPUT=n +CONFIG_SERIO_ALTERA_PS2=n +CONFIG_SERIO_ARC_PS2=n +CONFIG_NOZOMI=n +CONFIG_N_GSM=n +CONFIG_SERIAL_JSM=n +CONFIG_SERIAL_ARC=n +CONFIG_HANGCHECK_TIMER=n +CONFIG_TELCLOCK=n +CONFIG_I2C_AMD756=n +CONFIG_I2C_AMD8111=n +CONFIG_I2C_PIIX4=n +CONFIG_I2C_NFORCE2=n +CONFIG_I2C_SIS96X=n +CONFIG_I2C_VIA=n +CONFIG_I2C_VIAPRO=n +CONFIG_I2C_PCA_PLATFORM=n +CONFIG_I2C_SIMTEC=n +CONFIG_I2C_DIOLAN_U2C=n +CONFIG_I2C_PARPORT_LIGHT=n +CONFIG_PPS_CLIENT_LDISC=n +CONFIG_PPS_CLIENT_GPIO=n +CONFIG_PTP_1588_CLOCK_PCH=n +CONFIG_CHARGER_SMB347=n +CONFIG_SENSORS_ABITUGURU=n +CONFIG_SENSORS_ABITUGURU3=n +CONFIG_SENSORS_AD7414=n +CONFIG_SENSORS_AD7418=n +CONFIG_SENSORS_ADM1021=n +CONFIG_SENSORS_ADM1025=n +CONFIG_SENSORS_ADM1026=n +CONFIG_SENSORS_ADM1029=n +CONFIG_SENSORS_ADM1031=n +CONFIG_SENSORS_ADM9240=n +CONFIG_SENSORS_ADT7410=n +CONFIG_SENSORS_ADT7411=n +CONFIG_SENSORS_ADT7462=n +CONFIG_SENSORS_ADT7470=n +CONFIG_SENSORS_ADT7475=n +CONFIG_SENSORS_ASC7621=n +CONFIG_SENSORS_K8TEMP=n +CONFIG_SENSORS_K10TEMP=n +CONFIG_SENSORS_FAM15H_POWER=n +CONFIG_SENSORS_ASB100=n +CONFIG_SENSORS_ATXP1=n +CONFIG_SENSORS_DS620=n +CONFIG_SENSORS_DS1621=n +CONFIG_SENSORS_I5K_AMB=n +CONFIG_SENSORS_F71805F=n +CONFIG_SENSORS_F71882FG=n +CONFIG_SENSORS_F75375S=n +CONFIG_SENSORS_FSCHMD=n +CONFIG_SENSORS_G760A=n +CONFIG_SENSORS_GL518SM=n +CONFIG_SENSORS_GL520SM=n +CONFIG_SENSORS_IBMAEM=n +CONFIG_SENSORS_IBMPEX=n +CONFIG_SENSORS_IT87=n +CONFIG_SENSORS_LINEAGE=n +CONFIG_SENSORS_LM63=n +CONFIG_SENSORS_LM73=n +CONFIG_SENSORS_LM75=n +CONFIG_SENSORS_LM77=n +CONFIG_SENSORS_LM78=n +CONFIG_SENSORS_LM80=n +CONFIG_SENSORS_LM83=n +CONFIG_SENSORS_LM85=n +CONFIG_SENSORS_LM87=n +CONFIG_SENSORS_LM90=n +CONFIG_SENSORS_LM92=n +CONFIG_SENSORS_LM93=n +CONFIG_SENSORS_LTC4151=n +CONFIG_SENSORS_LTC4215=n +CONFIG_SENSORS_LTC4245=n +CONFIG_SENSORS_LTC4261=n +CONFIG_SENSORS_LM95234=n +CONFIG_SENSORS_LM95241=n +CONFIG_SENSORS_LM95245=n +CONFIG_SENSORS_MAX16065=n +CONFIG_SENSORS_MAX1619=n +CONFIG_SENSORS_MAX1668=n +CONFIG_SENSORS_MAX197=n +CONFIG_SENSORS_MAX6639=n +CONFIG_SENSORS_MAX6642=n +CONFIG_SENSORS_MAX6650=n +CONFIG_SENSORS_MAX6697=n +CONFIG_SENSORS_MCP3021=n +CONFIG_SENSORS_NCT6775=n +CONFIG_SENSORS_NTC_THERMISTOR=n +CONFIG_SENSORS_PC87360=n +CONFIG_SENSORS_PC87427=n +CONFIG_SENSORS_PCF8591=n +CONFIG_SENSORS_PMBUS=n +CONFIG_SENSORS_ADM1275=n +CONFIG_SENSORS_LM25066=n +CONFIG_SENSORS_LTC2978=n +CONFIG_SENSORS_MAX16064=n +CONFIG_SENSORS_MAX34440=n +CONFIG_SENSORS_MAX8688=n +CONFIG_SENSORS_UCD9000=n +CONFIG_SENSORS_UCD9200=n +CONFIG_SENSORS_ZL6100=n +CONFIG_SENSORS_SHT21=n +CONFIG_SENSORS_SIS5595=n +CONFIG_SENSORS_DME1737=n +CONFIG_SENSORS_EMC1403=n +CONFIG_SENSORS_EMC6W201=n +CONFIG_SENSORS_SMSC47M1=n +CONFIG_SENSORS_SMSC47M192=n +CONFIG_SENSORS_SMSC47B397=n +CONFIG_SENSORS_SCH56XX_COMMON=n +CONFIG_SENSORS_SCH5627=n +CONFIG_SENSORS_SCH5636=n +CONFIG_SENSORS_ADS1015=n +CONFIG_SENSORS_ADS7828=n +CONFIG_SENSORS_AMC6821=n +CONFIG_SENSORS_INA209=n +CONFIG_SENSORS_INA2XX=n +CONFIG_SENSORS_THMC50=n +CONFIG_SENSORS_TMP102=n +CONFIG_SENSORS_TMP401=n +CONFIG_SENSORS_TMP421=n +CONFIG_SENSORS_VIA_CPUTEMP=n +CONFIG_SENSORS_VIA686A=n +CONFIG_SENSORS_VT1211=n +CONFIG_SENSORS_VT8231=n +CONFIG_SENSORS_W83781D=n +CONFIG_SENSORS_W83791D=n +CONFIG_SENSORS_W83792D=n +CONFIG_SENSORS_W83793=n +CONFIG_SENSORS_W83795=n +CONFIG_SENSORS_W83L785TS=n +CONFIG_SENSORS_W83L786NG=n +CONFIG_SENSORS_W83627HF=n +CONFIG_SENSORS_W83627EHF=n +CONFIG_SENSORS_APPLESMC=n +CONFIG_SENSORS_ATK0110=n +CONFIG_ALIM1535_WDT=n +CONFIG_ALIM7101_WDT=n +CONFIG_F71808E_WDT=n +CONFIG_SP5100_TCO=n +CONFIG_SBC_FITPC2_WATCHDOG=n +CONFIG_IB700_WDT=n +CONFIG_IBMASR=n +CONFIG_IT8712F_WDT=n +CONFIG_IT87_WDT=n +CONFIG_NV_TCO=n +CONFIG_SMSC_SCH311X_WDT=n +CONFIG_VIA_WDT=n +CONFIG_W83627HF_WDT=n +CONFIG_W83697HF_WDT=n +CONFIG_W83697UG_WDT=n +CONFIG_W83877F_WDT=n +CONFIG_W83977F_WDT=n +CONFIG_MACHZ_WDT=n +CONFIG_PCIPCWATCHDOG=n +CONFIG_WDTPCI=n +CONFIG_USBPCWATCHDOG=n +CONFIG_MFD_VIPERBOARD=n +CONFIG_MFD_SM501=n +CONFIG_MFD_VX855=n +CONFIG_AGP_AMD64=n +CONFIG_AGP_SIS=n +CONFIG_AGP_VIA=n +CONFIG_VGA_SWITCHEROO=n +CONFIG_DRM_LOAD_EDID_FIRMWARE=n +CONFIG_DRM_I2C_CH7006=n +CONFIG_DRM_I2C_SIL164=n +CONFIG_DRM_I2C_NXP_TDA998X=n +CONFIG_DRM_RADEON=n +CONFIG_DRM_NOUVEAU=n +CONFIG_DRM_VMWGFX=n +CONFIG_DRM_GMA500=n +CONFIG_DRM_GMA600=n +CONFIG_DRM_GMA3600=n +CONFIG_DRM_UDL=n +CONFIG_DRM_AST=n +CONFIG_DRM_MGAG200=n +CONFIG_DRM_CIRRUS_QEMU=n +CONFIG_DRM_QXL=n +CONFIG_FB_SYS_FILLRECT=n +CONFIG_FB_SYS_COPYAREA=n +CONFIG_FB_SYS_IMAGEBLIT=n +CONFIG_FB_SYS_FOPS=n +CONFIG_FB_BACKLIGHT=n +CONFIG_LCD_PLATFORM=n +CONFIG_BACKLIGHT_APPLE=n +CONFIG_BACKLIGHT_LP855X=n +CONFIG_VGACON_SOFT_SCROLLBACK=n +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=n +CONFIG_LOGO=n +CONFIG_HID_BATTERY_STRENGTH=n +CONFIG_HIDRAW=n +CONFIG_UHID=n +CONFIG_HID_ACRUX=n +CONFIG_HID_APPLEIR=n +CONFIG_HID_AUREAL=n +CONFIG_HID_DRAGONRISE=n +CONFIG_HID_ELECOM=n +CONFIG_HID_HOLTEK=n +CONFIG_HID_KEYTOUCH=n +CONFIG_HID_KYE=n +CONFIG_HID_UCLOGIC=n +CONFIG_HID_WALTOP=n +CONFIG_HID_GYRATION=n +CONFIG_HID_ICADE=n +CONFIG_HID_TWINHAN=n +CONFIG_HID_LCPOWER=n +CONFIG_HID_LENOVO_TPKBD=n +CONFIG_HID_MAGICMOUSE=n +CONFIG_HID_NTRIG=n +CONFIG_HID_ORTEK=n +CONFIG_HID_PANTHERLORD=n +CONFIG_HID_PETALYNX=n +CONFIG_HID_PICOLCD=n +CONFIG_HID_PRIMAX=n +CONFIG_HID_PS3REMOTE=n +CONFIG_HID_ROCCAT=n +CONFIG_HID_SAITEK=n +CONFIG_HID_SAMSUNG=n +CONFIG_HID_SONY=n +CONFIG_HID_SPEEDLINK=n +CONFIG_HID_STEELSERIES=n +CONFIG_HID_SUNPLUS=n +CONFIG_HID_GREENASIA=n +CONFIG_HID_SMARTJOYPLUS=n +CONFIG_HID_TIVO=n +CONFIG_HID_TOPSEED=n +CONFIG_HID_THINGM=n +CONFIG_HID_THRUSTMASTER=n +CONFIG_HID_WACOM=n +CONFIG_HID_WIIMOTE=n +CONFIG_HID_ZEROPLUS=n +CONFIG_HID_ZYDACRON=n +CONFIG_HID_PID=n +CONFIG_USB_HIDDEV=n +CONFIG_USB_ANNOUNCE_NEW_DEVICES=n +CONFIG_USB_MON=n +CONFIG_USB_WUSB_CBAF=n +CONFIG_USB_OHCI_HCD=n +CONFIG_USB_ACM=n +CONFIG_USB_PRINTER=n +CONFIG_USB_WDM=n +CONFIG_USB_TMC=n +CONFIG_USB_STORAGE_REALTEK=n +CONFIG_USB_STORAGE_DATAFAB=n +CONFIG_USB_STORAGE_FREECOM=n +CONFIG_USB_STORAGE_ISD200=n +CONFIG_USB_STORAGE_USBAT=n +CONFIG_USB_STORAGE_SDDR09=n +CONFIG_USB_STORAGE_SDDR55=n +CONFIG_USB_STORAGE_JUMPSHOT=n +CONFIG_USB_STORAGE_ALAUDA=n +CONFIG_USB_STORAGE_ONETOUCH=n +CONFIG_USB_STORAGE_KARMA=n +CONFIG_USB_STORAGE_CYPRESS_ATACB=n +CONFIG_USB_STORAGE_ENE_UB6250=n +CONFIG_USB_MDC800=n +CONFIG_USB_MICROTEK=n +CONFIG_USB_SERIAL_AIRCABLE=n +CONFIG_USB_SERIAL_ARK3116=n +CONFIG_USB_SERIAL_BELKIN=n +CONFIG_USB_SERIAL_WHITEHEAT=n +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=n +CONFIG_USB_SERIAL_CYPRESS_M8=n +CONFIG_USB_SERIAL_EMPEG=n +CONFIG_USB_SERIAL_VISOR=n +CONFIG_USB_SERIAL_IPAQ=n +CONFIG_USB_SERIAL_IR=n +CONFIG_USB_SERIAL_EDGEPORT=n +CONFIG_USB_SERIAL_EDGEPORT_TI=n +CONFIG_USB_SERIAL_GARMIN=n +CONFIG_USB_SERIAL_IPW=n +CONFIG_USB_SERIAL_IUU=n +CONFIG_USB_SERIAL_KEYSPAN_PDA=n +CONFIG_USB_SERIAL_KEYSPAN=n +CONFIG_USB_SERIAL_KLSI=n +CONFIG_USB_SERIAL_KOBIL_SCT=n +CONFIG_USB_SERIAL_MCT_U232=n +CONFIG_USB_SERIAL_MOS7720=n +CONFIG_USB_SERIAL_MOS7840=n +CONFIG_USB_SERIAL_NAVMAN=n +CONFIG_USB_SERIAL_OTI6858=n +CONFIG_USB_SERIAL_QCAUX=n +CONFIG_USB_SERIAL_QUALCOMM=n +CONFIG_USB_SERIAL_SPCP8X5=n +CONFIG_USB_SERIAL_SAFE=n +CONFIG_USB_SERIAL_SIERRAWIRELESS=n +CONFIG_USB_SERIAL_SYMBOL=n +CONFIG_USB_SERIAL_TI=n +CONFIG_USB_SERIAL_CYBERJACK=n +CONFIG_USB_SERIAL_XIRCOM=n +CONFIG_USB_SERIAL_OPTION=n +CONFIG_USB_SERIAL_OMNINET=n +CONFIG_USB_SERIAL_OPTICON=n +CONFIG_USB_SERIAL_XSENS_MT=n +CONFIG_USB_SERIAL_SSU100=n +CONFIG_USB_SERIAL_QT2=n +CONFIG_USB_SERIAL_DEBUG=n +CONFIG_USB_EMI62=n +CONFIG_USB_EMI26=n +CONFIG_USB_ADUTUX=n +CONFIG_USB_SEVSEG=n +CONFIG_USB_LEGOTOWER=n +CONFIG_USB_LCD=n +CONFIG_USB_LED=n +CONFIG_USB_IDMOUSE=n +CONFIG_USB_FTDI_ELAN=n +CONFIG_USB_APPLEDISPLAY=n +CONFIG_USB_SISUSBVGA=n +CONFIG_USB_LD=n +CONFIG_USB_IOWARRIOR=n +CONFIG_USB_ISIGHTFW=n +CONFIG_USB_EZUSB_FX2=n +CONFIG_USB_HSIC_USB3503=n +CONFIG_USB_ATM=n +CONFIG_UWB=n +CONFIG_MMC_RICOH_MMC=n +CONFIG_MMC_TIFM_SD=n +CONFIG_MMC_CB710=n +CONFIG_MMC_VIA_SDMMC=n +CONFIG_MMC_VUB300=n +CONFIG_MMC_USHC=n +CONFIG_MEMSTICK=n +CONFIG_LEDS_LM3530=n +CONFIG_LEDS_LP3944=n +CONFIG_LEDS_LP5521=n +CONFIG_LEDS_LP5523=n +CONFIG_LEDS_LP5562=n +CONFIG_LEDS_CLEVO_MAIL=n +CONFIG_LEDS_INTEL_SS4200=n +CONFIG_LEDS_BLINKM=n +CONFIG_LEDS_TRIGGER_TIMER=n +CONFIG_LEDS_TRIGGER_ONESHOT=n +CONFIG_LEDS_TRIGGER_HEARTBEAT=n +CONFIG_LEDS_TRIGGER_BACKLIGHT=n +CONFIG_LEDS_TRIGGER_TRANSIENT=n +CONFIG_LEDS_TRIGGER_CAMERA=n +CONFIG_INFINIBAND=n +CONFIG_EDAC=n +CONFIG_UIO_CIF=n +CONFIG_UIO_AEC=n +CONFIG_UIO_SERCOS3=n +CONFIG_STAGING=n +CONFIG_ACERHDF=n +CONFIG_ASUS_LAPTOP=n +CONFIG_CHROMEOS_LAPTOP=n +CONFIG_FUJITSU_LAPTOP=n +CONFIG_FUJITSU_TABLET=n +CONFIG_AMILO_RFKILL=n +CONFIG_HP_ACCEL=n +CONFIG_MSI_LAPTOP=n +CONFIG_PANASONIC_LAPTOP=n +CONFIG_COMPAL_LAPTOP=n +CONFIG_SONY_LAPTOP=n +CONFIG_IDEAPAD_LAPTOP=n +CONFIG_THINKPAD_ACPI=n +CONFIG_SENSORS_HDAPS=n +CONFIG_EEEPC_LAPTOP=n +CONFIG_ACPI_WMI=n +CONFIG_TOPSTAR_LAPTOP=n +CONFIG_TOSHIBA_BT_RFKILL=n +CONFIG_ACPI_CMPC=n +CONFIG_SAMSUNG_LAPTOP=n +CONFIG_INTEL_OAKTRAIL=n +CONFIG_SAMSUNG_Q10=n +CONFIG_APPLE_GMUX=n +CONFIG_PVPANIC=n +CONFIG_EDD=n +CONFIG_DELL_RBU=n +CONFIG_DCDBAS=n +CONFIG_JBD_DEBUG=n +CONFIG_FANOTIFY=n +CONFIG_JOLIET=n +CONFIG_UDF_FS=n +CONFIG_CRAMFS=n +CONFIG_SQUASHFS=n +CONFIG_EFIVAR_FS=n +CONFIG_SUNRPC_DEBUG=n +CONFIG_NLS_MAC_ROMAN=n +CONFIG_NLS_MAC_CELTIC=n +CONFIG_NLS_MAC_CENTEURO=n +CONFIG_NLS_MAC_CROATIAN=n +CONFIG_NLS_MAC_CYRILLIC=n +CONFIG_NLS_MAC_GAELIC=n +CONFIG_NLS_MAC_GREEK=n +CONFIG_NLS_MAC_ICELAND=n +CONFIG_NLS_MAC_INUIT=n +CONFIG_NLS_MAC_ROMANIAN=n +CONFIG_NLS_MAC_TURKISH=n +CONFIG_SCHED_TRACER=n +CONFIG_TRACER_SNAPSHOT=n +CONFIG_UPROBE_EVENT=n +CONFIG_PROBE_EVENTS=n +CONFIG_FUNCTION_PROFILER=n +CONFIG_RING_BUFFER_BENCHMARK=n +CONFIG_ATOMIC64_SELFTEST=n +CONFIG_ASYNC_RAID6_TEST=n +CONFIG_KGDB=n +CONFIG_TEST_KSTRTOX=n +CONFIG_STRICT_DEVMEM=n +CONFIG_DEBUG_SET_MODULE_RONX=n +CONFIG_DEBUG_NX_TEST=n +CONFIG_CRYPTO_BLOWFISH_X86_64=n +CONFIG_CRYPTO_CAMELLIA_X86_64=n +CONFIG_CRYPTO_CAMELLIA_AESNI_AVX_X86_64=n +CONFIG_CRYPTO_CAMELLIA_AESNI_AVX2_X86_64=n +CONFIG_CRYPTO_CAST5_AVX_X86_64=n +CONFIG_CRYPTO_CAST6_AVX_X86_64=n +CONFIG_CRYPTO_SALSA20=n +CONFIG_CRYPTO_SALSA20_X86_64=n +CONFIG_CRYPTO_SERPENT_SSE2_X86_64=n +CONFIG_CRYPTO_SERPENT_AVX_X86_64=n +CONFIG_CRYPTO_SERPENT_AVX2_X86_64=n +CONFIG_CRYPTO_TWOFISH_X86_64=n +CONFIG_CRYPTO_TWOFISH_X86_64_3WAY=n +CONFIG_CRYPTO_TWOFISH_AVX_X86_64=n +CONFIG_CRYPTO_USER_API_HASH=n +CONFIG_CRYPTO_USER_API_SKCIPHER=n +CONFIG_CRYPTO_DEV_PADLOCK=n + +#Disable unused options from rt kernel +CONFIG_WIRELESS_EXT=n +CONFIG_DLCI=n +CONFIG_IPPP_FILTER=n +CONFIG_HISAX_EURO=n +CONFIG_DE_AOC=n +CONFIG_HISAX_NO_SENDCOMPLETE=n +CONFIG_HISAX_NO_LLC=n +CONFIG_HISAX_NO_KEYPAD=n +CONFIG_HISAX_1TR6=n +CONFIG_HISAX_NI1=n +CONFIG_HISAX_16_3=n +CONFIG_HISAX_TELESPCI=n +CONFIG_HISAX_S0BOX=n +CONFIG_HISAX_FRITZPCI=n +CONFIG_HISAX_AVM_A1_PCMCIA=n +CONFIG_HISAX_ELSA=n +CONFIG_HISAX_DIEHLDIVA=n +CONFIG_HISAX_SEDLBAUER=n +CONFIG_HISAX_NETJET=n +CONFIG_HISAX_NETJET_U=n +CONFIG_HISAX_NICCY=n +CONFIG_HISAX_BKM_A4T=n +CONFIG_HISAX_SCT_QUADRO=n +CONFIG_HISAX_GAZEL=n +CONFIG_HISAX_HFC_PCI=n +CONFIG_HISAX_W6692=n +CONFIG_HISAX_HFC_SX=n +CONFIG_HISAX_ENTERNOW_PCI=n +CONFIG_HISAX_ST5481=n +CONFIG_HISAX_HFC4S8S=n +CONFIG_HISAX_FRITZ_PCIPNP=n +CONFIG_CAPI_AVM=n +CONFIG_GIGASET_CAPI=n +CONFIG_GIGASET_BASE=n +CONFIG_GIGASET_M105=n +CONFIG_GIGASET_M101=n +CONFIG_HYSDN=n +CONFIG_HYSDN_CAPI=n +CONFIG_MISDN=n +CONFIG_MISDN_DSP=n +CONFIG_MISDN_L1OIP=n +CONFIG_MISDN_HFCPCI=n +CONFIG_MISDN_HFCMULTI=n +CONFIG_MISDN_HFCUSB=n +CONFIG_MISDN_AVMFRITZ=n +CONFIG_MISDN_SPEEDFAX=n +CONFIG_MISDN_INFINEON=n +CONFIG_MISDN_W6692=n +CONFIG_MISDN_NETJET=n +CONFIG_MISDN_IPAC=n +CONFIG_MISDN_ISAR=n +CONFIG_MEDIA_CAMERA_SUPPORT=n +CONFIG_MEDIA_ANALOG_TV_SUPPORT=n +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=n +CONFIG_MEDIA_RADIO_SUPPORT=n +CONFIG_MEDIA_RC_SUPPORT=n +CONFIG_VIDEO_DEV=n +CONFIG_VIDEO_V4L2=n +CONFIG_VIDEO_TUNER=n +CONFIG_VIDEOBUF_GEN=n +CONFIG_VIDEOBUF_DMA_SG=n +CONFIG_VIDEOBUF_VMALLOC=n +CONFIG_VIDEOBUF_DVB=n +CONFIG_VIDEOBUF2_CORE=n +CONFIG_VIDEOBUF2_MEMOPS=n +CONFIG_VIDEOBUF2_VMALLOC=n +CONFIG_DVB=n +CONFIG_DVB_CORE=n +CONFIG_DVB_NET=n +CONFIG_TTPCI_EEPROM=n +CONFIG_DVB_DYNAMIC_MINORS=n +CONFIG_RC_CORE=n +CONFIG_RC_MAP=n +CONFIG_RC_DECODERS=n +CONFIG_LIRC=n +CONFIG_IR_LIRC_CODEC=n +CONFIG_IR_NEC_DECODER=n +CONFIG_IR_RC5_DECODER=n +CONFIG_IR_RC6_DECODER=n +CONFIG_IR_JVC_DECODER=n +CONFIG_IR_SONY_DECODER=n +CONFIG_IR_RC5_SZ_DECODER=n +CONFIG_IR_SANYO_DECODER=n +CONFIG_IR_MCE_KBD_DECODER=n +CONFIG_RC_DEVICES=n +CONFIG_RC_ATI_REMOTE=n +CONFIG_IR_ENE=n +CONFIG_IR_IMON=n +CONFIG_IR_MCEUSB=n +CONFIG_IR_ITE_CIR=n +CONFIG_IR_FINTEK=n +CONFIG_IR_NUVOTON=n +CONFIG_IR_REDRAT3=n +CONFIG_IR_STREAMZAP=n +CONFIG_IR_WINBOND_CIR=n +CONFIG_IR_IGUANA=n +CONFIG_IR_TTUSBIR=n +CONFIG_VIDEO_EM28XX=n +CONFIG_VIDEO_EM28XX_ALSA=n +CONFIG_VIDEO_EM28XX_DVB=n +CONFIG_VIDEO_EM28XX_RC=n +CONFIG_MEDIA_PCI_SUPPORT=n +CONFIG_VIDEO_IVTV=n +CONFIG_VIDEO_FB_IVTV=n +CONFIG_VIDEO_CX18=n +CONFIG_VIDEO_CX18_ALSA=n +CONFIG_VIDEO_CX23885=n +CONFIG_MEDIA_ALTERA_CI=n +CONFIG_VIDEO_CX88=n +CONFIG_VIDEO_CX88_ALSA=n +CONFIG_VIDEO_CX88_BLACKBIRD=n +CONFIG_VIDEO_CX88_DVB=n +CONFIG_VIDEO_CX88_VP3054=n +CONFIG_VIDEO_CX88_MPEG=n +CONFIG_VIDEO_BT848=n +CONFIG_VIDEO_SAA7134=n +CONFIG_VIDEO_SAA7134_ALSA=n +CONFIG_VIDEO_SAA7134_RC=n +CONFIG_VIDEO_SAA7134_DVB=n +CONFIG_VIDEO_SAA7164=n +CONFIG_MANTIS_CORE=n +CONFIG_SMS_SDIO_DRV=n +CONFIG_MEDIA_COMMON_OPTIONS=n +CONFIG_VIDEO_CX2341X=n +CONFIG_VIDEO_BTCX=n +CONFIG_VIDEO_TVEEPROM=n +CONFIG_CYPRESS_FIRMWARE=n +CONFIG_VIDEO_SAA7146=n +CONFIG_VIDEO_SAA7146_VV=n +CONFIG_SMS_SIANO_MDTV=n +CONFIG_SMS_SIANO_RC=n +CONFIG_MEDIA_SUBDRV_AUTOSELECT=n +CONFIG_MEDIA_ATTACH=n +CONFIG_VIDEO_IR_I2C=n +CONFIG_VIDEO_TVAUDIO=n +CONFIG_VIDEO_TDA7432=n +CONFIG_VIDEO_MSP3400=n +CONFIG_VIDEO_CS5345=n +CONFIG_VIDEO_CS53L32A=n +CONFIG_VIDEO_WM8775=n +CONFIG_VIDEO_WM8739=n +CONFIG_VIDEO_VP27SMPX=n +CONFIG_VIDEO_SAA6588=n +CONFIG_VIDEO_SAA711X=n +CONFIG_VIDEO_TVP5150=n +CONFIG_VIDEO_SAA717X=n +CONFIG_VIDEO_CX25840=n +CONFIG_VIDEO_SAA7127=n +CONFIG_VIDEO_MT9V011=n +CONFIG_VIDEO_UPD64031A=n +CONFIG_VIDEO_UPD64083=n +CONFIG_VIDEO_M52790=n +CONFIG_MEDIA_TUNER=n +CONFIG_SND=n +CONFIG_IR_GPIO_CIR=n +CONFIG_BLK_DEV_NBD=m +CONFIG_AIC94XX_DEBUG=n + +# Disable transparent huge pages +CONFIG_TRANSPARENT_HUGEPAGE=n +CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=n + +# Make performance default governor +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=n +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y + +CONFIG_GPIO_BT8XX=n + +CONFIG_KERNFS=y +CONFIG_INTEL_RDT_A=y diff --git a/kernel-rt/centos/patches/memblock-introduce-memblock_alloc_range.patch b/kernel-rt/centos/patches/memblock-introduce-memblock_alloc_range.patch new file mode 100644 index 00000000..8dd2ded6 --- /dev/null +++ b/kernel-rt/centos/patches/memblock-introduce-memblock_alloc_range.patch @@ -0,0 +1,96 @@ +From ea38fcef34645b2220eadad6c862d5884c48026d Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Akinobu Mita +Date: Tue, 31 May 2016 16:07:55 -0400 +Subject: [PATCH 10/32] memblock: introduce memblock_alloc_range() + +Commit 2bfc2862c4fe38379a2fb2cfba33fad32ccb4ff4 upstream +Backported-by: Nam Ninh + +This introduces memblock_alloc_range() which allocates memblock from the +specified range of physical address. I would like to use this function +to specify the location of CMA. + +Signed-off-by: Akinobu Mita +Cc: Marek Szyprowski +Cc: Konrad Rzeszutek Wilk +Cc: David Woodhouse +Cc: Don Dutile +Cc: Thomas Gleixner +Cc: Ingo Molnar +Cc: "H. Peter Anvin" +Cc: Andi Kleen +Cc: Yinghai Lu +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +Signed-off-by: Jim Somerville +--- + include/linux/memblock.h | 2 ++ + mm/memblock.c | 22 ++++++++++++++++++---- + 2 files changed, 20 insertions(+), 4 deletions(-) + +diff --git a/include/linux/memblock.h b/include/linux/memblock.h +index 5a439c9..d6bcbef 100644 +--- a/include/linux/memblock.h ++++ b/include/linux/memblock.h +@@ -304,6 +304,8 @@ static inline bool memblock_bottom_up(void) { return false; } + #define MEMBLOCK_ALLOC_ANYWHERE (~(phys_addr_t)0) + #define MEMBLOCK_ALLOC_ACCESSIBLE 0 + ++phys_addr_t __init memblock_alloc_range(phys_addr_t size, phys_addr_t align, ++ phys_addr_t start, phys_addr_t end); + phys_addr_t memblock_alloc_base(phys_addr_t size, phys_addr_t align, + phys_addr_t max_addr); + phys_addr_t __memblock_alloc_base(phys_addr_t size, phys_addr_t align, +diff --git a/mm/memblock.c b/mm/memblock.c +index fbc8071..ff910a4 100644 +--- a/mm/memblock.c ++++ b/mm/memblock.c +@@ -1120,9 +1120,9 @@ int __init_memblock memblock_set_node(phys_addr_t base, phys_addr_t size, + } + #endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */ + +-static phys_addr_t __init memblock_alloc_base_nid(phys_addr_t size, +- phys_addr_t align, phys_addr_t max_addr, +- int nid, ulong flags) ++static phys_addr_t __init memblock_alloc_range_nid(phys_addr_t size, ++ phys_addr_t align, phys_addr_t start, ++ phys_addr_t end, int nid, ulong flags) + { + phys_addr_t found; + +@@ -1132,7 +1132,7 @@ static phys_addr_t __init memblock_alloc_base_nid(phys_addr_t size, + /* align @size to avoid excessive fragmentation on reserved array */ + size = round_up(size, align); + +- found = memblock_find_in_range_node(size, align, 0, max_addr, nid, ++ found = memblock_find_in_range_node(size, align, start, end, nid, + flags); + if (found && !memblock_reserve(found, size)) + return found; +@@ -1140,6 +1140,20 @@ static phys_addr_t __init memblock_alloc_base_nid(phys_addr_t size, + return 0; + } + ++phys_addr_t __init memblock_alloc_range(phys_addr_t size, phys_addr_t align, ++ phys_addr_t start, phys_addr_t end) ++{ ++ ulong flags = choose_memblock_flags(); ++ return memblock_alloc_range_nid(size, align, start, end, NUMA_NO_NODE, flags); ++} ++ ++static phys_addr_t __init memblock_alloc_base_nid(phys_addr_t size, ++ phys_addr_t align, phys_addr_t max_addr, ++ int nid, ulong flags) ++{ ++ return memblock_alloc_range_nid(size, align, 0, max_addr, nid, flags); ++} ++ + phys_addr_t __init memblock_alloc_nid(phys_addr_t size, phys_addr_t align, int nid) + { + ulong flags = choose_memblock_flags(); +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/rcu-Don-t-wake-rcuc-X-kthreads-on-NOCB-CPUs.patch b/kernel-rt/centos/patches/rcu-Don-t-wake-rcuc-X-kthreads-on-NOCB-CPUs.patch new file mode 100644 index 00000000..9991f480 --- /dev/null +++ b/kernel-rt/centos/patches/rcu-Don-t-wake-rcuc-X-kthreads-on-NOCB-CPUs.patch @@ -0,0 +1,47 @@ +From b48c1062db1a4529f9ebecbffb5a80542da9f4f5 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: "Paul E. McKenney" +Date: Thu, 15 Dec 2016 15:37:47 -0800 +Subject: [PATCH 18/32] rcu: Don't wake rcuc/X kthreads on NOCB CPUs + +[ upstream 630c7ed9ca0608912fa7c8591d05dfc8742dc9e6 in tip repo ] + +Chris Friesen notice that rcuc/X kthreads were consuming CPU even on +NOCB CPUs. This makes no sense because the only purpose or these +kthreads is to invoke normal (non-offloaded) callbacks, of which there +will never be any on NOCB CPUs. This problem was due to a bug in +cpu_has_callbacks_ready_to_invoke(), which should have been checking +->nxttail[RCU_NEXT_TAIL] for NULL, but which was instead (incorrectly) +checking ->nxttail[RCU_DONE_TAIL]. Because ->nxttail[RCU_DONE_TAIL] is +never NULL, the only effect is to cause the rcuc/X kthread to execute +when it should not do so. + +This commit therefore checks ->nxttail[RCU_NEXT_TAIL], which is NULL +for NOCB CPUs. + +Reported-by: Chris Friesen +Signed-off-by: Paul E. McKenney +Reviewed-by: Josh Triplett +Signed-off-by: Jim Somerville +--- + kernel/rcutree.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/kernel/rcutree.c b/kernel/rcutree.c +index 9648918..fb7b2a8 100644 +--- a/kernel/rcutree.c ++++ b/kernel/rcutree.c +@@ -319,7 +319,7 @@ static int + cpu_has_callbacks_ready_to_invoke(struct rcu_data *rdp) + { + return &rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL] && +- rdp->nxttail[RCU_DONE_TAIL] != NULL; ++ rdp->nxttail[RCU_NEXT_TAIL] != NULL; + } + + /* +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/restrict-iSCSI-kthreads-to-CPUs-in-cpu_kthread_mask.patch b/kernel-rt/centos/patches/restrict-iSCSI-kthreads-to-CPUs-in-cpu_kthread_mask.patch new file mode 100644 index 00000000..a98e0b37 --- /dev/null +++ b/kernel-rt/centos/patches/restrict-iSCSI-kthreads-to-CPUs-in-cpu_kthread_mask.patch @@ -0,0 +1,47 @@ +From d692cbb183b38e030c7c1221b67c9389bdeaf7f3 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Alex Kozyrev +Date: Fri, 16 Mar 2018 15:50:57 -0400 +Subject: [PATCH 32/32] restrict iSCSI kthreads to CPUs in cpu_kthread_mask + +Do not allow them to run on other CPUs to prevent interference with VMs. + +Signed-off-by: Alex Kozyrev +Signed-off-by: Jim Somerville +--- + drivers/target/iscsi/iscsi_target.c | 4 ++-- + include/linux/cpumask.h | 1 + + 2 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c +index d8f587e..17b08b7 100644 +--- a/drivers/target/iscsi/iscsi_target.c ++++ b/drivers/target/iscsi/iscsi_target.c +@@ -3594,8 +3594,8 @@ void iscsit_thread_get_cpumask(struct iscsi_conn *conn) + * iSCSI connection's RX/TX threads will be scheduled to + * execute upon. + */ +- ord = conn->bitmap_id % cpumask_weight(cpu_online_mask); +- for_each_online_cpu(cpu) { ++ ord = conn->bitmap_id % cpumask_weight(cpu_kthread_mask); ++ for_each_kthread_cpu(cpu) { + if (ord-- == 0) { + cpumask_set_cpu(cpu, conn->conn_cpumask); + return; +diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h +index 46ae77a..42b6c63 100644 +--- a/include/linux/cpumask.h ++++ b/include/linux/cpumask.h +@@ -754,6 +754,7 @@ extern const DECLARE_BITMAP(cpu_all_bits, NR_CPUS); + #define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask) + #define for_each_online_cpu(cpu) for_each_cpu((cpu), cpu_online_mask) + #define for_each_present_cpu(cpu) for_each_cpu((cpu), cpu_present_mask) ++#define for_each_kthread_cpu(cpu) for_each_cpu((cpu), cpu_kthread_mask) + + /* Wrappers for arch boot code to manipulate normally-constant masks */ + void set_cpu_possible(unsigned int cpu, bool possible); +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/timer-Minimize-nohz-off-overhead.patch b/kernel-rt/centos/patches/timer-Minimize-nohz-off-overhead.patch new file mode 100644 index 00000000..6f83a72e --- /dev/null +++ b/kernel-rt/centos/patches/timer-Minimize-nohz-off-overhead.patch @@ -0,0 +1,168 @@ +From ecfd98039a9be52230746b209010c77adb575629 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Thomas Gleixner +Date: Tue, 26 May 2015 22:50:35 +0000 +Subject: [PATCH 28/32] timer: Minimize nohz off overhead + +If nohz is disabled on the kernel command line the [hr]timer code +still calls wake_up_nohz_cpu() and tick_nohz_full_cpu(), a pretty +pointless exercise. Cache nohz_active in [hr]timer per cpu bases and +avoid the overhead. + +Before: + 48.10% hog [.] main + 15.25% [kernel] [k] _raw_spin_lock_irqsave + 9.76% [kernel] [k] _raw_spin_unlock_irqrestore + 6.50% [kernel] [k] mod_timer + 6.44% [kernel] [k] lock_timer_base.isra.38 + 3.87% [kernel] [k] detach_if_pending + 3.80% [kernel] [k] del_timer + 2.67% [kernel] [k] internal_add_timer + 1.33% [kernel] [k] __internal_add_timer + 0.73% [kernel] [k] timerfn + 0.54% [kernel] [k] wake_up_nohz_cpu + +After: + 48.73% hog [.] main + 15.36% [kernel] [k] _raw_spin_lock_irqsave + 9.77% [kernel] [k] _raw_spin_unlock_irqrestore + 6.61% [kernel] [k] lock_timer_base.isra.38 + 6.42% [kernel] [k] mod_timer + 3.90% [kernel] [k] detach_if_pending + 3.76% [kernel] [k] del_timer + 2.41% [kernel] [k] internal_add_timer + 1.39% [kernel] [k] __internal_add_timer + 0.76% [kernel] [k] timerfn + +We probably should have a cached value for nohz full in the per cpu +bases as well to avoid the cpumask check. The base cache line is hot +already, the cpumask not necessarily. + +Signed-off-by: Thomas Gleixner +Cc: Peter Zijlstra +Cc: Paul McKenney +Cc: Frederic Weisbecker +Cc: Eric Dumazet +Cc: Viresh Kumar +Cc: John Stultz +Cc: Joonwoo Park +Cc: Wenbo Wang +Link: http://lkml.kernel.org/r/20150526224512.207378134@linutronix.de +Signed-off-by: Thomas Gleixner +Signed-off-by: Alex Kozyrev +Signed-off-by: Jim Somerville +--- + include/linux/hrtimer.h | 2 ++ + kernel/time/tick-internal.h | 4 ++-- + kernel/time/tick-sched.c | 2 +- + kernel/timer.c | 14 +++++++++++--- + 4 files changed, 16 insertions(+), 6 deletions(-) + +diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h +index 00d4c9b..6183bf8 100644 +--- a/include/linux/hrtimer.h ++++ b/include/linux/hrtimer.h +@@ -176,6 +176,7 @@ enum hrtimer_base_type { + * Note that in RHEL7 clock_was_set is upstream's + * clock_was_set_seq (KABI). + * @migration_enabled: The migration of hrtimers to other cpus is enabled ++ * @nohz_active: The nohz functionality is enabled + * @expires_next: absolute time of the next event which was scheduled + * via clock_set_next_event() + * @hres_active: State of high resolution mode +@@ -191,6 +192,7 @@ struct hrtimer_cpu_base { + unsigned int active_bases; + unsigned int clock_was_set; /* clock_was_set_seq */ + bool migration_enabled; ++ bool nohz_active; + #ifdef CONFIG_HIGH_RES_TIMERS + ktime_t expires_next; + int hres_active; +diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h +index 3ebdda4..13468bd 100644 +--- a/kernel/time/tick-internal.h ++++ b/kernel/time/tick-internal.h +@@ -173,9 +173,9 @@ extern unsigned long tick_nohz_active; + #endif + + #if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON) +-extern void timers_update_migration(void); ++extern void timers_update_migration(bool update_nohz); + #else +-static inline void timers_update_migration(void) { } ++static inline void timers_update_migration(bool update_nohz) { } + #endif + + DECLARE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases); +diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c +index 6c92920..3ccc18c 100644 +--- a/kernel/time/tick-sched.c ++++ b/kernel/time/tick-sched.c +@@ -980,7 +980,7 @@ static inline void tick_nohz_activate(struct tick_sched *ts, int mode) + ts->nohz_mode = mode; + /* One update is enough */ + if (!test_and_set_bit(0, &tick_nohz_active)) +- timers_update_migration(); ++ timers_update_migration(true); + } + + /** +diff --git a/kernel/timer.c b/kernel/timer.c +index 4fcb630..08c96e1 100644 +--- a/kernel/timer.c ++++ b/kernel/timer.c +@@ -88,6 +88,7 @@ struct tvec_base { + unsigned long active_timers; + int cpu; + bool migration_enabled; ++ bool nohz_active; + struct tvec_root tv1; + struct tvec tv2; + struct tvec tv3; +@@ -103,7 +104,7 @@ static DEFINE_PER_CPU(struct tvec_base *, tvec_bases) = &boot_tvec_bases; + #if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON) + unsigned int sysctl_timer_migration = 1; + +-void timers_update_migration(void) ++void timers_update_migration(bool update_nohz) + { + bool on = sysctl_timer_migration && tick_nohz_active; + unsigned int cpu; +@@ -119,6 +120,10 @@ void timers_update_migration(void) + tvec_base->migration_enabled = on; + hrtimer_base = &per_cpu(hrtimer_bases, cpu); + hrtimer_base->migration_enabled = on; ++ if (!update_nohz) ++ continue; ++ tvec_base->nohz_active = true; ++ hrtimer_base->nohz_active = true; + } + } + +@@ -132,7 +137,7 @@ int timer_migration_handler(struct ctl_table *table, int write, + mutex_lock(&mutex); + ret = proc_dointvec(table, write, buffer, lenp, ppos); + if (!ret && write) +- timers_update_migration(); ++ timers_update_migration(false); + mutex_unlock(&mutex); + return ret; + } +@@ -482,8 +487,11 @@ static void internal_add_timer(struct tvec_base *base, struct timer_list *timer) + * require special care against races with idle_cpu(), lets deal + * with that later. + */ +- if (!tbase_get_deferrable(base) || tick_nohz_full_cpu(base->cpu)) ++ if (base->nohz_active) { ++ if (!tbase_get_deferrable(base) || ++ tick_nohz_full_cpu(base->cpu)) + wake_up_nohz_cpu(base->cpu); ++ } + } + + #ifdef CONFIG_TIMER_STATS +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/timer-Reduce-timer-migration-overhead-if-disabled.patch b/kernel-rt/centos/patches/timer-Reduce-timer-migration-overhead-if-disabled.patch new file mode 100644 index 00000000..92674a06 --- /dev/null +++ b/kernel-rt/centos/patches/timer-Reduce-timer-migration-overhead-if-disabled.patch @@ -0,0 +1,533 @@ +From 100eaa897b32405365ce13248c20fcbfd6e4a85d Mon Sep 17 00:00:00 2001 +Message-Id: <100eaa897b32405365ce13248c20fcbfd6e4a85d.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Thomas Gleixner +Date: Tue, 26 May 2015 22:50:33 +0000 +Subject: [PATCH 27/32] timer: Reduce timer migration overhead if disabled + +Eric reported that the timer_migration sysctl is not really nice +performance wise as it needs to check at every timer insertion whether +the feature is enabled or not. Further the check does not live in the +timer code, so we have an extra function call which checks an extra +cache line to figure out that it is disabled. + +We can do better and store that information in the per cpu (hr)timer +bases. I pondered to use a static key, but that's a nightmare to +update from the nohz code and the timer base cache line is hot anyway +when we select a timer base. + +The old logic enabled the timer migration unconditionally if +CONFIG_NO_HZ was set even if nohz was disabled on the kernel command +line. + +With this modification, we start off with migration disabled. The user +visible sysctl is still set to enabled. If the kernel switches to NOHZ +migration is enabled, if the user did not disable it via the sysctl +prior to the switch. If nohz=off is on the kernel command line, +migration stays disabled no matter what. + +Before: + 47.76% hog [.] main + 14.84% [kernel] [k] _raw_spin_lock_irqsave + 9.55% [kernel] [k] _raw_spin_unlock_irqrestore + 6.71% [kernel] [k] mod_timer + 6.24% [kernel] [k] lock_timer_base.isra.38 + 3.76% [kernel] [k] detach_if_pending + 3.71% [kernel] [k] del_timer + 2.50% [kernel] [k] internal_add_timer + 1.51% [kernel] [k] get_nohz_timer_target + 1.28% [kernel] [k] __internal_add_timer + 0.78% [kernel] [k] timerfn + 0.48% [kernel] [k] wake_up_nohz_cpu + +After: + 48.10% hog [.] main + 15.25% [kernel] [k] _raw_spin_lock_irqsave + 9.76% [kernel] [k] _raw_spin_unlock_irqrestore + 6.50% [kernel] [k] mod_timer + 6.44% [kernel] [k] lock_timer_base.isra.38 + 3.87% [kernel] [k] detach_if_pending + 3.80% [kernel] [k] del_timer + 2.67% [kernel] [k] internal_add_timer + 1.33% [kernel] [k] __internal_add_timer + 0.73% [kernel] [k] timerfn + 0.54% [kernel] [k] wake_up_nohz_cpu + +Reported-by: Eric Dumazet +Signed-off-by: Thomas Gleixner +Cc: Peter Zijlstra +Cc: Paul McKenney +Cc: Frederic Weisbecker +Cc: Viresh Kumar +Cc: John Stultz +Cc: Joonwoo Park +Cc: Wenbo Wang +Link: http://lkml.kernel.org/r/20150526224512.127050787@linutronix.de +Signed-off-by: Thomas Gleixner +Signed-off-by: Alex Kozyrev +Signed-off-by: Jim Somerville +--- + include/linux/hrtimer.h | 2 ++ + include/linux/sched/sysctl.h | 12 -------- + include/linux/timer.h | 8 +++++ + kernel/hrtimer.c | 48 +++++++++++++++++------------- + kernel/rcutree_plugin.h | 2 -- + kernel/sched/core.c | 2 -- + kernel/sysctl.c | 18 ++++++------ + kernel/time/tick-internal.h | 14 +++++++++ + kernel/time/tick-sched.c | 25 +++++++++------- + kernel/time/timer_list.c | 3 +- + kernel/timer.c | 70 ++++++++++++++++++++++++++++++++++++-------- + 11 files changed, 133 insertions(+), 71 deletions(-) + +diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h +index cd04b77..00d4c9b 100644 +--- a/include/linux/hrtimer.h ++++ b/include/linux/hrtimer.h +@@ -175,6 +175,7 @@ enum hrtimer_base_type { + * @clock_was_set: Sequence counter of clock was set events + * Note that in RHEL7 clock_was_set is upstream's + * clock_was_set_seq (KABI). ++ * @migration_enabled: The migration of hrtimers to other cpus is enabled + * @expires_next: absolute time of the next event which was scheduled + * via clock_set_next_event() + * @hres_active: State of high resolution mode +@@ -189,6 +190,7 @@ struct hrtimer_cpu_base { + raw_spinlock_t lock; + unsigned int active_bases; + unsigned int clock_was_set; /* clock_was_set_seq */ ++ bool migration_enabled; + #ifdef CONFIG_HIGH_RES_TIMERS + ktime_t expires_next; + int hres_active; +diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h +index 4895484..02ab10e 100644 +--- a/include/linux/sched/sysctl.h ++++ b/include/linux/sched/sysctl.h +@@ -54,24 +54,12 @@ extern unsigned int sysctl_numa_balancing_settle_count; + extern unsigned int sysctl_sched_migration_cost; + extern unsigned int sysctl_sched_nr_migrate; + extern unsigned int sysctl_sched_time_avg; +-extern unsigned int sysctl_timer_migration; + extern unsigned int sysctl_sched_shares_window; + + int sched_proc_update_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *length, + loff_t *ppos); + #endif +-#ifdef CONFIG_SCHED_DEBUG +-static inline unsigned int get_sysctl_timer_migration(void) +-{ +- return sysctl_timer_migration; +-} +-#else +-static inline unsigned int get_sysctl_timer_migration(void) +-{ +- return 1; +-} +-#endif + + /* + * control realtime throttling: +diff --git a/include/linux/timer.h b/include/linux/timer.h +index c37d9b9..8eb4558 100644 +--- a/include/linux/timer.h ++++ b/include/linux/timer.h +@@ -249,6 +249,14 @@ extern void run_local_timers(void); + struct hrtimer; + extern enum hrtimer_restart it_real_fn(struct hrtimer *); + ++#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON) ++#include ++extern unsigned int sysctl_timer_migration; ++int timer_migration_handler(struct ctl_table *table, int write, ++ void __user *buffer, size_t *lenp, ++ loff_t *ppos); ++#endif ++ + unsigned long __round_jiffies(unsigned long j, int cpu); + unsigned long __round_jiffies_relative(unsigned long j, int cpu); + unsigned long round_jiffies(unsigned long j); +diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c +index 40655c8..55444ab 100644 +--- a/kernel/hrtimer.c ++++ b/kernel/hrtimer.c +@@ -168,19 +168,6 @@ struct hrtimer_clock_base *lock_hrtimer_base(const struct hrtimer *timer, + } + } + +- +-/* +- * Get the preferred target CPU for NOHZ +- */ +-static int hrtimer_get_target(int this_cpu, int pinned) +-{ +-#ifdef CONFIG_NO_HZ_COMMON +- if (!pinned && get_sysctl_timer_migration()) +- return get_nohz_timer_target(); +-#endif +- return this_cpu; +-} +- + /* + * With HIGHRES=y we do not migrate the timer when it is expiring + * before the next event on the target cpu because we cannot reprogram +@@ -204,6 +191,24 @@ hrtimer_check_target(struct hrtimer *timer, struct hrtimer_clock_base *new_base) + #endif + } + ++#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON) ++static inline ++struct hrtimer_cpu_base *get_target_base(struct hrtimer_cpu_base *base, ++ int pinned) ++{ ++ if (pinned || !base->migration_enabled) ++ return this_cpu_ptr(&hrtimer_bases); ++ return &per_cpu(hrtimer_bases, get_nohz_timer_target()); ++} ++#else ++static inline ++struct hrtimer_cpu_base *get_target_base(struct hrtimer_cpu_base *base, ++ int pinned) ++{ ++ return this_cpu_ptr(&hrtimer_bases); ++} ++#endif ++ + /* + * Switch the timer base to the current CPU when possible. + */ +@@ -211,14 +216,13 @@ static inline struct hrtimer_clock_base * + switch_hrtimer_base(struct hrtimer *timer, struct hrtimer_clock_base *base, + int pinned) + { ++ struct hrtimer_cpu_base *new_cpu_base, *this_base; + struct hrtimer_clock_base *new_base; +- struct hrtimer_cpu_base *new_cpu_base; +- int this_cpu = smp_processor_id(); +- int cpu = hrtimer_get_target(this_cpu, pinned); + int basenum = base->index; + ++ this_base = this_cpu_ptr(&hrtimer_bases); ++ new_cpu_base = get_target_base(this_base, pinned); + again: +- new_cpu_base = &per_cpu(hrtimer_bases, cpu); + new_base = &new_cpu_base->clock_base[basenum]; + + if (base != new_base) { +@@ -239,17 +243,19 @@ again: + raw_spin_unlock(&base->cpu_base->lock); + raw_spin_lock(&new_base->cpu_base->lock); + +- if (cpu != this_cpu && hrtimer_check_target(timer, new_base)) { +- cpu = this_cpu; ++ if (new_cpu_base != this_base && ++ hrtimer_check_target(timer, new_base)) { + raw_spin_unlock(&new_base->cpu_base->lock); + raw_spin_lock(&base->cpu_base->lock); ++ new_cpu_base = this_base; + timer->base = base; + goto again; + } + timer->base = new_base; + } else { +- if (cpu != this_cpu && hrtimer_check_target(timer, new_base)) { +- cpu = this_cpu; ++ if (new_cpu_base != this_base && ++ hrtimer_check_target(timer, new_base)) { ++ new_cpu_base = this_base; + goto again; + } + } +diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h +index 8f410cc..7232ecb 100644 +--- a/kernel/rcutree_plugin.h ++++ b/kernel/rcutree_plugin.h +@@ -1502,8 +1502,6 @@ module_param(rcu_idle_gp_delay, int, 0644); + static int rcu_idle_lazy_gp_delay = RCU_IDLE_LAZY_GP_DELAY; + module_param(rcu_idle_lazy_gp_delay, int, 0644); + +-extern int tick_nohz_active; +- + /* + * Try to advance callbacks for all flavors of RCU on the current CPU. + * Afterwards, if there are any callbacks ready for immediate invocation, +diff --git a/kernel/sched/core.c b/kernel/sched/core.c +index b2333b7..d82e745 100644 +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -8824,8 +8824,6 @@ void __init sched_init_smp(void) + } + #endif /* CONFIG_SMP */ + +-const_debug unsigned int sysctl_timer_migration = 1; +- + int in_sched_functions(unsigned long addr) + { + return in_lock_functions(addr) || +diff --git a/kernel/sysctl.c b/kernel/sysctl.c +index 8516049..b435155 100644 +--- a/kernel/sysctl.c ++++ b/kernel/sysctl.c +@@ -376,15 +376,6 @@ static struct ctl_table kern_table[] = { + .mode = 0644, + .proc_handler = proc_dointvec, + }, +- { +- .procname = "timer_migration", +- .data = &sysctl_timer_migration, +- .maxlen = sizeof(unsigned int), +- .mode = 0644, +- .proc_handler = proc_dointvec_minmax, +- .extra1 = &zero, +- .extra2 = &one, +- }, + #ifdef CONFIG_SCHEDSTATS + { + .procname = "sched_schedstats", +@@ -1200,6 +1191,15 @@ static struct ctl_table kern_table[] = { + .extra1 = &zero, + .extra2 = &one, + }, ++#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON) ++ { ++ .procname = "timer_migration", ++ .data = &sysctl_timer_migration, ++ .maxlen = sizeof(unsigned int), ++ .mode = 0644, ++ .proc_handler = timer_migration_handler, ++ }, ++#endif + { } + }; + +diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h +index ecd2ff4..3ebdda4 100644 +--- a/kernel/time/tick-internal.h ++++ b/kernel/time/tick-internal.h +@@ -165,3 +165,17 @@ extern void do_timer(unsigned long ticks); + extern void update_wall_time(void); + + extern u64 get_next_timer_interrupt(unsigned long basej, u64 basem); ++ ++#ifdef CONFIG_NO_HZ_COMMON ++extern unsigned long tick_nohz_active; ++#else ++#define tick_nohz_active (0) ++#endif ++ ++#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON) ++extern void timers_update_migration(void); ++#else ++static inline void timers_update_migration(void) { } ++#endif ++ ++DECLARE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases); +diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c +index 625c116..6c92920 100644 +--- a/kernel/time/tick-sched.c ++++ b/kernel/time/tick-sched.c +@@ -412,7 +412,7 @@ void __init tick_nohz_init(void) + /* + * NO HZ enabled ? + */ +-int tick_nohz_active __read_mostly; ++unsigned long tick_nohz_active __read_mostly; + /* + * Enable / Disable tickless mode + */ +@@ -973,6 +973,16 @@ static void tick_nohz_handler(struct clock_event_device *dev) + tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1); + } + ++static inline void tick_nohz_activate(struct tick_sched *ts, int mode) ++{ ++ if (!tick_nohz_enabled) ++ return; ++ ts->nohz_mode = mode; ++ /* One update is enough */ ++ if (!test_and_set_bit(0, &tick_nohz_active)) ++ timers_update_migration(); ++} ++ + /** + * tick_nohz_switch_to_nohz - switch to nohz mode + */ +@@ -987,9 +997,6 @@ static void tick_nohz_switch_to_nohz(void) + if (tick_switch_to_oneshot(tick_nohz_handler)) + return; + +- tick_nohz_active = 1; +- ts->nohz_mode = NOHZ_MODE_LOWRES; +- + /* + * Recycle the hrtimer in ts, so we can share the + * hrtimer_forward with the highres code. +@@ -1001,6 +1008,7 @@ static void tick_nohz_switch_to_nohz(void) + hrtimer_forward_now(&ts->sched_timer, tick_period); + hrtimer_set_expires(&ts->sched_timer, next); + tick_program_event(next, 1); ++ tick_nohz_activate(ts, NOHZ_MODE_LOWRES); + } + + /* +@@ -1052,6 +1060,7 @@ static inline void tick_check_nohz_this_cpu(void) + + static inline void tick_nohz_switch_to_nohz(void) { } + static inline void tick_check_nohz_this_cpu(void) { } ++static inline void tick_nohz_activate(struct tick_sched *ts, int mode) { } + + #endif /* CONFIG_NO_HZ_COMMON */ + +@@ -1137,13 +1146,7 @@ void tick_setup_sched_timer(void) + + hrtimer_forward(&ts->sched_timer, now, tick_period); + hrtimer_start_expires(&ts->sched_timer, HRTIMER_MODE_ABS_PINNED); +- +-#ifdef CONFIG_NO_HZ_COMMON +- if (tick_nohz_enabled) { +- ts->nohz_mode = NOHZ_MODE_HIGHRES; +- tick_nohz_active = 1; +- } +-#endif ++ tick_nohz_activate(ts, NOHZ_MODE_HIGHRES); + } + #endif /* HIGH_RES_TIMERS */ + +diff --git a/kernel/time/timer_list.c b/kernel/time/timer_list.c +index 9174c0a..d7dd92a 100644 +--- a/kernel/time/timer_list.c ++++ b/kernel/time/timer_list.c +@@ -20,6 +20,7 @@ + + #include + ++#include "tick-internal.h" + + struct timer_list_iter { + int cpu; +@@ -29,8 +30,6 @@ struct timer_list_iter { + + typedef void (*print_fn_t)(struct seq_file *m, unsigned int *classes); + +-DECLARE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases); +- + /* + * This allows printing both to /proc/timer_list and + * to the console (on SysRq-Q): +diff --git a/kernel/timer.c b/kernel/timer.c +index dc85e24..4fcb630 100644 +--- a/kernel/timer.c ++++ b/kernel/timer.c +@@ -49,6 +49,8 @@ + #include + #include + ++#include "time/tick-internal.h" ++ + #define CREATE_TRACE_POINTS + #include + +@@ -85,6 +87,7 @@ struct tvec_base { + unsigned long next_timer; + unsigned long active_timers; + int cpu; ++ bool migration_enabled; + struct tvec_root tv1; + struct tvec tv2; + struct tvec tv3; +@@ -97,6 +100,58 @@ struct tvec_base boot_tvec_bases; + EXPORT_SYMBOL(boot_tvec_bases); + static DEFINE_PER_CPU(struct tvec_base *, tvec_bases) = &boot_tvec_bases; + ++#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON) ++unsigned int sysctl_timer_migration = 1; ++ ++void timers_update_migration(void) ++{ ++ bool on = sysctl_timer_migration && tick_nohz_active; ++ unsigned int cpu; ++ struct tvec_base *tvec_base = this_cpu_read(tvec_bases); ++ struct hrtimer_cpu_base *hrtimer_base = this_cpu_ptr(&hrtimer_bases); ++ ++ /* Avoid the loop, if nothing to update */ ++ if (tvec_base->migration_enabled == on) ++ return; ++ ++ for_each_possible_cpu(cpu) { ++ tvec_base = per_cpu(tvec_bases, cpu); ++ tvec_base->migration_enabled = on; ++ hrtimer_base = &per_cpu(hrtimer_bases, cpu); ++ hrtimer_base->migration_enabled = on; ++ } ++} ++ ++int timer_migration_handler(struct ctl_table *table, int write, ++ void __user *buffer, size_t *lenp, ++ loff_t *ppos) ++{ ++ static DEFINE_MUTEX(mutex); ++ int ret; ++ ++ mutex_lock(&mutex); ++ ret = proc_dointvec(table, write, buffer, lenp, ppos); ++ if (!ret && write) ++ timers_update_migration(); ++ mutex_unlock(&mutex); ++ return ret; ++} ++ ++static inline struct tvec_base *get_target_base(struct tvec_base *base, ++ int pinned) ++{ ++ if (pinned || !base->migration_enabled) ++ return this_cpu_read(tvec_bases); ++ return per_cpu(tvec_bases, get_nohz_timer_target()); ++} ++#else ++static inline struct tvec_base *get_target_base(struct tvec_base *base, ++ int pinned) ++{ ++ return this_cpu_read(tvec_bases); ++} ++#endif ++ + /* Functions below help us manage 'deferrable' flag */ + static inline unsigned int tbase_get_deferrable(struct tvec_base *base) + { +@@ -793,11 +848,11 @@ static inline struct tvec_base *switch_timer_base(struct timer_list *timer, + + static inline int + __mod_timer(struct timer_list *timer, unsigned long expires, +- bool pending_only, int pinned) ++ bool pending_only, int pinned) + { + struct tvec_base *base, *new_base; + unsigned long flags; +- int ret = 0 , cpu; ++ int ret = 0; + + timer_stats_timer_set_start_info(timer); + BUG_ON(!timer->function); +@@ -810,16 +865,7 @@ __mod_timer(struct timer_list *timer, unsigned long expires, + + debug_activate(timer, expires); + +- preempt_disable_rt(); +- cpu = smp_processor_id(); +- +-#if defined(CONFIG_NO_HZ_COMMON) && defined(CONFIG_SMP) +- if (!pinned && get_sysctl_timer_migration()) +- cpu = get_nohz_timer_target(); +-#endif +- preempt_enable_rt(); +- +- new_base = per_cpu(tvec_bases, cpu); ++ new_base = get_target_base(base, pinned); + + if (base != new_base) { + /* +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/turn-off-write-same-in-smartqpi-driver.patch b/kernel-rt/centos/patches/turn-off-write-same-in-smartqpi-driver.patch new file mode 100644 index 00000000..61dc4c1f --- /dev/null +++ b/kernel-rt/centos/patches/turn-off-write-same-in-smartqpi-driver.patch @@ -0,0 +1,28 @@ +From 81371b5453a5a08d8d03d37aa993b41a7169b342 Mon Sep 17 00:00:00 2001 +Message-Id: <81371b5453a5a08d8d03d37aa993b41a7169b342.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Jim Somerville +Date: Tue, 6 Mar 2018 12:54:40 -0500 +Subject: [PATCH 31/32] turn off write same in smartqpi driver + +Signed-off-by: Jim Somerville +--- + drivers/scsi/smartpqi/smartpqi_init.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c +index 2c6b546..6968c48 100644 +--- a/drivers/scsi/smartpqi/smartpqi_init.c ++++ b/drivers/scsi/smartpqi/smartpqi_init.c +@@ -5843,6 +5843,7 @@ static struct scsi_host_template pqi_driver_template = { + .slave_alloc = pqi_slave_alloc, + .sdev_attrs = pqi_sdev_attrs, + .shost_attrs = pqi_shost_attrs, ++ .no_write_same = 1, + }; + + static int pqi_register_scsi(struct pqi_ctrl_info *ctrl_info) +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/x86-enable-DMA-CMA-with-swiotlb.patch b/kernel-rt/centos/patches/x86-enable-DMA-CMA-with-swiotlb.patch new file mode 100644 index 00000000..3cbe751a --- /dev/null +++ b/kernel-rt/centos/patches/x86-enable-DMA-CMA-with-swiotlb.patch @@ -0,0 +1,182 @@ +From 37680a25caeba0ee4e5f26cb524ff8b4faa5c0f5 Mon Sep 17 00:00:00 2001 +Message-Id: <37680a25caeba0ee4e5f26cb524ff8b4faa5c0f5.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Akinobu Mita +Date: Wed, 4 Jun 2014 16:06:50 -0700 +Subject: [PATCH 13/32] x86: enable DMA CMA with swiotlb + +commit 9c5a3621427da68afe6a078cadf807d2c8cc1d12 upstream. +Ported-by: Nam Ninh + +The DMA Contiguous Memory Allocator support on x86 is disabled when +swiotlb config option is enabled. So DMA CMA is always disabled on +x86_64 because swiotlb is always enabled. This attempts to support for +DMA CMA with enabling swiotlb config option. + +The contiguous memory allocator on x86 is integrated in the function +dma_generic_alloc_coherent() which is .alloc callback in nommu_dma_ops +for dma_alloc_coherent(). + +x86_swiotlb_alloc_coherent() which is .alloc callback in swiotlb_dma_ops +tries to allocate with dma_generic_alloc_coherent() firstly and then +swiotlb_alloc_coherent() is called as a fallback. + +The main part of supporting DMA CMA with swiotlb is that changing +x86_swiotlb_free_coherent() which is .free callback in swiotlb_dma_ops +for dma_free_coherent() so that it can distinguish memory allocated by +dma_generic_alloc_coherent() from one allocated by +swiotlb_alloc_coherent() and release it with dma_generic_free_coherent() +which can handle contiguous memory. This change requires making +is_swiotlb_buffer() global function. + +This also needs to change .free callback in the dma_map_ops for amd_gart +and sta2x11, because these dma_ops are also using +dma_generic_alloc_coherent(). + +Signed-off-by: Akinobu Mita +Acked-by: Marek Szyprowski +Acked-by: Konrad Rzeszutek Wilk +Cc: David Woodhouse +Cc: Don Dutile +Cc: Thomas Gleixner +Cc: Ingo Molnar +Cc: "H. Peter Anvin" +Cc: Andi Kleen +Cc: Yinghai Lu +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +Signed-off-by: Jim Somerville +--- + arch/x86/Kconfig | 2 +- + arch/x86/include/asm/swiotlb.h | 7 +++++++ + arch/x86/kernel/amd_gart_64.c | 2 +- + arch/x86/kernel/pci-swiotlb.c | 9 ++++++--- + arch/x86/pci/sta2x11-fixup.c | 6 ++---- + include/linux/swiotlb.h | 2 ++ + lib/swiotlb.c | 2 +- + 7 files changed, 20 insertions(+), 10 deletions(-) + +diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig +index bdcca71..f67aa39 100644 +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -44,7 +44,7 @@ config X86 + select ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH if SMP + select ARCH_WANT_OPTIONAL_GPIOLIB + select ARCH_WANT_FRAME_POINTERS +- select HAVE_DMA_CONTIGUOUS if !SWIOTLB ++ select HAVE_DMA_CONTIGUOUS + select HAVE_KRETPROBES + select HAVE_OPTPROBES + select HAVE_KPROBES_ON_FTRACE +diff --git a/arch/x86/include/asm/swiotlb.h b/arch/x86/include/asm/swiotlb.h +index 977f176..ab05d73 100644 +--- a/arch/x86/include/asm/swiotlb.h ++++ b/arch/x86/include/asm/swiotlb.h +@@ -29,4 +29,11 @@ static inline void pci_swiotlb_late_init(void) + + static inline void dma_mark_clean(void *addr, size_t size) {} + ++extern void *x86_swiotlb_alloc_coherent(struct device *hwdev, size_t size, ++ dma_addr_t *dma_handle, gfp_t flags, ++ struct dma_attrs *attrs); ++extern void x86_swiotlb_free_coherent(struct device *dev, size_t size, ++ void *vaddr, dma_addr_t dma_addr, ++ struct dma_attrs *attrs); ++ + #endif /* _ASM_X86_SWIOTLB_H */ +diff --git a/arch/x86/kernel/amd_gart_64.c b/arch/x86/kernel/amd_gart_64.c +index b574b29..8e3842f 100644 +--- a/arch/x86/kernel/amd_gart_64.c ++++ b/arch/x86/kernel/amd_gart_64.c +@@ -512,7 +512,7 @@ gart_free_coherent(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_addr, struct dma_attrs *attrs) + { + gart_unmap_page(dev, dma_addr, size, DMA_BIDIRECTIONAL, NULL); +- free_pages((unsigned long)vaddr, get_order(size)); ++ dma_generic_free_coherent(dev, size, vaddr, dma_addr, attrs); + } + + static int gart_mapping_error(struct device *dev, dma_addr_t dma_addr) +diff --git a/arch/x86/kernel/pci-swiotlb.c b/arch/x86/kernel/pci-swiotlb.c +index 4853440..284d506 100644 +--- a/arch/x86/kernel/pci-swiotlb.c ++++ b/arch/x86/kernel/pci-swiotlb.c +@@ -16,7 +16,7 @@ + + int swiotlb __read_mostly; + +-static void *x86_swiotlb_alloc_coherent(struct device *hwdev, size_t size, ++void *x86_swiotlb_alloc_coherent(struct device *hwdev, size_t size, + dma_addr_t *dma_handle, gfp_t flags, + struct dma_attrs *attrs) + { +@@ -30,11 +30,14 @@ static void *x86_swiotlb_alloc_coherent(struct device *hwdev, size_t size, + return swiotlb_alloc_coherent(hwdev, size, dma_handle, flags); + } + +-static void x86_swiotlb_free_coherent(struct device *dev, size_t size, ++void x86_swiotlb_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_addr, + struct dma_attrs *attrs) + { +- swiotlb_free_coherent(dev, size, vaddr, dma_addr); ++ if (is_swiotlb_buffer(dma_to_phys(dev, dma_addr))) ++ swiotlb_free_coherent(dev, size, vaddr, dma_addr); ++ else ++ dma_generic_free_coherent(dev, size, vaddr, dma_addr, attrs); + } + + static struct dma_map_ops swiotlb_dma_ops = { +diff --git a/arch/x86/pci/sta2x11-fixup.c b/arch/x86/pci/sta2x11-fixup.c +index 9d8a509..5ceda85 100644 +--- a/arch/x86/pci/sta2x11-fixup.c ++++ b/arch/x86/pci/sta2x11-fixup.c +@@ -173,9 +173,7 @@ static void *sta2x11_swiotlb_alloc_coherent(struct device *dev, + { + void *vaddr; + +- vaddr = dma_generic_alloc_coherent(dev, size, dma_handle, flags, attrs); +- if (!vaddr) +- vaddr = swiotlb_alloc_coherent(dev, size, dma_handle, flags); ++ vaddr = x86_swiotlb_alloc_coherent(dev, size, dma_handle, flags, attrs); + *dma_handle = p2a(*dma_handle, to_pci_dev(dev)); + return vaddr; + } +@@ -183,7 +181,7 @@ static void *sta2x11_swiotlb_alloc_coherent(struct device *dev, + /* We have our own dma_ops: the same as swiotlb but from alloc (above) */ + static struct dma_map_ops sta2x11_dma_ops = { + .alloc = sta2x11_swiotlb_alloc_coherent, +- .free = swiotlb_free_coherent, ++ .free = x86_swiotlb_free_coherent, + .map_page = swiotlb_map_page, + .unmap_page = swiotlb_unmap_page, + .map_sg = swiotlb_map_sg_attrs, +diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h +index 16c296a..65c4a7a 100644 +--- a/include/linux/swiotlb.h ++++ b/include/linux/swiotlb.h +@@ -117,4 +117,6 @@ static inline void swiotlb_free(void) { } + #endif + + extern void swiotlb_print_info(void); ++extern int is_swiotlb_buffer(phys_addr_t paddr); ++ + #endif /* __LINUX_SWIOTLB_H */ +diff --git a/lib/swiotlb.c b/lib/swiotlb.c +index ffcaff5..d89c82a 100644 +--- a/lib/swiotlb.c ++++ b/lib/swiotlb.c +@@ -404,7 +404,7 @@ void __init swiotlb_free(void) + io_tlb_nslabs = 0; + } + +-static int is_swiotlb_buffer(phys_addr_t paddr) ++int is_swiotlb_buffer(phys_addr_t paddr) + { + return paddr >= io_tlb_start && paddr < io_tlb_end; + } +-- +1.8.3.1 + diff --git a/kernel-rt/centos/patches/x86-make-dma_alloc_coherent-return-zeroed-memory-if-.patch b/kernel-rt/centos/patches/x86-make-dma_alloc_coherent-return-zeroed-memory-if-.patch new file mode 100644 index 00000000..d87c8a91 --- /dev/null +++ b/kernel-rt/centos/patches/x86-make-dma_alloc_coherent-return-zeroed-memory-if-.patch @@ -0,0 +1,87 @@ +From 78743472402604dc4fc3304aed9634ae9fe845dd Mon Sep 17 00:00:00 2001 +Message-Id: <78743472402604dc4fc3304aed9634ae9fe845dd.1528226387.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Akinobu Mita +Date: Wed, 4 Jun 2014 16:06:48 -0700 +Subject: [PATCH 17/32] x86: make dma_alloc_coherent() return zeroed memory if + CMA is enabled + +This patchset enhances the DMA Contiguous Memory Allocator on x86. + +Currently the DMA CMA is only supported with pci-nommu dma_map_ops and +furthermore it can't be enabled on x86_64. But I would like to allocate +big contiguous memory with dma_alloc_coherent() and tell it to the device +that requires it, regardless of which dma mapping implementation is +actually used in the system. + +So this makes it work with swiotlb and intel-iommu dma_map_ops, too. And +this also extends "cma=" kernel parameter to specify placement constraint +by the physical address range of memory allocations. For example, CMA +allocates memory below 4GB by "cma=64M@0-4G", it is required for the +devices only supporting 32-bit addressing on 64-bit systems without iommu. + +This patch (of 5): + +Calling dma_alloc_coherent() with __GFP_ZERO must return zeroed memory. + +But when the contiguous memory allocator (CMA) is enabled on x86 and the +memory region is allocated by dma_alloc_from_contiguous(), it doesn't +return zeroed memory. Because dma_generic_alloc_coherent() forgot to fill +the memory region with zero if it was allocated by +dma_alloc_from_contiguous() + +Most implementations of dma_alloc_coherent() return zeroed memory +regardless of whether __GFP_ZERO is specified. So this fixes it by +unconditionally zeroing the allocated memory region. + +Alternatively, we could fix dma_alloc_from_contiguous() to return zeroed +out memory and remove memset() from all caller of it. But we can't simply +remove the memset on arm because __dma_clear_buffer() is used there for +ensuring cache flushing and it is used in many places. Of course we can +do redundant memset in dma_alloc_from_contiguous(), but I think this patch +is less impact for fixing this problem. + +Signed-off-by: Akinobu Mita +Cc: Marek Szyprowski +Cc: Konrad Rzeszutek Wilk +Cc: David Woodhouse +Cc: Don Dutile +Cc: Thomas Gleixner +Cc: Ingo Molnar +Cc: "H. Peter Anvin" +Cc: Andi Kleen +Cc: Yinghai Lu +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +(cherry picked from commit d92ef66c4f8fdf7a24736b1ab6c48d32de9bfc07) +Signed-off-by: Jim Somerville +--- + arch/x86/kernel/pci-dma.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c +index 77a4e62..c84ffe7 100644 +--- a/arch/x86/kernel/pci-dma.c ++++ b/arch/x86/kernel/pci-dma.c +@@ -99,7 +99,7 @@ void *dma_generic_alloc_coherent(struct device *dev, size_t size, + + dma_mask = dma_alloc_coherent_mask(dev, flag); + +- flag |= __GFP_ZERO; ++ flag &= ~__GFP_ZERO; + again: + page = NULL; + /* CMA can be used only in the context which permits sleeping */ +@@ -130,7 +130,7 @@ again: + + return NULL; + } +- ++ memset(page_address(page), 0, size); + *dma_addr = addr; + return page_address(page); + } +-- +1.8.3.1 + diff --git a/kernel-rt/centos/srpm_path b/kernel-rt/centos/srpm_path new file mode 100644 index 00000000..c5cf404d --- /dev/null +++ b/kernel-rt/centos/srpm_path @@ -0,0 +1 @@ +mirror:Source/kernel-rt-3.10.0-862.6.3.rt56.811.el7.src.rpm diff --git a/kernel-rt/files/centos.cer b/kernel-rt/files/centos.cer new file mode 100644 index 00000000..00a55801 Binary files /dev/null and b/kernel-rt/files/centos.cer differ diff --git a/kernel-rt/files/ima_signing_key.pub b/kernel-rt/files/ima_signing_key.pub new file mode 100644 index 00000000..89bb70b6 Binary files /dev/null and b/kernel-rt/files/ima_signing_key.pub differ diff --git a/kernel-std/centos/build_srpm.data b/kernel-std/centos/build_srpm.data new file mode 100644 index 00000000..065981e7 --- /dev/null +++ b/kernel-std/centos/build_srpm.data @@ -0,0 +1,4 @@ +COPY_LIST="files/*" +TIS_PATCH_VER=35 +BUILD_IS_BIG=10 +BUILD_IS_SLOW=12 diff --git a/kernel-std/centos/meta_patches/Build-logic-and-sources-for-TiC.patch b/kernel-std/centos/meta_patches/Build-logic-and-sources-for-TiC.patch new file mode 100644 index 00000000..922974a4 --- /dev/null +++ b/kernel-std/centos/meta_patches/Build-logic-and-sources-for-TiC.patch @@ -0,0 +1,231 @@ +From a75195ff25cd30a4662cae5835d32a2403032a33 Mon Sep 17 00:00:00 2001 +From: Jim Somerville +Date: Fri, 20 Apr 2018 14:51:56 -0400 +Subject: [PATCH 1/5] Build logic and sources for TiC + +Signed-off-by: Jim Somerville +--- + SPECS/kernel.spec | 73 +++++++++++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 60 insertions(+), 13 deletions(-) + +diff --git a/SPECS/kernel.spec b/SPECS/kernel.spec +index 31a5f11..eef356a 100644 +--- a/SPECS/kernel.spec ++++ b/SPECS/kernel.spec +@@ -5,7 +5,8 @@ Summary: The Linux kernel + + %define dist .el7 + +-# % define buildid .local ++# This is the WRS patch release ++%define buildid .%{tis_patch_ver}.tis + + # For a kernel released for public testing, released_kernel should be 1. + # For internal testing builds during development, it should be 0. +@@ -14,12 +15,14 @@ Summary: The Linux kernel + %global distro_build 862 + + %define rpmversion 3.10.0 +-%define pkgrelease 862.6.3.el7 ++%define _pkgrelease 862.6.3 ++%define pkgrelease %{_pkgrelease}.el7 ++ + + # allow pkg_release to have configurable %%{?dist} tag + %define specrelease 862.6.3%{?dist} + +-%define pkg_release %{specrelease}%{?buildid} ++%define pkg_release %{specrelease}%{buildid} + + # The kernel tarball/base version + %define rheltarball %{rpmversion}-%{pkgrelease} +@@ -66,7 +69,7 @@ Summary: The Linux kernel + %define with_dbgonly %{?_with_dbgonly: 1} %{?!_with_dbgonly: 0} + + # Control whether we perform a compat. check against published ABI. +-%define with_kabichk %{?_without_kabichk: 0} %{?!_without_kabichk: 1} ++%define with_kabichk 0 + + # Control whether we perform a compat. check against DUP ABI. + %define with_kabidupchk 1 +@@ -89,7 +92,7 @@ Summary: The Linux kernel + # Set debugbuildsenabled to 1 for production (build separate debug kernels) + # and 0 for rawhide (all kernels are debug kernels). + # See also 'make debug' and 'make release'. RHEL only ever does 1. +-%define debugbuildsenabled 1 ++%define debugbuildsenabled 0 + + %define with_gcov %{?_with_gcov: 1} %{?!_with_gcov: 0} + +@@ -370,6 +373,7 @@ BuildRequires: bison flex + # required for zfcpdump + BuildRequires: glibc-static + %endif ++BuildRequires: util-linux + + Source0: linux-%{rpmversion}-%{pkgrelease}.tar.xz + +@@ -435,6 +439,12 @@ Patch1000: debrand-single-cpu.patch + Patch1001: debrand-rh_taint.patch + Patch1002: debrand-rh-i686-cpu.patch + ++# Titanium Cloud sources here. ++# Not sure if we need to worry about numerical collisions between ++# SourceX and PatchX, so let's not risk it ++Source30000: kernel-3.10.0-x86_64.config.tis_extra ++Source30001: ima_signing_key.pub ++ + BuildRoot: %{_tmppath}/kernel-%{KVRA}-root + + %description +@@ -586,6 +596,13 @@ This package provides debug information for package kernel-tools. + + %endif # with_tools + ++%ifarch x86_64 ++%package unsigned ++Summary: Unsigned build of the Linux kernel ++%description unsigned ++Contains an unsigned version of the Linux kernel ++%endif # x86_64 ++ + %if %{with_gcov} + %package gcov + Summary: gcov graph and source files for coverage data collection. +@@ -751,6 +768,9 @@ cd linux-%{KVRA} + # Drop some necessary files from the source dir into the buildroot + cp $RPM_SOURCE_DIR/kernel-%{version}-*.config . + ++# Copy any TiS-specific config changes ++cp $RPM_SOURCE_DIR/kernel-%{version}-*.config.tis_extra . ++ + ApplyOptionalPatch linux-kernel-test.patch + ApplyOptionalPatch debrand-single-cpu.patch + ApplyOptionalPatch debrand-rh_taint.patch +@@ -795,6 +815,15 @@ for i in *.config + do + mv $i .config + Arch=`head -1 .config | cut -b 3-` ++ ++ # Handle Titanium Cloud customizations. Use -n to match oldnoconfig below. We want this before ++ # the make line below so that the one below removes any dependencies of ones that we ++ # turn off here. We also want it before "make listnewconfig" so that we can set the ++ # config option for new configs introduced in the Titanium Cloud patches. ++ if [ -f ${i}.tis_extra ]; then ++ scripts/kconfig/merge_config.sh -m -n .config ${i}.tis_extra ++ fi ++ + make %{?cross_opts} ARCH=$Arch listnewconfig | grep -E '^CONFIG_' >.newoptions || true + %if %{listnewconfig_fail} + if [ -s .newoptions ]; then +@@ -868,12 +897,13 @@ BuildKernel() { + + # and now to start the build process + +- make %{?cross_opts} -s mrproper ++ make -j"%(nproc)" %{?cross_opts} -s mrproper + + cp %{SOURCE11} . # x509.genkey + cp %{SOURCE12} . # extra_certificates + cp %{SOURCE15} . # rheldup3.x509 + cp %{SOURCE16} . # rhelkpatch1.x509 ++ cp %{SOURCE30001} . # ima_signing_key.pub + + cp configs/$Config .config + +@@ -888,8 +918,8 @@ BuildKernel() { + fi + %endif + +- make -s %{?cross_opts} ARCH=$Arch oldnoconfig >/dev/null +- make -s %{?cross_opts} ARCH=$Arch V=1 %{?_smp_mflags} KCFLAGS="%{?kcflags}" WITH_GCOV="%{?with_gcov}" $MakeTarget %{?sparse_mflags} ++ make -s -j"%(nproc)" %{?cross_opts} ARCH=$Arch oldnoconfig >/dev/null ++ make -s -j"%(nproc)" %{?cross_opts} ARCH=$Arch V=1 %{?_smp_mflags} KCFLAGS="%{?kcflags}" WITH_GCOV="%{?with_gcov}" $MakeTarget %{?sparse_mflags} + + if [ "$Flavour" != "kdump" ]; then + make -s %{?cross_opts} ARCH=$Arch V=1 %{?_smp_mflags} KCFLAGS="%{?kcflags}" WITH_GCOV="%{?with_gcov}" modules %{?sparse_mflags} || exit 1 +@@ -913,6 +943,8 @@ BuildKernel() { + fi + # EFI SecureBoot signing, x86_64-only + %ifarch x86_64 ++ cp $KernelImage vmlinuz.unsigned ++ $CopyKernel vmlinuz.unsigned $RPM_BUILD_ROOT/%{image_install_path}/vmlinuz.unsigned + %pesign -s -i $KernelImage -o $KernelImage.signed -a %{SOURCE13} -c %{SOURCE13} + mv $KernelImage.signed $KernelImage + %endif +@@ -929,7 +961,7 @@ BuildKernel() { + if [ "$Flavour" != "kdump" ]; then + # Override $(mod-fw) because we don't want it to install any firmware + # we'll get it from the linux-firmware package and we don't want conflicts +- make -s %{?cross_opts} ARCH=$Arch INSTALL_MOD_PATH=$RPM_BUILD_ROOT modules_install KERNELRELEASE=$KernelVer mod-fw= ++ make -s -j"%(nproc)" %{?cross_opts} ARCH=$Arch INSTALL_MOD_PATH=$RPM_BUILD_ROOT modules_install KERNELRELEASE=$KernelVer mod-fw= + %if %{with_gcov} + # install gcov-needed files to $BUILDROOT/$BUILD/...: + # gcov_info->filename is absolute path +@@ -939,7 +971,7 @@ BuildKernel() { + %endif + fi + %ifarch %{vdso_arches} +- make -s %{?cross_opts} ARCH=$Arch INSTALL_MOD_PATH=$RPM_BUILD_ROOT vdso_install KERNELRELEASE=$KernelVer ++ make -s -j"%(nproc)" %{?cross_opts} ARCH=$Arch INSTALL_MOD_PATH=$RPM_BUILD_ROOT vdso_install KERNELRELEASE=$KernelVer + if [ ! -s ldconfig-kernel.conf ]; then + echo > ldconfig-kernel.conf "\ + # Placeholder file, no vDSO hwcap entries used in this kernel." +@@ -1148,6 +1180,12 @@ BuildKernel() { + cp signing_key.priv signing_key.priv.sign${Flavour:+.${Flavour}} + cp signing_key.x509 signing_key.x509.sign${Flavour:+.${Flavour}} + ++ # WRS: Copy these keys as part of the devel package ++ # The Module signing keys are to ensure that only Out-of-tree ++ # built against the Titanium Kernel get signed and loaded sans warnings ++ cp signing_key.priv ${RPM_BUILD_ROOT}/lib/modules/${KernelVer}/build/ ++ cp signing_key.x509 ${RPM_BUILD_ROOT}/lib/modules/${KernelVer}/build/ ++ + # remove files that will be auto generated by depmod at rpm -i time + for i in alias alias.bin builtin.bin ccwmap dep dep.bin ieee1394map inputmap isapnpmap ofmap pcimap seriomap symbols symbols.bin usbmap softdep devname + do +@@ -1208,15 +1246,15 @@ make %{?cross_opts} %{?_smp_mflags} -C tools/power/cpupower CPUFREQ_BENCH=false + %endif + %ifarch x86_64 + pushd tools/power/x86/x86_energy_perf_policy/ +- make ++ make -j"%(nproc)" + popd + pushd tools/power/x86/turbostat +- make ++ make -j"%(nproc)" + popd + %endif #turbostat/x86_energy_perf_policy + %endif + pushd tools +-make tmon ++make -j"%(nproc)" tmon + popd + %endif + +@@ -1487,6 +1525,10 @@ fi}\ + %{expand:\ + %{_sbindir}/new-kernel-pkg --package kernel%{?-v:-%{-v*}} --install %{KVRA}%{?-v:.%{-v*}} || exit $?\ + }\ ++# If this is a pkg upgrade (ie installed as a patch), set the reboot flag\ ++if [ $1 -gt 1 ] ; then\ ++ touch /var/run/node_is_patched_rr\ ++fi\ + %{nil} + + # +@@ -1697,6 +1739,11 @@ fi + %kernel_variant_files %{with_debug} debug + %kernel_variant_files %{with_kdump} kdump + ++%ifarch x86_64 ++%files unsigned ++/boot/vmlinuz.unsigned ++%endif ++ + %changelog + * Tue Jun 26 2018 CentOS Sources - 3.10.0-862.6.3.el7 + - Apply debranding changes +-- +2.7.4 + diff --git a/kernel-std/centos/meta_patches/Compile-issues.patch b/kernel-std/centos/meta_patches/Compile-issues.patch new file mode 100644 index 00000000..cdee5a12 --- /dev/null +++ b/kernel-std/centos/meta_patches/Compile-issues.patch @@ -0,0 +1,34 @@ +From 0bdbfc48c66177522eae9bb7c0449f39f418e609 Mon Sep 17 00:00:00 2001 +From: Jim Somerville +Date: Wed, 30 May 2018 13:12:03 -0400 +Subject: [PATCH 4/5] Compile issues + +Signed-off-by: Jim Somerville +--- + SPECS/kernel.spec | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/SPECS/kernel.spec b/SPECS/kernel.spec +index 01dd989..d45c419 100644 +--- a/SPECS/kernel.spec ++++ b/SPECS/kernel.spec +@@ -474,6 +474,8 @@ Patch40024: aic94xx-Skip-reading-user-settings-if-flash-is-not-f.patch + Patch40025: dpt_i2o-fix-build-warning.patch + # DRBD was choking on write same + Patch40026: turn-off-write-same-in-smartqpi-driver.patch ++# Fix assorted compilation issues ++Patch40027: fix-compilation-issues.patch + + BuildRoot: %{_tmppath}/kernel-%{KVRA}-root + +@@ -833,6 +835,7 @@ ApplyOptionalPatch US103091-IMA-System-Configuration.patch + ApplyOptionalPatch aic94xx-Skip-reading-user-settings-if-flash-is-not-f.patch + ApplyOptionalPatch dpt_i2o-fix-build-warning.patch + ApplyOptionalPatch turn-off-write-same-in-smartqpi-driver.patch ++ApplyOptionalPatch fix-compilation-issues.patch + + # Any further pre-build tree manipulations happen here. + +-- +2.7.4 + diff --git a/kernel-std/centos/meta_patches/Kernel-source-patches-for-TiC.patch b/kernel-std/centos/meta_patches/Kernel-source-patches-for-TiC.patch new file mode 100644 index 00000000..cc7d1201 --- /dev/null +++ b/kernel-std/centos/meta_patches/Kernel-source-patches-for-TiC.patch @@ -0,0 +1,89 @@ +From d9d90b72c19c1d063272d2b84bd76c52514bf6ac Mon Sep 17 00:00:00 2001 +From: Jim Somerville +Date: Fri, 20 Apr 2018 16:13:47 -0400 +Subject: [PATCH 2/5] Kernel source patches for TiC + +Signed-off-by: Jim Somerville +--- + SPECS/kernel.spec | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 58 insertions(+) + +diff --git a/SPECS/kernel.spec b/SPECS/kernel.spec +index eef356a..f1a0092 100644 +--- a/SPECS/kernel.spec ++++ b/SPECS/kernel.spec +@@ -445,6 +445,36 @@ Patch1002: debrand-rh-i686-cpu.patch + Source30000: kernel-3.10.0-x86_64.config.tis_extra + Source30001: ima_signing_key.pub + ++# Titanium Cloud patches here. ++Patch40001: Fix-compile-issue-when-transparent-hugepages-are-off.patch ++Patch40002: Notification-of-death-of-arbitrary-processes.patch ++Patch40003: CGTS-3744-route-do-not-cache-fib-route-info-on-local.patch ++Patch40004: PCI-Add-ACS-quirk-for-Intel-Fortville-NICs.patch ++Patch40005: affine-compute-kernel-threads.patch ++Patch40006: Affine-irqs-and-workqueues-with-kthread_cpus.patch ++Patch40007: Make-kernel-start-eth-devices-at-offset.patch ++Patch40008: intel-iommu-allow-ignoring-Ethernet-device-RMRR-with.patch ++Patch40009: memblock-introduce-memblock_alloc_range.patch ++Patch40010: cma-add-placement-specifier-for-cma-kernel-parameter.patch ++Patch40011: x86-enable-DMA-CMA-with-swiotlb.patch ++Patch40012: Enable-building-mpt2sas-and-mpt3sas-as-builtin-for-C.patch ++Patch40013: Enable-building-kernel-with-CONFIG_BLK_DEV_NBD.patch ++Patch40014: x86-make-dma_alloc_coherent-return-zeroed-memory-if-.patch ++Patch40015: rcu-Don-t-wake-rcuc-X-kthreads-on-NOCB-CPUs.patch ++Patch40016: Porting-Cacheinfo-from-Kernel-4.10.17.patch ++Patch40017: Fix-cacheinfo-compilation-issues-for-3.10.patch ++Patch40018: cpuidle-menu-stop-seeking-deeper-idle-if-current-sta.patch ++Patch40019: cpuidle-menu-add-per-CPU-PM-QoS-resume-latency-consi.patch ++Patch40020: CPU-PM-expose-pm_qos_resume_latency-for-CPUs.patch ++Patch40021: cpuidle-menu-Avoid-taking-spinlock-for-accessing-QoS.patch ++Patch40022: US101216-IMA-support-in-Titanium-kernel.patch ++Patch40023: US103091-IMA-System-Configuration.patch ++# Fix compile warnings that break the build ++Patch40024: aic94xx-Skip-reading-user-settings-if-flash-is-not-f.patch ++Patch40025: dpt_i2o-fix-build-warning.patch ++# DRBD was choking on write same ++Patch40026: turn-off-write-same-in-smartqpi-driver.patch ++ + BuildRoot: %{_tmppath}/kernel-%{KVRA}-root + + %description +@@ -776,6 +806,34 @@ ApplyOptionalPatch debrand-single-cpu.patch + ApplyOptionalPatch debrand-rh_taint.patch + ApplyOptionalPatch debrand-rh-i686-cpu.patch + ++# Titanium Cloud patches here. ++ApplyOptionalPatch Fix-compile-issue-when-transparent-hugepages-are-off.patch ++ApplyOptionalPatch Notification-of-death-of-arbitrary-processes.patch ++ApplyOptionalPatch CGTS-3744-route-do-not-cache-fib-route-info-on-local.patch ++ApplyOptionalPatch PCI-Add-ACS-quirk-for-Intel-Fortville-NICs.patch ++ApplyOptionalPatch affine-compute-kernel-threads.patch ++ApplyOptionalPatch Affine-irqs-and-workqueues-with-kthread_cpus.patch ++ApplyOptionalPatch Make-kernel-start-eth-devices-at-offset.patch ++ApplyOptionalPatch intel-iommu-allow-ignoring-Ethernet-device-RMRR-with.patch ++ApplyOptionalPatch memblock-introduce-memblock_alloc_range.patch ++ApplyOptionalPatch cma-add-placement-specifier-for-cma-kernel-parameter.patch ++ApplyOptionalPatch x86-enable-DMA-CMA-with-swiotlb.patch ++ApplyOptionalPatch Enable-building-mpt2sas-and-mpt3sas-as-builtin-for-C.patch ++ApplyOptionalPatch Enable-building-kernel-with-CONFIG_BLK_DEV_NBD.patch ++ApplyOptionalPatch x86-make-dma_alloc_coherent-return-zeroed-memory-if-.patch ++ApplyOptionalPatch rcu-Don-t-wake-rcuc-X-kthreads-on-NOCB-CPUs.patch ++ApplyOptionalPatch Porting-Cacheinfo-from-Kernel-4.10.17.patch ++ApplyOptionalPatch Fix-cacheinfo-compilation-issues-for-3.10.patch ++ApplyOptionalPatch cpuidle-menu-stop-seeking-deeper-idle-if-current-sta.patch ++ApplyOptionalPatch cpuidle-menu-add-per-CPU-PM-QoS-resume-latency-consi.patch ++ApplyOptionalPatch CPU-PM-expose-pm_qos_resume_latency-for-CPUs.patch ++ApplyOptionalPatch cpuidle-menu-Avoid-taking-spinlock-for-accessing-QoS.patch ++ApplyOptionalPatch US101216-IMA-support-in-Titanium-kernel.patch ++ApplyOptionalPatch US103091-IMA-System-Configuration.patch ++ApplyOptionalPatch aic94xx-Skip-reading-user-settings-if-flash-is-not-f.patch ++ApplyOptionalPatch dpt_i2o-fix-build-warning.patch ++ApplyOptionalPatch turn-off-write-same-in-smartqpi-driver.patch ++ + # Any further pre-build tree manipulations happen here. + + chmod +x scripts/checkpatch.pl +-- +2.7.4 + diff --git a/kernel-std/centos/meta_patches/Lower-the-compiler-version-requirement.patch b/kernel-std/centos/meta_patches/Lower-the-compiler-version-requirement.patch new file mode 100644 index 00000000..d7dfa01d --- /dev/null +++ b/kernel-std/centos/meta_patches/Lower-the-compiler-version-requirement.patch @@ -0,0 +1,26 @@ +From 393eb8d3d3a15cda274f5d0c45763de9a8b3fcab Mon Sep 17 00:00:00 2001 +From: Jim Somerville +Date: Tue, 29 May 2018 13:06:24 -0400 +Subject: [PATCH 3/5] Lower the compiler version requirement + +Signed-off-by: Jim Somerville +--- + SPECS/kernel.spec | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/SPECS/kernel.spec b/SPECS/kernel.spec +index f1a0092..01dd989 100644 +--- a/SPECS/kernel.spec ++++ b/SPECS/kernel.spec +@@ -335,7 +335,7 @@ ExclusiveOS: Linux + # + BuildRequires: module-init-tools, patch >= 2.5.4, bash >= 2.03, sh-utils, tar + BuildRequires: xz, findutils, gzip, m4, perl, make >= 3.78, diffutils, gawk +-BuildRequires: gcc >= 4.8.5-28.el7_5.1, binutils >= 2.25, redhat-rpm-config >= 9.1.0-55 ++BuildRequires: gcc >= 4.8.5-16, binutils >= 2.25, redhat-rpm-config >= 9.1.0-55 + BuildRequires: hostname, net-tools, bc + BuildRequires: xmlto, asciidoc + BuildRequires: openssl +-- +2.7.4 + diff --git a/kernel-std/centos/meta_patches/Lower-the-linux-firmware-version-requirement.patch b/kernel-std/centos/meta_patches/Lower-the-linux-firmware-version-requirement.patch new file mode 100644 index 00000000..220def9c --- /dev/null +++ b/kernel-std/centos/meta_patches/Lower-the-linux-firmware-version-requirement.patch @@ -0,0 +1,26 @@ +From ec433bddc585744616d73daefc1b32326002a8ab Mon Sep 17 00:00:00 2001 +From: Jim Somerville +Date: Fri, 1 Jun 2018 16:17:43 -0400 +Subject: [PATCH 5/5] Lower the linux firmware version requirement + +Signed-off-by: Jim Somerville +--- + SPECS/kernel.spec | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/SPECS/kernel.spec b/SPECS/kernel.spec +index d45c419..9fdd37a 100644 +--- a/SPECS/kernel.spec ++++ b/SPECS/kernel.spec +@@ -301,7 +301,7 @@ Provides: kernel-modeset = 1\ + Provides: kernel-uname-r = %{KVRA}%{?1:.%{1}}\ + Requires(pre): %{kernel_prereq}\ + Requires(pre): %{initrd_prereq}\ +-Requires(pre): linux-firmware >= 20180113-61\ ++Requires(pre): linux-firmware >= 20170606-56\ + Requires(post): %{_sbindir}/new-kernel-pkg\ + Requires(post): system-release\ + Requires(preun): %{_sbindir}/new-kernel-pkg\ +-- +2.7.4 + diff --git a/kernel-std/centos/meta_patches/PATCH_ORDER b/kernel-std/centos/meta_patches/PATCH_ORDER new file mode 100644 index 00000000..7a3ea4aa --- /dev/null +++ b/kernel-std/centos/meta_patches/PATCH_ORDER @@ -0,0 +1,5 @@ +Build-logic-and-sources-for-TiC.patch +Kernel-source-patches-for-TiC.patch +Lower-the-compiler-version-requirement.patch +Compile-issues.patch +Lower-the-linux-firmware-version-requirement.patch diff --git a/kernel-std/centos/patches/Affine-irqs-and-workqueues-with-kthread_cpus.patch b/kernel-std/centos/patches/Affine-irqs-and-workqueues-with-kthread_cpus.patch new file mode 100644 index 00000000..01e5261e --- /dev/null +++ b/kernel-std/centos/patches/Affine-irqs-and-workqueues-with-kthread_cpus.patch @@ -0,0 +1,73 @@ +From e223cda3a8edd8fd935d589231c8e19a66bd8947 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Chris Friesen +Date: Tue, 24 Nov 2015 16:27:29 -0500 +Subject: [PATCH 06/26] Affine irqs and workqueues with kthread_cpus + +If the kthread_cpus boot arg is set it means we want to affine +kernel threads to the specified CPU mask as much as possible +in order to avoid doing work on other CPUs. + +In this commit we extend the meaning of that boot arg to also +apply to the CPU affinity of unbound and ordered workqueues. + +We also use the kthread_cpus value to determine the default irq +affinity. Specifically, as long as the previously-calculated +irq affinity intersects with the kthread_cpus affinity then we'll +use the intersection of the two as the default irq affinity. + +Signed-off-by: Chris Friesen +[VT: replacing spaces with tabs. Performed tests] +Signed-off-by: Vu Tran + +Signed-off-by: Jim Somerville +--- + kernel/irq/manage.c | 7 +++++++ + kernel/workqueue.c | 4 ++++ + 2 files changed, 11 insertions(+) + +diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c +index e639145..be46349 100644 +--- a/kernel/irq/manage.c ++++ b/kernel/irq/manage.c +@@ -366,6 +366,13 @@ setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask) + if (cpumask_intersects(mask, nodemask)) + cpumask_and(mask, mask, nodemask); + } ++ ++ /* This will narrow down the affinity further if we've specified ++ * a reduced cpu_kthread_mask in the boot args. ++ */ ++ if (cpumask_intersects(mask, cpu_kthread_mask)) ++ cpumask_and(mask, mask, cpu_kthread_mask); ++ + irq_do_set_affinity(&desc->irq_data, mask, false); + return 0; + } +diff --git a/kernel/workqueue.c b/kernel/workqueue.c +index 179cbb2..9524a6f 100644 +--- a/kernel/workqueue.c ++++ b/kernel/workqueue.c +@@ -5424,6 +5424,8 @@ static int __init init_workqueues(void) + + BUG_ON(!(attrs = alloc_workqueue_attrs(GFP_KERNEL))); + attrs->nice = std_nice[i]; ++ /* If we've specified a kthread mask apply it here too. */ ++ cpumask_copy(attrs->cpumask, cpu_kthread_mask); + unbound_std_wq_attrs[i] = attrs; + + /* +@@ -5434,6 +5436,8 @@ static int __init init_workqueues(void) + BUG_ON(!(attrs = alloc_workqueue_attrs(GFP_KERNEL))); + attrs->nice = std_nice[i]; + attrs->no_numa = true; ++ /* If we've specified a kthread mask apply it here too. */ ++ cpumask_copy(attrs->cpumask, cpu_kthread_mask); + ordered_wq_attrs[i] = attrs; + } + +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/CGTS-3744-route-do-not-cache-fib-route-info-on-local.patch b/kernel-std/centos/patches/CGTS-3744-route-do-not-cache-fib-route-info-on-local.patch new file mode 100644 index 00000000..57776310 --- /dev/null +++ b/kernel-std/centos/patches/CGTS-3744-route-do-not-cache-fib-route-info-on-local.patch @@ -0,0 +1,58 @@ +From 049ff2fee2c00370ddc9c9bb598fe1e89aaced2d Mon Sep 17 00:00:00 2001 +Message-Id: <049ff2fee2c00370ddc9c9bb598fe1e89aaced2d.1527544850.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Allain Legacy +Date: Fri, 29 Jan 2016 12:13:40 -0500 +Subject: [PATCH 03/26] CGTS-3744: route: do not cache fib route info on local + routes with oif + +For local routes that require a particular output interface we do not want to +cache the result. Caching the result causes incorrect behaviour when there are +multiple source addresses on the interface. The end result being that if the +intended recipient is waiting on that interface for the packet he won't receive +it because it will be delivered on the loopback interface and the IP_PKTINFO +ipi_ifindex will be set to the loopback interface as well. + +This can be tested by running a program such as "dhcp_release" which attempts +to inject a packet on a particular interface so that it is received by another +program on the same board. The receiving process should see an IP_PKTINFO +ipi_ifndex value of the source interface (e.g., eth1) instead of the loopback +interface (e.g., lo). The packet will still appear on the loopback interface +in tcpdump but the important aspect is that the CMSG info is correct. + +Sample dhcp_release command line: + + dhcp_release eth1 192.168.204.222 02:11:33:22:44:66 + +Signed-off-by: Allain Legacy +Signed-off-by: Jim Somerville +--- + net/ipv4/route.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/net/ipv4/route.c b/net/ipv4/route.c +index f19aca2..5246096 100644 +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -2057,6 +2057,17 @@ static struct rtable *__mkroute_output(const struct fib_result *res, + */ + if (fi && res->prefixlen < 4) + fi = NULL; ++ } else if ((type == RTN_LOCAL) && (orig_oif != 0)) { ++ /* ++ * For local routes that require a particular output interface we do ++ * not want to cache the result. Caching the result causes incorrect ++ * behaviour when there are multiple source addresses on the interface. ++ * The end result being that if the intended recipient is waiting on ++ * that interface for the packet he won't receive it because it will be ++ * delivered on the loopback interface and the IP_PKTINFO ipi_ifindex ++ * will be set to the loopback interface as well. ++ */ ++ fi = NULL; + } + + fnhe = NULL; +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/CPU-PM-expose-pm_qos_resume_latency-for-CPUs.patch b/kernel-std/centos/patches/CPU-PM-expose-pm_qos_resume_latency-for-CPUs.patch new file mode 100644 index 00000000..19066ed3 --- /dev/null +++ b/kernel-std/centos/patches/CPU-PM-expose-pm_qos_resume_latency-for-CPUs.patch @@ -0,0 +1,63 @@ +From 7a41b9bb6ebe53e024ad5fc56c12089467d7b133 Mon Sep 17 00:00:00 2001 +Message-Id: <7a41b9bb6ebe53e024ad5fc56c12089467d7b133.1527544850.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Alex Shi +Date: Thu, 12 Jan 2017 21:27:03 +0800 +Subject: [PATCH 20/26] CPU / PM: expose pm_qos_resume_latency for CPUs + +[ commit 37efa4b41ffb31dcdfc3beb97d47992bb2a083e5 from linux-stable ] + +The cpu-dma PM QoS constraint impacts all the cpus in the system. There is no way +to let the user to choose a PM QoS constraint per cpu. + +The following patch exposes to the userspace a per cpu based sysfs file +in order to let the userspace to change the value of the PM QoS latency +constraint. + +This change is inoperative in its form and the cpuidle governors have to +take into account the per cpu latency constraint in addition to the +global cpu-dma latency constraint in order to operate properly. + +BTW +The pm_qos_resume_latency usage defined in +Documentation/ABI/testing/sysfs-devices-power +The /sys/devices/.../power/pm_qos_resume_latency_us attribute +contains the PM QoS resume latency limit for the given device, +which is the maximum allowed time it can take to resume the +device, after it has been suspended at run time, from a resume +request to the moment the device will be ready to process I/O, +in microseconds. If it is equal to 0, however, this means that +the PM QoS resume latency may be arbitrary. + +Signed-off-by: Alex Shi +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Alex Kozyrev +Signed-off-by: Jim Somerville +--- + drivers/base/cpu.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c +index 803d2a0..1d9bba3 100644 +--- a/drivers/base/cpu.c ++++ b/drivers/base/cpu.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + #include "base.h" + +@@ -318,6 +319,7 @@ int register_cpu(struct cpu *cpu, int num) + per_cpu(cpu_sys_devices, num) = &cpu->dev; + if (!error) + register_cpu_under_node(num, cpu_to_node(num)); ++ dev_pm_qos_expose_latency_limit(&cpu->dev, 0); + + #ifdef CONFIG_KEXEC + if (!error) +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/Enable-building-kernel-with-CONFIG_BLK_DEV_NBD.patch b/kernel-std/centos/patches/Enable-building-kernel-with-CONFIG_BLK_DEV_NBD.patch new file mode 100644 index 00000000..d520d85f --- /dev/null +++ b/kernel-std/centos/patches/Enable-building-kernel-with-CONFIG_BLK_DEV_NBD.patch @@ -0,0 +1,36 @@ +From aeefa74788f568a9c3b49e18a17fc3d59657bbe0 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Chris Friesen +Date: Wed, 11 Jan 2017 13:38:37 -0500 +Subject: [PATCH 13/26] Enable building kernel with CONFIG_BLK_DEV_NBD + +By default, the CentOS 7.3 kernel will fail to build if +CONFIG_BLK_DEV_NBD is enabled, either as module or builtin. + +The issue seems to be due to the use of REQ_TYPE_SPECIAL in the +NBD code. Switching it to use REQ_TYPE_DRV_PRIV instead makes the +problem go away. + +Signed-off-by: Jim Somerville +--- + drivers/block/nbd.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c +index a40a4f0..e0c6b62 100644 +--- a/drivers/block/nbd.c ++++ b/drivers/block/nbd.c +@@ -616,7 +616,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, + fsync_bdev(bdev); + mutex_lock(&nbd->tx_lock); + blk_rq_init(NULL, &sreq); +- sreq.cmd_type = REQ_TYPE_SPECIAL; ++ sreq.cmd_type = REQ_TYPE_DRV_PRIV; + nbd_cmd(&sreq) = NBD_CMD_DISC; + + /* Check again after getting mutex back. */ +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/Enable-building-mpt2sas-and-mpt3sas-as-builtin-for-C.patch b/kernel-std/centos/patches/Enable-building-mpt2sas-and-mpt3sas-as-builtin-for-C.patch new file mode 100644 index 00000000..bc7daba9 --- /dev/null +++ b/kernel-std/centos/patches/Enable-building-mpt2sas-and-mpt3sas-as-builtin-for-C.patch @@ -0,0 +1,34236 @@ +From 90a9ed3030882fdaf1bf8c7b9f17e47821f20700 Mon Sep 17 00:00:00 2001 +Message-Id: <90a9ed3030882fdaf1bf8c7b9f17e47821f20700.1527544850.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Chris Friesen +Date: Mon, 9 Jan 2017 15:03:00 -0500 +Subject: [PATCH 12/26] Enable building mpt2sas and mpt3sas as builtin for + CentOS 7.3 + +In CentOS 7.3 the upstream mpt2sas/mpt3sas drivers are built from +essentially the same codebase with a flag defined to give the mpt2sas +behaviour. This is purely a problem in 7.3 due to how they have mangled +the drivers. More recently in the Linux kernel these have been merged +into a single driver, while previously in 7.2 they were two totally +separate drivers. + +The 7.3 code works fine when building as modules, but when building as +builtin it gives problems because KBUILD_MODNAME is not defined for files +that are included in multiple drivers. + +The workaround is to move the mpt2sas driver into its own directory, +copying the whole mpt3sas codebase into it. This required a number of +changes after duplicating the codebase: + +1) Add knowledge of the new "mpt2sas" directory to from drivers/scsi/Kconfig +and drivers/scsi/Makefile. + +2) Remove mention of mpt2sas from drivers/scsi/mpt3sas/Kconfig and +drivers/scsi/mpt3sas/Makefile. + +3) Remove mention of mpt3sas from drivers/scsi/mpt2sas/Kconfig and +drivers/scsi/mpt2sas/Makefile. + +4) In the mpt2sas directory, all functions/variables with "mpt3sas" in +the name which did not already have an mpt2sas equivalent were renamed +to instead use "mpt2sas". Thus "_mpt3sas_init" became "_mpt2sas_init". + +5) All other symbols that collided between the two modules were renamed +in the mpt2sas driver by appending "_mpt2sas". This included ones with +"mpt2sas" already in the name, so "mpt2sas_raid_template" became +"mpt2sas_raid_template_mpt2sas". (While the version in the mpt3sas +driver remained "mpt2sas_raid_template".) + +Signed-off-by: Jim Somerville +--- + drivers/scsi/Kconfig | 1 + + drivers/scsi/Makefile | 1 + + drivers/scsi/mpt2sas/Kconfig | 61 + + drivers/scsi/mpt2sas/Makefile | 12 + + drivers/scsi/mpt2sas/mpi/mpi2.h | 1243 ++++ + drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h | 3493 ++++++++++ + drivers/scsi/mpt2sas/mpi/mpi2_init.h | 581 ++ + drivers/scsi/mpt2sas/mpi/mpi2_ioc.h | 1860 +++++ + drivers/scsi/mpt2sas/mpi/mpi2_raid.h | 355 + + drivers/scsi/mpt2sas/mpi/mpi2_sas.h | 303 + + drivers/scsi/mpt2sas/mpi/mpi2_tool.h | 483 ++ + drivers/scsi/mpt2sas/mpi/mpi2_type.h | 57 + + drivers/scsi/mpt2sas/mpt3sas_base.c | 5713 ++++++++++++++++ + drivers/scsi/mpt2sas/mpt3sas_base.h | 1462 ++++ + drivers/scsi/mpt2sas/mpt3sas_config.c | 1716 +++++ + drivers/scsi/mpt2sas/mpt3sas_ctl.c | 3483 ++++++++++ + drivers/scsi/mpt2sas/mpt3sas_ctl.h | 423 ++ + drivers/scsi/mpt2sas/mpt3sas_debug.h | 206 + + drivers/scsi/mpt2sas/mpt3sas_scsih.c | 9356 ++++++++++++++++++++++++++ + drivers/scsi/mpt2sas/mpt3sas_transport.c | 2138 ++++++ + drivers/scsi/mpt2sas/mpt3sas_trigger_diag.c | 434 ++ + drivers/scsi/mpt2sas/mpt3sas_trigger_diag.h | 194 + + drivers/scsi/mpt2sas/mpt3sas_warpdrive.c | 344 + + drivers/scsi/mpt2sas/wrapper_mpt3sas_scsih.c | 4 + + drivers/scsi/mpt3sas/Kconfig | 20 - + drivers/scsi/mpt3sas/Makefile | 9 - + 26 files changed, 33923 insertions(+), 29 deletions(-) + create mode 100644 drivers/scsi/mpt2sas/Kconfig + create mode 100644 drivers/scsi/mpt2sas/Makefile + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2.h + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2_init.h + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2_ioc.h + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2_raid.h + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2_sas.h + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2_tool.h + create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2_type.h + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_base.c + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_base.h + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_config.c + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_ctl.c + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_ctl.h + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_debug.h + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_scsih.c + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_transport.c + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_trigger_diag.c + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_trigger_diag.h + create mode 100644 drivers/scsi/mpt2sas/mpt3sas_warpdrive.c + create mode 100644 drivers/scsi/mpt2sas/wrapper_mpt3sas_scsih.c + +diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig +index 4cb0e23..4d79408 100644 +--- a/drivers/scsi/Kconfig ++++ b/drivers/scsi/Kconfig +@@ -599,6 +599,7 @@ config SCSI_ARCMSR + module will be called arcmsr (modprobe arcmsr). + + source "drivers/scsi/megaraid/Kconfig.megaraid" ++source "drivers/scsi/mpt2sas/Kconfig" + source "drivers/scsi/mpt3sas/Kconfig" + source "drivers/scsi/smartpqi/Kconfig" + source "drivers/scsi/ufs/Kconfig" +diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile +index 95aed68..1a08fca 100644 +--- a/drivers/scsi/Makefile ++++ b/drivers/scsi/Makefile +@@ -109,6 +109,7 @@ obj-$(CONFIG_CXLFLASH) += cxlflash/ + obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o + obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/ + obj-$(CONFIG_MEGARAID_SAS) += megaraid/ ++obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas/ + obj-$(CONFIG_SCSI_MPT3SAS) += mpt3sas/ + obj-$(CONFIG_SCSI_UFSHCD) += ufs/ + obj-$(CONFIG_SCSI_ACARD) += atp870u.o +diff --git a/drivers/scsi/mpt2sas/Kconfig b/drivers/scsi/mpt2sas/Kconfig +new file mode 100644 +index 0000000..a53ee73 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/Kconfig +@@ -0,0 +1,61 @@ ++# ++# Kernel configuration file for the MPT2SAS ++# ++# This code is based on drivers/scsi/mpt3sas/Kconfig ++# Copyright (C) 2012-2014 LSI Corporation ++# (mailto:DL-MPTFusionLinux@lsi.com) ++ ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation; either version 2 ++# of the License, or (at your option) any later version. ++ ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++ ++# NO WARRANTY ++# THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++# LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++# solely responsible for determining the appropriateness of using and ++# distributing the Program and assumes all risks associated with its ++# exercise of rights under this Agreement, including but not limited to ++# the risks and costs of program errors, damage to or loss of data, ++# programs or equipment, and unavailability or interruption of operations. ++ ++# DISCLAIMER OF LIABILITY ++# NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++# DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++# USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++# HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++# USA. ++ ++config SCSI_MPT2SAS ++ tristate "LSI MPT Fusion SAS 2.0 Device Driver" ++ depends on PCI && SCSI ++ select SCSI_SAS_ATTRS ++ select RAID_ATTRS ++ ---help--- ++ This driver supports PCI-Express SAS 6Gb/s Host Adapters. ++ ++config SCSI_MPT2SAS_MAX_SGE ++ int "LSI MPT Fusion SAS 2.0 Max number of SG Entries (16 - 256)" ++ depends on PCI && SCSI && SCSI_MPT2SAS ++ default "128" ++ range 16 256 ++ ---help--- ++ This option allows you to specify the maximum number of scatter- ++ gather entries per I/O. The driver default is 128, which matches ++ MAX_PHYS_SEGMENTS in most kernels. However in SuSE kernels this ++ can be 256. However, it may decreased down to 16. Decreasing this ++ parameter will reduce memory requirements on a per controller instance. +diff --git a/drivers/scsi/mpt2sas/Makefile b/drivers/scsi/mpt2sas/Makefile +new file mode 100644 +index 0000000..5706ea4 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/Makefile +@@ -0,0 +1,12 @@ ++# mpt2-3sas makefile ++ ++obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas.o ++ ++obj-m += mpt2sas.o ++mpt2sas-y += mpt3sas_base.o \ ++ mpt3sas_config.o \ ++ wrapper_mpt3sas_scsih.o \ ++ mpt3sas_transport.o \ ++ mpt3sas_ctl.o \ ++ mpt3sas_trigger_diag.o \ ++ mpt3sas_warpdrive.o +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2.h b/drivers/scsi/mpt2sas/mpi/mpi2.h +new file mode 100644 +index 0000000..a9a659f +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2.h +@@ -0,0 +1,1243 @@ ++/* ++ * Copyright 2000-2015 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2.h ++ * Title: MPI Message independent structures and definitions ++ * including System Interface Register Set and ++ * scatter/gather formats. ++ * Creation Date: June 21, 2006 ++ * ++ * mpi2.h Version: 02.00.42 ++ * ++ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 ++ * prefix are for use only on MPI v2.5 products, and must not be used ++ * with MPI v2.0 products. Unless otherwise noted, names beginning with ++ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products. ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 06-04-07 02.00.01 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 06-26-07 02.00.02 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 08-31-07 02.00.03 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Moved ReplyPostHostIndex register to offset 0x6C of the ++ * MPI2_SYSTEM_INTERFACE_REGS and modified the define for ++ * MPI2_REPLY_POST_HOST_INDEX_OFFSET. ++ * Added union of request descriptors. ++ * Added union of reply descriptors. ++ * 10-31-07 02.00.04 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added define for MPI2_VERSION_02_00. ++ * Fixed the size of the FunctionDependent5 field in the ++ * MPI2_DEFAULT_REPLY structure. ++ * 12-18-07 02.00.05 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Removed the MPI-defined Fault Codes and extended the ++ * product specific codes up to 0xEFFF. ++ * Added a sixth key value for the WriteSequence register ++ * and changed the flush value to 0x0. ++ * Added message function codes for Diagnostic Buffer Post ++ * and Diagnsotic Release. ++ * New IOCStatus define: MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED ++ * Moved MPI2_VERSION_UNION from mpi2_ioc.h. ++ * 02-29-08 02.00.06 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 03-03-08 02.00.07 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 05-21-08 02.00.08 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added #defines for marking a reply descriptor as unused. ++ * 06-27-08 02.00.09 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 10-02-08 02.00.10 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Moved LUN field defines from mpi2_init.h. ++ * 01-19-09 02.00.11 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 05-06-09 02.00.12 Bumped MPI2_HEADER_VERSION_UNIT. ++ * In all request and reply descriptors, replaced VF_ID ++ * field with MSIxIndex field. ++ * Removed DevHandle field from ++ * MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR and made those ++ * bytes reserved. ++ * Added RAID Accelerator functionality. ++ * 07-30-09 02.00.13 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 10-28-09 02.00.14 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added MSI-x index mask and shift for Reply Post Host ++ * Index register. ++ * Added function code for Host Based Discovery Action. ++ * 02-10-10 02.00.15 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added define for MPI2_FUNCTION_PWR_MGMT_CONTROL. ++ * Added defines for product-specific range of message ++ * function codes, 0xF0 to 0xFF. ++ * 05-12-10 02.00.16 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added alternative defines for the SGE Direction bit. ++ * 08-11-10 02.00.17 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 11-10-10 02.00.18 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR define. ++ * 02-23-11 02.00.19 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added MPI2_FUNCTION_SEND_HOST_MESSAGE. ++ * 03-09-11 02.00.20 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 05-25-11 02.00.21 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 08-24-11 02.00.22 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 11-18-11 02.00.23 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Incorporating additions for MPI v2.5. ++ * 02-06-12 02.00.24 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 03-29-12 02.00.25 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added Hard Reset delay timings. ++ * 07-10-12 02.00.26 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 07-26-12 02.00.27 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 11-27-12 02.00.28 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 12-20-12 02.00.29 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET. ++ * 04-09-13 02.00.30 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 04-17-13 02.00.31 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 08-19-13 02.00.32 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 12-05-13 02.00.33 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 01-08-14 02.00.34 Bumped MPI2_HEADER_VERSION_UNIT ++ * 06-13-14 02.00.35 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 11-18-14 02.00.36 Updated copyright information. ++ * Bumped MPI2_HEADER_VERSION_UNIT. ++ * 03-16-15 02.00.37 Bumped MPI2_HEADER_VERSION_UNIT. ++ * Added Scratchpad registers to ++ * MPI2_SYSTEM_INTERFACE_REGS. ++ * Added MPI2_DIAG_SBR_RELOAD. ++ * 03-19-15 02.00.38 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 05-25-15 02.00.39 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 08-25-15 02.00.40 Bumped MPI2_HEADER_VERSION_UNIT. ++ * 12-15-15 02.00.41 Bumped MPI_HEADER_VERSION_UNIT ++ * 01-01-16 02.00.42 Bumped MPI_HEADER_VERSION_UNIT ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_H ++#define MPI2_H ++ ++/***************************************************************************** ++* ++* MPI Version Definitions ++* ++*****************************************************************************/ ++ ++#define MPI2_VERSION_MAJOR_MASK (0xFF00) ++#define MPI2_VERSION_MAJOR_SHIFT (8) ++#define MPI2_VERSION_MINOR_MASK (0x00FF) ++#define MPI2_VERSION_MINOR_SHIFT (0) ++ ++/*major version for all MPI v2.x */ ++#define MPI2_VERSION_MAJOR (0x02) ++ ++/*minor version for MPI v2.0 compatible products */ ++#define MPI2_VERSION_MINOR (0x00) ++#define MPI2_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \ ++ MPI2_VERSION_MINOR) ++#define MPI2_VERSION_02_00 (0x0200) ++ ++/*minor version for MPI v2.5 compatible products */ ++#define MPI25_VERSION_MINOR (0x05) ++#define MPI25_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \ ++ MPI25_VERSION_MINOR) ++#define MPI2_VERSION_02_05 (0x0205) ++ ++/*minor version for MPI v2.6 compatible products */ ++#define MPI26_VERSION_MINOR (0x06) ++#define MPI26_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \ ++ MPI26_VERSION_MINOR) ++#define MPI2_VERSION_02_06 (0x0206) ++ ++/*Unit and Dev versioning for this MPI header set */ ++#define MPI2_HEADER_VERSION_UNIT (0x2A) ++#define MPI2_HEADER_VERSION_DEV (0x00) ++#define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) ++#define MPI2_HEADER_VERSION_UNIT_SHIFT (8) ++#define MPI2_HEADER_VERSION_DEV_MASK (0x00FF) ++#define MPI2_HEADER_VERSION_DEV_SHIFT (0) ++#define MPI2_HEADER_VERSION ((MPI2_HEADER_VERSION_UNIT << 8) | \ ++ MPI2_HEADER_VERSION_DEV) ++ ++/***************************************************************************** ++* ++* IOC State Definitions ++* ++*****************************************************************************/ ++ ++#define MPI2_IOC_STATE_RESET (0x00000000) ++#define MPI2_IOC_STATE_READY (0x10000000) ++#define MPI2_IOC_STATE_OPERATIONAL (0x20000000) ++#define MPI2_IOC_STATE_FAULT (0x40000000) ++ ++#define MPI2_IOC_STATE_MASK (0xF0000000) ++#define MPI2_IOC_STATE_SHIFT (28) ++ ++/*Fault state range for prodcut specific codes */ ++#define MPI2_FAULT_PRODUCT_SPECIFIC_MIN (0x0000) ++#define MPI2_FAULT_PRODUCT_SPECIFIC_MAX (0xEFFF) ++ ++/***************************************************************************** ++* ++* System Interface Register Definitions ++* ++*****************************************************************************/ ++ ++typedef volatile struct _MPI2_SYSTEM_INTERFACE_REGS { ++ U32 Doorbell; /*0x00 */ ++ U32 WriteSequence; /*0x04 */ ++ U32 HostDiagnostic; /*0x08 */ ++ U32 Reserved1; /*0x0C */ ++ U32 DiagRWData; /*0x10 */ ++ U32 DiagRWAddressLow; /*0x14 */ ++ U32 DiagRWAddressHigh; /*0x18 */ ++ U32 Reserved2[5]; /*0x1C */ ++ U32 HostInterruptStatus; /*0x30 */ ++ U32 HostInterruptMask; /*0x34 */ ++ U32 DCRData; /*0x38 */ ++ U32 DCRAddress; /*0x3C */ ++ U32 Reserved3[2]; /*0x40 */ ++ U32 ReplyFreeHostIndex; /*0x48 */ ++ U32 Reserved4[8]; /*0x4C */ ++ U32 ReplyPostHostIndex; /*0x6C */ ++ U32 Reserved5; /*0x70 */ ++ U32 HCBSize; /*0x74 */ ++ U32 HCBAddressLow; /*0x78 */ ++ U32 HCBAddressHigh; /*0x7C */ ++ U32 Reserved6[12]; /*0x80 */ ++ U32 Scratchpad[4]; /*0xB0 */ ++ U32 RequestDescriptorPostLow; /*0xC0 */ ++ U32 RequestDescriptorPostHigh; /*0xC4 */ ++ U32 AtomicRequestDescriptorPost;/*0xC8 */ ++ U32 Reserved7[13]; /*0xCC */ ++} MPI2_SYSTEM_INTERFACE_REGS, ++ *PTR_MPI2_SYSTEM_INTERFACE_REGS, ++ Mpi2SystemInterfaceRegs_t, ++ *pMpi2SystemInterfaceRegs_t; ++ ++/* ++ *Defines for working with the Doorbell register. ++ */ ++#define MPI2_DOORBELL_OFFSET (0x00000000) ++ ++/*IOC --> System values */ ++#define MPI2_DOORBELL_USED (0x08000000) ++#define MPI2_DOORBELL_WHO_INIT_MASK (0x07000000) ++#define MPI2_DOORBELL_WHO_INIT_SHIFT (24) ++#define MPI2_DOORBELL_FAULT_CODE_MASK (0x0000FFFF) ++#define MPI2_DOORBELL_DATA_MASK (0x0000FFFF) ++ ++/*System --> IOC values */ ++#define MPI2_DOORBELL_FUNCTION_MASK (0xFF000000) ++#define MPI2_DOORBELL_FUNCTION_SHIFT (24) ++#define MPI2_DOORBELL_ADD_DWORDS_MASK (0x00FF0000) ++#define MPI2_DOORBELL_ADD_DWORDS_SHIFT (16) ++ ++/* ++ *Defines for the WriteSequence register ++ */ ++#define MPI2_WRITE_SEQUENCE_OFFSET (0x00000004) ++#define MPI2_WRSEQ_KEY_VALUE_MASK (0x0000000F) ++#define MPI2_WRSEQ_FLUSH_KEY_VALUE (0x0) ++#define MPI2_WRSEQ_1ST_KEY_VALUE (0xF) ++#define MPI2_WRSEQ_2ND_KEY_VALUE (0x4) ++#define MPI2_WRSEQ_3RD_KEY_VALUE (0xB) ++#define MPI2_WRSEQ_4TH_KEY_VALUE (0x2) ++#define MPI2_WRSEQ_5TH_KEY_VALUE (0x7) ++#define MPI2_WRSEQ_6TH_KEY_VALUE (0xD) ++ ++/* ++ *Defines for the HostDiagnostic register ++ */ ++#define MPI2_HOST_DIAGNOSTIC_OFFSET (0x00000008) ++ ++#define MPI2_DIAG_SBR_RELOAD (0x00002000) ++ ++#define MPI2_DIAG_BOOT_DEVICE_SELECT_MASK (0x00001800) ++#define MPI2_DIAG_BOOT_DEVICE_SELECT_DEFAULT (0x00000000) ++#define MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW (0x00000800) ++ ++#define MPI2_DIAG_CLEAR_FLASH_BAD_SIG (0x00000400) ++#define MPI2_DIAG_FORCE_HCB_ON_RESET (0x00000200) ++#define MPI2_DIAG_HCB_MODE (0x00000100) ++#define MPI2_DIAG_DIAG_WRITE_ENABLE (0x00000080) ++#define MPI2_DIAG_FLASH_BAD_SIG (0x00000040) ++#define MPI2_DIAG_RESET_HISTORY (0x00000020) ++#define MPI2_DIAG_DIAG_RW_ENABLE (0x00000010) ++#define MPI2_DIAG_RESET_ADAPTER (0x00000004) ++#define MPI2_DIAG_HOLD_IOC_RESET (0x00000002) ++ ++/* ++ *Offsets for DiagRWData and address ++ */ ++#define MPI2_DIAG_RW_DATA_OFFSET (0x00000010) ++#define MPI2_DIAG_RW_ADDRESS_LOW_OFFSET (0x00000014) ++#define MPI2_DIAG_RW_ADDRESS_HIGH_OFFSET (0x00000018) ++ ++/* ++ *Defines for the HostInterruptStatus register ++ */ ++#define MPI2_HOST_INTERRUPT_STATUS_OFFSET (0x00000030) ++#define MPI2_HIS_SYS2IOC_DB_STATUS (0x80000000) ++#define MPI2_HIS_IOP_DOORBELL_STATUS MPI2_HIS_SYS2IOC_DB_STATUS ++#define MPI2_HIS_RESET_IRQ_STATUS (0x40000000) ++#define MPI2_HIS_REPLY_DESCRIPTOR_INTERRUPT (0x00000008) ++#define MPI2_HIS_IOC2SYS_DB_STATUS (0x00000001) ++#define MPI2_HIS_DOORBELL_INTERRUPT MPI2_HIS_IOC2SYS_DB_STATUS ++ ++/* ++ *Defines for the HostInterruptMask register ++ */ ++#define MPI2_HOST_INTERRUPT_MASK_OFFSET (0x00000034) ++#define MPI2_HIM_RESET_IRQ_MASK (0x40000000) ++#define MPI2_HIM_REPLY_INT_MASK (0x00000008) ++#define MPI2_HIM_RIM MPI2_HIM_REPLY_INT_MASK ++#define MPI2_HIM_IOC2SYS_DB_MASK (0x00000001) ++#define MPI2_HIM_DIM MPI2_HIM_IOC2SYS_DB_MASK ++ ++/* ++ *Offsets for DCRData and address ++ */ ++#define MPI2_DCR_DATA_OFFSET (0x00000038) ++#define MPI2_DCR_ADDRESS_OFFSET (0x0000003C) ++ ++/* ++ *Offset for the Reply Free Queue ++ */ ++#define MPI2_REPLY_FREE_HOST_INDEX_OFFSET (0x00000048) ++ ++/* ++ *Defines for the Reply Descriptor Post Queue ++ */ ++#define MPI2_REPLY_POST_HOST_INDEX_OFFSET (0x0000006C) ++#define MPI2_REPLY_POST_HOST_INDEX_MASK (0x00FFFFFF) ++#define MPI2_RPHI_MSIX_INDEX_MASK (0xFF000000) ++#define MPI2_RPHI_MSIX_INDEX_SHIFT (24) ++#define MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET (0x0000030C) /*MPI v2.5 only*/ ++ ++ ++/* ++ *Defines for the HCBSize and address ++ */ ++#define MPI2_HCB_SIZE_OFFSET (0x00000074) ++#define MPI2_HCB_SIZE_SIZE_MASK (0xFFFFF000) ++#define MPI2_HCB_SIZE_HCB_ENABLE (0x00000001) ++ ++#define MPI2_HCB_ADDRESS_LOW_OFFSET (0x00000078) ++#define MPI2_HCB_ADDRESS_HIGH_OFFSET (0x0000007C) ++ ++/* ++ *Offsets for the Scratchpad registers ++ */ ++#define MPI26_SCRATCHPAD0_OFFSET (0x000000B0) ++#define MPI26_SCRATCHPAD1_OFFSET (0x000000B4) ++#define MPI26_SCRATCHPAD2_OFFSET (0x000000B8) ++#define MPI26_SCRATCHPAD3_OFFSET (0x000000BC) ++ ++/* ++ *Offsets for the Request Descriptor Post Queue ++ */ ++#define MPI2_REQUEST_DESCRIPTOR_POST_LOW_OFFSET (0x000000C0) ++#define MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET (0x000000C4) ++#define MPI26_ATOMIC_REQUEST_DESCRIPTOR_POST_OFFSET (0x000000C8) ++ ++/*Hard Reset delay timings */ ++#define MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC (50000) ++#define MPI2_HARD_RESET_PCIE_RESET_READ_WINDOW_MICRO_SEC (255000) ++#define MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC (256000) ++ ++/***************************************************************************** ++* ++* Message Descriptors ++* ++*****************************************************************************/ ++ ++/*Request Descriptors */ ++ ++/*Default Request Descriptor */ ++typedef struct _MPI2_DEFAULT_REQUEST_DESCRIPTOR { ++ U8 RequestFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U16 LMID; /*0x04 */ ++ U16 DescriptorTypeDependent; /*0x06 */ ++} MPI2_DEFAULT_REQUEST_DESCRIPTOR, ++ *PTR_MPI2_DEFAULT_REQUEST_DESCRIPTOR, ++ Mpi2DefaultRequestDescriptor_t, ++ *pMpi2DefaultRequestDescriptor_t; ++ ++/*defines for the RequestFlags field */ ++#define MPI2_REQ_DESCRIPT_FLAGS_TYPE_MASK (0x1E) ++#define MPI2_REQ_DESCRIPT_FLAGS_TYPE_RSHIFT (1) ++#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO (0x00) ++#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET (0x02) ++#define MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY (0x06) ++#define MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE (0x08) ++#define MPI2_REQ_DESCRIPT_FLAGS_RAID_ACCELERATOR (0x0A) ++#define MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO (0x0C) ++ ++#define MPI2_REQ_DESCRIPT_FLAGS_IOC_FIFO_MARKER (0x01) ++ ++/*High Priority Request Descriptor */ ++typedef struct _MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR { ++ U8 RequestFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U16 LMID; /*0x04 */ ++ U16 Reserved1; /*0x06 */ ++} MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR, ++ *PTR_MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR, ++ Mpi2HighPriorityRequestDescriptor_t, ++ *pMpi2HighPriorityRequestDescriptor_t; ++ ++/*SCSI IO Request Descriptor */ ++typedef struct _MPI2_SCSI_IO_REQUEST_DESCRIPTOR { ++ U8 RequestFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U16 LMID; /*0x04 */ ++ U16 DevHandle; /*0x06 */ ++} MPI2_SCSI_IO_REQUEST_DESCRIPTOR, ++ *PTR_MPI2_SCSI_IO_REQUEST_DESCRIPTOR, ++ Mpi2SCSIIORequestDescriptor_t, ++ *pMpi2SCSIIORequestDescriptor_t; ++ ++/*SCSI Target Request Descriptor */ ++typedef struct _MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR { ++ U8 RequestFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U16 LMID; /*0x04 */ ++ U16 IoIndex; /*0x06 */ ++} MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR, ++ *PTR_MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR, ++ Mpi2SCSITargetRequestDescriptor_t, ++ *pMpi2SCSITargetRequestDescriptor_t; ++ ++/*RAID Accelerator Request Descriptor */ ++typedef struct _MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR { ++ U8 RequestFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U16 LMID; /*0x04 */ ++ U16 Reserved; /*0x06 */ ++} MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR, ++ *PTR_MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR, ++ Mpi2RAIDAcceleratorRequestDescriptor_t, ++ *pMpi2RAIDAcceleratorRequestDescriptor_t; ++ ++/*Fast Path SCSI IO Request Descriptor */ ++typedef MPI2_SCSI_IO_REQUEST_DESCRIPTOR ++ MPI25_FP_SCSI_IO_REQUEST_DESCRIPTOR, ++ *PTR_MPI25_FP_SCSI_IO_REQUEST_DESCRIPTOR, ++ Mpi25FastPathSCSIIORequestDescriptor_t, ++ *pMpi25FastPathSCSIIORequestDescriptor_t; ++ ++/*union of Request Descriptors */ ++typedef union _MPI2_REQUEST_DESCRIPTOR_UNION { ++ MPI2_DEFAULT_REQUEST_DESCRIPTOR Default; ++ MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR HighPriority; ++ MPI2_SCSI_IO_REQUEST_DESCRIPTOR SCSIIO; ++ MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR SCSITarget; ++ MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR RAIDAccelerator; ++ MPI25_FP_SCSI_IO_REQUEST_DESCRIPTOR FastPathSCSIIO; ++ U64 Words; ++} MPI2_REQUEST_DESCRIPTOR_UNION, ++ *PTR_MPI2_REQUEST_DESCRIPTOR_UNION, ++ Mpi2RequestDescriptorUnion_t, ++ *pMpi2RequestDescriptorUnion_t; ++ ++/*Atomic Request Descriptors */ ++ ++/* ++ * All Atomic Request Descriptors have the same format, so the following ++ * structure is used for all Atomic Request Descriptors: ++ * Atomic Default Request Descriptor ++ * Atomic High Priority Request Descriptor ++ * Atomic SCSI IO Request Descriptor ++ * Atomic SCSI Target Request Descriptor ++ * Atomic RAID Accelerator Request Descriptor ++ * Atomic Fast Path SCSI IO Request Descriptor ++ */ ++ ++/*Atomic Request Descriptor */ ++typedef struct _MPI26_ATOMIC_REQUEST_DESCRIPTOR { ++ U8 RequestFlags; /* 0x00 */ ++ U8 MSIxIndex; /* 0x01 */ ++ U16 SMID; /* 0x02 */ ++} MPI26_ATOMIC_REQUEST_DESCRIPTOR, ++ *PTR_MPI26_ATOMIC_REQUEST_DESCRIPTOR, ++ Mpi26AtomicRequestDescriptor_t, ++ *pMpi26AtomicRequestDescriptor_t; ++ ++/*for the RequestFlags field, use the same ++ *defines as MPI2_DEFAULT_REQUEST_DESCRIPTOR ++ */ ++ ++/*Reply Descriptors */ ++ ++/*Default Reply Descriptor */ ++typedef struct _MPI2_DEFAULT_REPLY_DESCRIPTOR { ++ U8 ReplyFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 DescriptorTypeDependent1; /*0x02 */ ++ U32 DescriptorTypeDependent2; /*0x04 */ ++} MPI2_DEFAULT_REPLY_DESCRIPTOR, ++ *PTR_MPI2_DEFAULT_REPLY_DESCRIPTOR, ++ Mpi2DefaultReplyDescriptor_t, ++ *pMpi2DefaultReplyDescriptor_t; ++ ++/*defines for the ReplyFlags field */ ++#define MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK (0x0F) ++#define MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS (0x00) ++#define MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY (0x01) ++#define MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS (0x02) ++#define MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER (0x03) ++#define MPI2_RPY_DESCRIPT_FLAGS_RAID_ACCELERATOR_SUCCESS (0x05) ++#define MPI25_RPY_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO_SUCCESS (0x06) ++#define MPI2_RPY_DESCRIPT_FLAGS_UNUSED (0x0F) ++ ++/*values for marking a reply descriptor as unused */ ++#define MPI2_RPY_DESCRIPT_UNUSED_WORD0_MARK (0xFFFFFFFF) ++#define MPI2_RPY_DESCRIPT_UNUSED_WORD1_MARK (0xFFFFFFFF) ++ ++/*Address Reply Descriptor */ ++typedef struct _MPI2_ADDRESS_REPLY_DESCRIPTOR { ++ U8 ReplyFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U32 ReplyFrameAddress; /*0x04 */ ++} MPI2_ADDRESS_REPLY_DESCRIPTOR, ++ *PTR_MPI2_ADDRESS_REPLY_DESCRIPTOR, ++ Mpi2AddressReplyDescriptor_t, ++ *pMpi2AddressReplyDescriptor_t; ++ ++#define MPI2_ADDRESS_REPLY_SMID_INVALID (0x00) ++ ++/*SCSI IO Success Reply Descriptor */ ++typedef struct _MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR { ++ U8 ReplyFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U16 TaskTag; /*0x04 */ ++ U16 Reserved1; /*0x06 */ ++} MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, ++ *PTR_MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, ++ Mpi2SCSIIOSuccessReplyDescriptor_t, ++ *pMpi2SCSIIOSuccessReplyDescriptor_t; ++ ++/*TargetAssist Success Reply Descriptor */ ++typedef struct _MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR { ++ U8 ReplyFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U8 SequenceNumber; /*0x04 */ ++ U8 Reserved1; /*0x05 */ ++ U16 IoIndex; /*0x06 */ ++} MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR, ++ *PTR_MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR, ++ Mpi2TargetAssistSuccessReplyDescriptor_t, ++ *pMpi2TargetAssistSuccessReplyDescriptor_t; ++ ++/*Target Command Buffer Reply Descriptor */ ++typedef struct _MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR { ++ U8 ReplyFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U8 VP_ID; /*0x02 */ ++ U8 Flags; /*0x03 */ ++ U16 InitiatorDevHandle; /*0x04 */ ++ U16 IoIndex; /*0x06 */ ++} MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR, ++ *PTR_MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR, ++ Mpi2TargetCommandBufferReplyDescriptor_t, ++ *pMpi2TargetCommandBufferReplyDescriptor_t; ++ ++/*defines for Flags field */ ++#define MPI2_RPY_DESCRIPT_TCB_FLAGS_PHYNUM_MASK (0x3F) ++ ++/*RAID Accelerator Success Reply Descriptor */ ++typedef struct _MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR { ++ U8 ReplyFlags; /*0x00 */ ++ U8 MSIxIndex; /*0x01 */ ++ U16 SMID; /*0x02 */ ++ U32 Reserved; /*0x04 */ ++} MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR, ++ *PTR_MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR, ++ Mpi2RAIDAcceleratorSuccessReplyDescriptor_t, ++ *pMpi2RAIDAcceleratorSuccessReplyDescriptor_t; ++ ++/*Fast Path SCSI IO Success Reply Descriptor */ ++typedef MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR ++ MPI25_FP_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, ++ *PTR_MPI25_FP_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, ++ Mpi25FastPathSCSIIOSuccessReplyDescriptor_t, ++ *pMpi25FastPathSCSIIOSuccessReplyDescriptor_t; ++ ++/*union of Reply Descriptors */ ++typedef union _MPI2_REPLY_DESCRIPTORS_UNION { ++ MPI2_DEFAULT_REPLY_DESCRIPTOR Default; ++ MPI2_ADDRESS_REPLY_DESCRIPTOR AddressReply; ++ MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR SCSIIOSuccess; ++ MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR TargetAssistSuccess; ++ MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer; ++ MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR RAIDAcceleratorSuccess; ++ MPI25_FP_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR FastPathSCSIIOSuccess; ++ U64 Words; ++} MPI2_REPLY_DESCRIPTORS_UNION, ++ *PTR_MPI2_REPLY_DESCRIPTORS_UNION, ++ Mpi2ReplyDescriptorsUnion_t, ++ *pMpi2ReplyDescriptorsUnion_t; ++ ++/***************************************************************************** ++* ++* Message Functions ++* ++*****************************************************************************/ ++ ++#define MPI2_FUNCTION_SCSI_IO_REQUEST (0x00) ++#define MPI2_FUNCTION_SCSI_TASK_MGMT (0x01) ++#define MPI2_FUNCTION_IOC_INIT (0x02) ++#define MPI2_FUNCTION_IOC_FACTS (0x03) ++#define MPI2_FUNCTION_CONFIG (0x04) ++#define MPI2_FUNCTION_PORT_FACTS (0x05) ++#define MPI2_FUNCTION_PORT_ENABLE (0x06) ++#define MPI2_FUNCTION_EVENT_NOTIFICATION (0x07) ++#define MPI2_FUNCTION_EVENT_ACK (0x08) ++#define MPI2_FUNCTION_FW_DOWNLOAD (0x09) ++#define MPI2_FUNCTION_TARGET_ASSIST (0x0B) ++#define MPI2_FUNCTION_TARGET_STATUS_SEND (0x0C) ++#define MPI2_FUNCTION_TARGET_MODE_ABORT (0x0D) ++#define MPI2_FUNCTION_FW_UPLOAD (0x12) ++#define MPI2_FUNCTION_RAID_ACTION (0x15) ++#define MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH (0x16) ++#define MPI2_FUNCTION_TOOLBOX (0x17) ++#define MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR (0x18) ++#define MPI2_FUNCTION_SMP_PASSTHROUGH (0x1A) ++#define MPI2_FUNCTION_SAS_IO_UNIT_CONTROL (0x1B) ++#define MPI2_FUNCTION_IO_UNIT_CONTROL (0x1B) ++#define MPI2_FUNCTION_SATA_PASSTHROUGH (0x1C) ++#define MPI2_FUNCTION_DIAG_BUFFER_POST (0x1D) ++#define MPI2_FUNCTION_DIAG_RELEASE (0x1E) ++#define MPI2_FUNCTION_TARGET_CMD_BUF_BASE_POST (0x24) ++#define MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST (0x25) ++#define MPI2_FUNCTION_RAID_ACCELERATOR (0x2C) ++#define MPI2_FUNCTION_HOST_BASED_DISCOVERY_ACTION (0x2F) ++#define MPI2_FUNCTION_PWR_MGMT_CONTROL (0x30) ++#define MPI2_FUNCTION_SEND_HOST_MESSAGE (0x31) ++#define MPI2_FUNCTION_MIN_PRODUCT_SPECIFIC (0xF0) ++#define MPI2_FUNCTION_MAX_PRODUCT_SPECIFIC (0xFF) ++ ++/*Doorbell functions */ ++#define MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40) ++#define MPI2_FUNCTION_HANDSHAKE (0x42) ++ ++/***************************************************************************** ++* ++* IOC Status Values ++* ++*****************************************************************************/ ++ ++/*mask for IOCStatus status value */ ++#define MPI2_IOCSTATUS_MASK (0x7FFF) ++ ++/**************************************************************************** ++* Common IOCStatus values for all replies ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_SUCCESS (0x0000) ++#define MPI2_IOCSTATUS_INVALID_FUNCTION (0x0001) ++#define MPI2_IOCSTATUS_BUSY (0x0002) ++#define MPI2_IOCSTATUS_INVALID_SGL (0x0003) ++#define MPI2_IOCSTATUS_INTERNAL_ERROR (0x0004) ++#define MPI2_IOCSTATUS_INVALID_VPID (0x0005) ++#define MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006) ++#define MPI2_IOCSTATUS_INVALID_FIELD (0x0007) ++#define MPI2_IOCSTATUS_INVALID_STATE (0x0008) ++#define MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED (0x0009) ++#define MPI2_IOCSTATUS_INSUFFICIENT_POWER (0x000A) ++ ++/**************************************************************************** ++* Config IOCStatus values ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020) ++#define MPI2_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021) ++#define MPI2_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022) ++#define MPI2_IOCSTATUS_CONFIG_INVALID_DATA (0x0023) ++#define MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024) ++#define MPI2_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025) ++ ++/**************************************************************************** ++* SCSI IO Reply ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040) ++#define MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE (0x0042) ++#define MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043) ++#define MPI2_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044) ++#define MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045) ++#define MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046) ++#define MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047) ++#define MPI2_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048) ++#define MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049) ++#define MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004A) ++#define MPI2_IOCSTATUS_SCSI_IOC_TERMINATED (0x004B) ++#define MPI2_IOCSTATUS_SCSI_EXT_TERMINATED (0x004C) ++ ++/**************************************************************************** ++* For use by SCSI Initiator and SCSI Target end-to-end data protection ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_EEDP_GUARD_ERROR (0x004D) ++#define MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR (0x004E) ++#define MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR (0x004F) ++ ++/**************************************************************************** ++* SCSI Target values ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX (0x0062) ++#define MPI2_IOCSTATUS_TARGET_ABORTED (0x0063) ++#define MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064) ++#define MPI2_IOCSTATUS_TARGET_NO_CONNECTION (0x0065) ++#define MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006A) ++#define MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR (0x006D) ++#define MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA (0x006E) ++#define MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT (0x006F) ++#define MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT (0x0070) ++#define MPI2_IOCSTATUS_TARGET_NAK_RECEIVED (0x0071) ++ ++/**************************************************************************** ++* Serial Attached SCSI values ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED (0x0090) ++#define MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN (0x0091) ++ ++/**************************************************************************** ++* Diagnostic Buffer Post / Diagnostic Release values ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00A0) ++ ++/**************************************************************************** ++* RAID Accelerator values ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_RAID_ACCEL_ERROR (0x00B0) ++ ++/**************************************************************************** ++* IOCStatus flag to indicate that log info is available ++****************************************************************************/ ++ ++#define MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE (0x8000) ++ ++/**************************************************************************** ++* IOCLogInfo Types ++****************************************************************************/ ++ ++#define MPI2_IOCLOGINFO_TYPE_MASK (0xF0000000) ++#define MPI2_IOCLOGINFO_TYPE_SHIFT (28) ++#define MPI2_IOCLOGINFO_TYPE_NONE (0x0) ++#define MPI2_IOCLOGINFO_TYPE_SCSI (0x1) ++#define MPI2_IOCLOGINFO_TYPE_FC (0x2) ++#define MPI2_IOCLOGINFO_TYPE_SAS (0x3) ++#define MPI2_IOCLOGINFO_TYPE_ISCSI (0x4) ++#define MPI2_IOCLOGINFO_LOG_DATA_MASK (0x0FFFFFFF) ++ ++/***************************************************************************** ++* ++* Standard Message Structures ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++*Request Message Header for all request messages ++****************************************************************************/ ++ ++typedef struct _MPI2_REQUEST_HEADER { ++ U16 FunctionDependent1; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 FunctionDependent2; /*0x04 */ ++ U8 FunctionDependent3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++} MPI2_REQUEST_HEADER, *PTR_MPI2_REQUEST_HEADER, ++ MPI2RequestHeader_t, *pMPI2RequestHeader_t; ++ ++/**************************************************************************** ++* Default Reply ++****************************************************************************/ ++ ++typedef struct _MPI2_DEFAULT_REPLY { ++ U16 FunctionDependent1; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 FunctionDependent2; /*0x04 */ ++ U8 FunctionDependent3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U16 FunctionDependent5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_DEFAULT_REPLY, *PTR_MPI2_DEFAULT_REPLY, ++ MPI2DefaultReply_t, *pMPI2DefaultReply_t; ++ ++/*common version structure/union used in messages and configuration pages */ ++ ++typedef struct _MPI2_VERSION_STRUCT { ++ U8 Dev; /*0x00 */ ++ U8 Unit; /*0x01 */ ++ U8 Minor; /*0x02 */ ++ U8 Major; /*0x03 */ ++} MPI2_VERSION_STRUCT; ++ ++typedef union _MPI2_VERSION_UNION { ++ MPI2_VERSION_STRUCT Struct; ++ U32 Word; ++} MPI2_VERSION_UNION; ++ ++/*LUN field defines, common to many structures */ ++#define MPI2_LUN_FIRST_LEVEL_ADDRESSING (0x0000FFFF) ++#define MPI2_LUN_SECOND_LEVEL_ADDRESSING (0xFFFF0000) ++#define MPI2_LUN_THIRD_LEVEL_ADDRESSING (0x0000FFFF) ++#define MPI2_LUN_FOURTH_LEVEL_ADDRESSING (0xFFFF0000) ++#define MPI2_LUN_LEVEL_1_WORD (0xFF00) ++#define MPI2_LUN_LEVEL_1_DWORD (0x0000FF00) ++ ++/***************************************************************************** ++* ++* Fusion-MPT MPI Scatter Gather Elements ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* MPI Simple Element structures ++****************************************************************************/ ++ ++typedef struct _MPI2_SGE_SIMPLE32 { ++ U32 FlagsLength; ++ U32 Address; ++} MPI2_SGE_SIMPLE32, *PTR_MPI2_SGE_SIMPLE32, ++ Mpi2SGESimple32_t, *pMpi2SGESimple32_t; ++ ++typedef struct _MPI2_SGE_SIMPLE64 { ++ U32 FlagsLength; ++ U64 Address; ++} MPI2_SGE_SIMPLE64, *PTR_MPI2_SGE_SIMPLE64, ++ Mpi2SGESimple64_t, *pMpi2SGESimple64_t; ++ ++typedef struct _MPI2_SGE_SIMPLE_UNION { ++ U32 FlagsLength; ++ union { ++ U32 Address32; ++ U64 Address64; ++ } u; ++} MPI2_SGE_SIMPLE_UNION, ++ *PTR_MPI2_SGE_SIMPLE_UNION, ++ Mpi2SGESimpleUnion_t, ++ *pMpi2SGESimpleUnion_t; ++ ++/**************************************************************************** ++* MPI Chain Element structures - for MPI v2.0 products only ++****************************************************************************/ ++ ++typedef struct _MPI2_SGE_CHAIN32 { ++ U16 Length; ++ U8 NextChainOffset; ++ U8 Flags; ++ U32 Address; ++} MPI2_SGE_CHAIN32, *PTR_MPI2_SGE_CHAIN32, ++ Mpi2SGEChain32_t, *pMpi2SGEChain32_t; ++ ++typedef struct _MPI2_SGE_CHAIN64 { ++ U16 Length; ++ U8 NextChainOffset; ++ U8 Flags; ++ U64 Address; ++} MPI2_SGE_CHAIN64, *PTR_MPI2_SGE_CHAIN64, ++ Mpi2SGEChain64_t, *pMpi2SGEChain64_t; ++ ++typedef struct _MPI2_SGE_CHAIN_UNION { ++ U16 Length; ++ U8 NextChainOffset; ++ U8 Flags; ++ union { ++ U32 Address32; ++ U64 Address64; ++ } u; ++} MPI2_SGE_CHAIN_UNION, ++ *PTR_MPI2_SGE_CHAIN_UNION, ++ Mpi2SGEChainUnion_t, ++ *pMpi2SGEChainUnion_t; ++ ++/**************************************************************************** ++* MPI Transaction Context Element structures - for MPI v2.0 products only ++****************************************************************************/ ++ ++typedef struct _MPI2_SGE_TRANSACTION32 { ++ U8 Reserved; ++ U8 ContextSize; ++ U8 DetailsLength; ++ U8 Flags; ++ U32 TransactionContext[1]; ++ U32 TransactionDetails[1]; ++} MPI2_SGE_TRANSACTION32, ++ *PTR_MPI2_SGE_TRANSACTION32, ++ Mpi2SGETransaction32_t, ++ *pMpi2SGETransaction32_t; ++ ++typedef struct _MPI2_SGE_TRANSACTION64 { ++ U8 Reserved; ++ U8 ContextSize; ++ U8 DetailsLength; ++ U8 Flags; ++ U32 TransactionContext[2]; ++ U32 TransactionDetails[1]; ++} MPI2_SGE_TRANSACTION64, ++ *PTR_MPI2_SGE_TRANSACTION64, ++ Mpi2SGETransaction64_t, ++ *pMpi2SGETransaction64_t; ++ ++typedef struct _MPI2_SGE_TRANSACTION96 { ++ U8 Reserved; ++ U8 ContextSize; ++ U8 DetailsLength; ++ U8 Flags; ++ U32 TransactionContext[3]; ++ U32 TransactionDetails[1]; ++} MPI2_SGE_TRANSACTION96, *PTR_MPI2_SGE_TRANSACTION96, ++ Mpi2SGETransaction96_t, *pMpi2SGETransaction96_t; ++ ++typedef struct _MPI2_SGE_TRANSACTION128 { ++ U8 Reserved; ++ U8 ContextSize; ++ U8 DetailsLength; ++ U8 Flags; ++ U32 TransactionContext[4]; ++ U32 TransactionDetails[1]; ++} MPI2_SGE_TRANSACTION128, *PTR_MPI2_SGE_TRANSACTION128, ++ Mpi2SGETransaction_t128, *pMpi2SGETransaction_t128; ++ ++typedef struct _MPI2_SGE_TRANSACTION_UNION { ++ U8 Reserved; ++ U8 ContextSize; ++ U8 DetailsLength; ++ U8 Flags; ++ union { ++ U32 TransactionContext32[1]; ++ U32 TransactionContext64[2]; ++ U32 TransactionContext96[3]; ++ U32 TransactionContext128[4]; ++ } u; ++ U32 TransactionDetails[1]; ++} MPI2_SGE_TRANSACTION_UNION, ++ *PTR_MPI2_SGE_TRANSACTION_UNION, ++ Mpi2SGETransactionUnion_t, ++ *pMpi2SGETransactionUnion_t; ++ ++/**************************************************************************** ++* MPI SGE union for IO SGL's - for MPI v2.0 products only ++****************************************************************************/ ++ ++typedef struct _MPI2_MPI_SGE_IO_UNION { ++ union { ++ MPI2_SGE_SIMPLE_UNION Simple; ++ MPI2_SGE_CHAIN_UNION Chain; ++ } u; ++} MPI2_MPI_SGE_IO_UNION, *PTR_MPI2_MPI_SGE_IO_UNION, ++ Mpi2MpiSGEIOUnion_t, *pMpi2MpiSGEIOUnion_t; ++ ++/**************************************************************************** ++* MPI SGE union for SGL's with Simple and Transaction elements - for MPI v2.0 products only ++****************************************************************************/ ++ ++typedef struct _MPI2_SGE_TRANS_SIMPLE_UNION { ++ union { ++ MPI2_SGE_SIMPLE_UNION Simple; ++ MPI2_SGE_TRANSACTION_UNION Transaction; ++ } u; ++} MPI2_SGE_TRANS_SIMPLE_UNION, ++ *PTR_MPI2_SGE_TRANS_SIMPLE_UNION, ++ Mpi2SGETransSimpleUnion_t, ++ *pMpi2SGETransSimpleUnion_t; ++ ++/**************************************************************************** ++* All MPI SGE types union ++****************************************************************************/ ++ ++typedef struct _MPI2_MPI_SGE_UNION { ++ union { ++ MPI2_SGE_SIMPLE_UNION Simple; ++ MPI2_SGE_CHAIN_UNION Chain; ++ MPI2_SGE_TRANSACTION_UNION Transaction; ++ } u; ++} MPI2_MPI_SGE_UNION, *PTR_MPI2_MPI_SGE_UNION, ++ Mpi2MpiSgeUnion_t, *pMpi2MpiSgeUnion_t; ++ ++/**************************************************************************** ++* MPI SGE field definition and masks ++****************************************************************************/ ++ ++/*Flags field bit definitions */ ++ ++#define MPI2_SGE_FLAGS_LAST_ELEMENT (0x80) ++#define MPI2_SGE_FLAGS_END_OF_BUFFER (0x40) ++#define MPI2_SGE_FLAGS_ELEMENT_TYPE_MASK (0x30) ++#define MPI2_SGE_FLAGS_LOCAL_ADDRESS (0x08) ++#define MPI2_SGE_FLAGS_DIRECTION (0x04) ++#define MPI2_SGE_FLAGS_ADDRESS_SIZE (0x02) ++#define MPI2_SGE_FLAGS_END_OF_LIST (0x01) ++ ++#define MPI2_SGE_FLAGS_SHIFT (24) ++ ++#define MPI2_SGE_LENGTH_MASK (0x00FFFFFF) ++#define MPI2_SGE_CHAIN_LENGTH_MASK (0x0000FFFF) ++ ++/*Element Type */ ++ ++#define MPI2_SGE_FLAGS_TRANSACTION_ELEMENT (0x00) ++#define MPI2_SGE_FLAGS_SIMPLE_ELEMENT (0x10) ++#define MPI2_SGE_FLAGS_CHAIN_ELEMENT (0x30) ++#define MPI2_SGE_FLAGS_ELEMENT_MASK (0x30) ++ ++/*Address location */ ++ ++#define MPI2_SGE_FLAGS_SYSTEM_ADDRESS (0x00) ++ ++/*Direction */ ++ ++#define MPI2_SGE_FLAGS_IOC_TO_HOST (0x00) ++#define MPI2_SGE_FLAGS_HOST_TO_IOC (0x04) ++ ++#define MPI2_SGE_FLAGS_DEST (MPI2_SGE_FLAGS_IOC_TO_HOST) ++#define MPI2_SGE_FLAGS_SOURCE (MPI2_SGE_FLAGS_HOST_TO_IOC) ++ ++/*Address Size */ ++ ++#define MPI2_SGE_FLAGS_32_BIT_ADDRESSING (0x00) ++#define MPI2_SGE_FLAGS_64_BIT_ADDRESSING (0x02) ++ ++/*Context Size */ ++ ++#define MPI2_SGE_FLAGS_32_BIT_CONTEXT (0x00) ++#define MPI2_SGE_FLAGS_64_BIT_CONTEXT (0x02) ++#define MPI2_SGE_FLAGS_96_BIT_CONTEXT (0x04) ++#define MPI2_SGE_FLAGS_128_BIT_CONTEXT (0x06) ++ ++#define MPI2_SGE_CHAIN_OFFSET_MASK (0x00FF0000) ++#define MPI2_SGE_CHAIN_OFFSET_SHIFT (16) ++ ++/**************************************************************************** ++* MPI SGE operation Macros ++****************************************************************************/ ++ ++/*SIMPLE FlagsLength manipulations... */ ++#define MPI2_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_SGE_FLAGS_SHIFT) ++#define MPI2_SGE_GET_FLAGS(f) (((f) & ~MPI2_SGE_LENGTH_MASK) >> \ ++ MPI2_SGE_FLAGS_SHIFT) ++#define MPI2_SGE_LENGTH(f) ((f) & MPI2_SGE_LENGTH_MASK) ++#define MPI2_SGE_CHAIN_LENGTH(f) ((f) & MPI2_SGE_CHAIN_LENGTH_MASK) ++ ++#define MPI2_SGE_SET_FLAGS_LENGTH(f, l) (MPI2_SGE_SET_FLAGS(f) | \ ++ MPI2_SGE_LENGTH(l)) ++ ++#define MPI2_pSGE_GET_FLAGS(psg) MPI2_SGE_GET_FLAGS((psg)->FlagsLength) ++#define MPI2_pSGE_GET_LENGTH(psg) MPI2_SGE_LENGTH((psg)->FlagsLength) ++#define MPI2_pSGE_SET_FLAGS_LENGTH(psg, f, l) ((psg)->FlagsLength = \ ++ MPI2_SGE_SET_FLAGS_LENGTH(f, l)) ++ ++/*CAUTION - The following are READ-MODIFY-WRITE! */ ++#define MPI2_pSGE_SET_FLAGS(psg, f) ((psg)->FlagsLength |= \ ++ MPI2_SGE_SET_FLAGS(f)) ++#define MPI2_pSGE_SET_LENGTH(psg, l) ((psg)->FlagsLength |= \ ++ MPI2_SGE_LENGTH(l)) ++ ++#define MPI2_GET_CHAIN_OFFSET(x) ((x & MPI2_SGE_CHAIN_OFFSET_MASK) >> \ ++ MPI2_SGE_CHAIN_OFFSET_SHIFT) ++ ++/***************************************************************************** ++* ++* Fusion-MPT IEEE Scatter Gather Elements ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* IEEE Simple Element structures ++****************************************************************************/ ++ ++/*MPI2_IEEE_SGE_SIMPLE32 is for MPI v2.0 products only */ ++typedef struct _MPI2_IEEE_SGE_SIMPLE32 { ++ U32 Address; ++ U32 FlagsLength; ++} MPI2_IEEE_SGE_SIMPLE32, *PTR_MPI2_IEEE_SGE_SIMPLE32, ++ Mpi2IeeeSgeSimple32_t, *pMpi2IeeeSgeSimple32_t; ++ ++typedef struct _MPI2_IEEE_SGE_SIMPLE64 { ++ U64 Address; ++ U32 Length; ++ U16 Reserved1; ++ U8 Reserved2; ++ U8 Flags; ++} MPI2_IEEE_SGE_SIMPLE64, *PTR_MPI2_IEEE_SGE_SIMPLE64, ++ Mpi2IeeeSgeSimple64_t, *pMpi2IeeeSgeSimple64_t; ++ ++typedef union _MPI2_IEEE_SGE_SIMPLE_UNION { ++ MPI2_IEEE_SGE_SIMPLE32 Simple32; ++ MPI2_IEEE_SGE_SIMPLE64 Simple64; ++} MPI2_IEEE_SGE_SIMPLE_UNION, ++ *PTR_MPI2_IEEE_SGE_SIMPLE_UNION, ++ Mpi2IeeeSgeSimpleUnion_t, ++ *pMpi2IeeeSgeSimpleUnion_t; ++ ++/**************************************************************************** ++* IEEE Chain Element structures ++****************************************************************************/ ++ ++/*MPI2_IEEE_SGE_CHAIN32 is for MPI v2.0 products only */ ++typedef MPI2_IEEE_SGE_SIMPLE32 MPI2_IEEE_SGE_CHAIN32; ++ ++/*MPI2_IEEE_SGE_CHAIN64 is for MPI v2.0 products only */ ++typedef MPI2_IEEE_SGE_SIMPLE64 MPI2_IEEE_SGE_CHAIN64; ++ ++typedef union _MPI2_IEEE_SGE_CHAIN_UNION { ++ MPI2_IEEE_SGE_CHAIN32 Chain32; ++ MPI2_IEEE_SGE_CHAIN64 Chain64; ++} MPI2_IEEE_SGE_CHAIN_UNION, ++ *PTR_MPI2_IEEE_SGE_CHAIN_UNION, ++ Mpi2IeeeSgeChainUnion_t, ++ *pMpi2IeeeSgeChainUnion_t; ++ ++/*MPI25_IEEE_SGE_CHAIN64 is for MPI v2.5 and later */ ++typedef struct _MPI25_IEEE_SGE_CHAIN64 { ++ U64 Address; ++ U32 Length; ++ U16 Reserved1; ++ U8 NextChainOffset; ++ U8 Flags; ++} MPI25_IEEE_SGE_CHAIN64, ++ *PTR_MPI25_IEEE_SGE_CHAIN64, ++ Mpi25IeeeSgeChain64_t, ++ *pMpi25IeeeSgeChain64_t; ++ ++/**************************************************************************** ++* All IEEE SGE types union ++****************************************************************************/ ++ ++/*MPI2_IEEE_SGE_UNION is for MPI v2.0 products only */ ++typedef struct _MPI2_IEEE_SGE_UNION { ++ union { ++ MPI2_IEEE_SGE_SIMPLE_UNION Simple; ++ MPI2_IEEE_SGE_CHAIN_UNION Chain; ++ } u; ++} MPI2_IEEE_SGE_UNION, *PTR_MPI2_IEEE_SGE_UNION, ++ Mpi2IeeeSgeUnion_t, *pMpi2IeeeSgeUnion_t; ++ ++/**************************************************************************** ++* IEEE SGE union for IO SGL's ++****************************************************************************/ ++ ++typedef union _MPI25_SGE_IO_UNION { ++ MPI2_IEEE_SGE_SIMPLE64 IeeeSimple; ++ MPI25_IEEE_SGE_CHAIN64 IeeeChain; ++} MPI25_SGE_IO_UNION, *PTR_MPI25_SGE_IO_UNION, ++ Mpi25SGEIOUnion_t, *pMpi25SGEIOUnion_t; ++ ++/**************************************************************************** ++* IEEE SGE field definitions and masks ++****************************************************************************/ ++ ++/*Flags field bit definitions */ ++ ++#define MPI2_IEEE_SGE_FLAGS_ELEMENT_TYPE_MASK (0x80) ++#define MPI25_IEEE_SGE_FLAGS_END_OF_LIST (0x40) ++ ++#define MPI2_IEEE32_SGE_FLAGS_SHIFT (24) ++ ++#define MPI2_IEEE32_SGE_LENGTH_MASK (0x00FFFFFF) ++ ++/*Element Type */ ++ ++#define MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT (0x00) ++#define MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT (0x80) ++ ++/*Next Segment Format */ ++ ++#define MPI26_IEEE_SGE_FLAGS_NSF_MASK (0x1C) ++#define MPI26_IEEE_SGE_FLAGS_NSF_MPI_IEEE (0x00) ++ ++/*Data Location Address Space */ ++ ++#define MPI2_IEEE_SGE_FLAGS_ADDR_MASK (0x03) ++#define MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR (0x00) ++#define MPI2_IEEE_SGE_FLAGS_IOCDDR_ADDR (0x01) ++#define MPI2_IEEE_SGE_FLAGS_IOCPLB_ADDR (0x02) ++#define MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR (0x03) ++#define MPI2_IEEE_SGE_FLAGS_SYSTEMPLBPCI_ADDR (0x03) ++#define MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR \ ++ (MPI2_IEEE_SGE_FLAGS_SYSTEMPLBPCI_ADDR) ++#define MPI26_IEEE_SGE_FLAGS_IOCCTL_ADDR (0x02) ++ ++/**************************************************************************** ++* IEEE SGE operation Macros ++****************************************************************************/ ++ ++/*SIMPLE FlagsLength manipulations... */ ++#define MPI2_IEEE32_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_IEEE32_SGE_FLAGS_SHIFT) ++#define MPI2_IEEE32_SGE_GET_FLAGS(f) (((f) & ~MPI2_IEEE32_SGE_LENGTH_MASK) \ ++ >> MPI2_IEEE32_SGE_FLAGS_SHIFT) ++#define MPI2_IEEE32_SGE_LENGTH(f) ((f) & MPI2_IEEE32_SGE_LENGTH_MASK) ++ ++#define MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f, l) (MPI2_IEEE32_SGE_SET_FLAGS(f) |\ ++ MPI2_IEEE32_SGE_LENGTH(l)) ++ ++#define MPI2_IEEE32_pSGE_GET_FLAGS(psg) \ ++ MPI2_IEEE32_SGE_GET_FLAGS((psg)->FlagsLength) ++#define MPI2_IEEE32_pSGE_GET_LENGTH(psg) \ ++ MPI2_IEEE32_SGE_LENGTH((psg)->FlagsLength) ++#define MPI2_IEEE32_pSGE_SET_FLAGS_LENGTH(psg, f, l) ((psg)->FlagsLength = \ ++ MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f, l)) ++ ++/*CAUTION - The following are READ-MODIFY-WRITE! */ ++#define MPI2_IEEE32_pSGE_SET_FLAGS(psg, f) ((psg)->FlagsLength |= \ ++ MPI2_IEEE32_SGE_SET_FLAGS(f)) ++#define MPI2_IEEE32_pSGE_SET_LENGTH(psg, l) ((psg)->FlagsLength |= \ ++ MPI2_IEEE32_SGE_LENGTH(l)) ++ ++/***************************************************************************** ++* ++* Fusion-MPT MPI/IEEE Scatter Gather Unions ++* ++*****************************************************************************/ ++ ++typedef union _MPI2_SIMPLE_SGE_UNION { ++ MPI2_SGE_SIMPLE_UNION MpiSimple; ++ MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple; ++} MPI2_SIMPLE_SGE_UNION, *PTR_MPI2_SIMPLE_SGE_UNION, ++ Mpi2SimpleSgeUntion_t, *pMpi2SimpleSgeUntion_t; ++ ++typedef union _MPI2_SGE_IO_UNION { ++ MPI2_SGE_SIMPLE_UNION MpiSimple; ++ MPI2_SGE_CHAIN_UNION MpiChain; ++ MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple; ++ MPI2_IEEE_SGE_CHAIN_UNION IeeeChain; ++} MPI2_SGE_IO_UNION, *PTR_MPI2_SGE_IO_UNION, ++ Mpi2SGEIOUnion_t, *pMpi2SGEIOUnion_t; ++ ++/**************************************************************************** ++* ++* Values for SGLFlags field, used in many request messages with an SGL ++* ++****************************************************************************/ ++ ++/*values for MPI SGL Data Location Address Space subfield */ ++#define MPI2_SGLFLAGS_ADDRESS_SPACE_MASK (0x0C) ++#define MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE (0x00) ++#define MPI2_SGLFLAGS_IOCDDR_ADDRESS_SPACE (0x04) ++#define MPI2_SGLFLAGS_IOCPLB_ADDRESS_SPACE (0x08) ++#define MPI26_SGLFLAGS_IOCPLB_ADDRESS_SPACE (0x08) ++#define MPI2_SGLFLAGS_IOCPLBNTA_ADDRESS_SPACE (0x0C) ++/*values for SGL Type subfield */ ++#define MPI2_SGLFLAGS_SGL_TYPE_MASK (0x03) ++#define MPI2_SGLFLAGS_SGL_TYPE_MPI (0x00) ++#define MPI2_SGLFLAGS_SGL_TYPE_IEEE32 (0x01) ++#define MPI2_SGLFLAGS_SGL_TYPE_IEEE64 (0x02) ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h +new file mode 100644 +index 0000000..95356a8 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h +@@ -0,0 +1,3493 @@ ++/* ++ * Copyright 2000-2015 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2_cnfg.h ++ * Title: MPI Configuration messages and pages ++ * Creation Date: November 10, 2006 ++ * ++ * mpi2_cnfg.h Version: 02.00.35 ++ * ++ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 ++ * prefix are for use only on MPI v2.5 products, and must not be used ++ * with MPI v2.0 products. Unless otherwise noted, names beginning with ++ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products. ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 06-04-07 02.00.01 Added defines for SAS IO Unit Page 2 PhyFlags. ++ * Added Manufacturing Page 11. ++ * Added MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE ++ * define. ++ * 06-26-07 02.00.02 Adding generic structure for product-specific ++ * Manufacturing pages: MPI2_CONFIG_PAGE_MANUFACTURING_PS. ++ * Rework of BIOS Page 2 configuration page. ++ * Fixed MPI2_BIOSPAGE2_BOOT_DEVICE to be a union of the ++ * forms. ++ * Added configuration pages IOC Page 8 and Driver ++ * Persistent Mapping Page 0. ++ * 08-31-07 02.00.03 Modified configuration pages dealing with Integrated ++ * RAID (Manufacturing Page 4, RAID Volume Pages 0 and 1, ++ * RAID Physical Disk Pages 0 and 1, RAID Configuration ++ * Page 0). ++ * Added new value for AccessStatus field of SAS Device ++ * Page 0 (_SATA_NEEDS_INITIALIZATION). ++ * 10-31-07 02.00.04 Added missing SEPDevHandle field to ++ * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. ++ * 12-18-07 02.00.05 Modified IO Unit Page 0 to use 32-bit version fields for ++ * NVDATA. ++ * Modified IOC Page 7 to use masks and added field for ++ * SASBroadcastPrimitiveMasks. ++ * Added MPI2_CONFIG_PAGE_BIOS_4. ++ * Added MPI2_CONFIG_PAGE_LOG_0. ++ * 02-29-08 02.00.06 Modified various names to make them 32-character unique. ++ * Added SAS Device IDs. ++ * Updated Integrated RAID configuration pages including ++ * Manufacturing Page 4, IOC Page 6, and RAID Configuration ++ * Page 0. ++ * 05-21-08 02.00.07 Added define MPI2_MANPAGE4_MIX_SSD_SAS_SATA. ++ * Added define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION. ++ * Fixed define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING. ++ * Added missing MaxNumRoutedSasAddresses field to ++ * MPI2_CONFIG_PAGE_EXPANDER_0. ++ * Added SAS Port Page 0. ++ * Modified structure layout for ++ * MPI2_CONFIG_PAGE_DRIVER_MAPPING_0. ++ * 06-27-08 02.00.08 Changed MPI2_CONFIG_PAGE_RD_PDISK_1 to use ++ * MPI2_RAID_PHYS_DISK1_PATH_MAX to size the array. ++ * 10-02-08 02.00.09 Changed MPI2_RAID_PGAD_CONFIGNUM_MASK from 0x0000FFFF ++ * to 0x000000FF. ++ * Added two new values for the Physical Disk Coercion Size ++ * bits in the Flags field of Manufacturing Page 4. ++ * Added product-specific Manufacturing pages 16 to 31. ++ * Modified Flags bits for controlling write cache on SATA ++ * drives in IO Unit Page 1. ++ * Added new bit to AdditionalControlFlags of SAS IO Unit ++ * Page 1 to control Invalid Topology Correction. ++ * Added additional defines for RAID Volume Page 0 ++ * VolumeStatusFlags field. ++ * Modified meaning of RAID Volume Page 0 VolumeSettings ++ * define for auto-configure of hot-swap drives. ++ * Added SupportedPhysDisks field to RAID Volume Page 1 and ++ * added related defines. ++ * Added PhysDiskAttributes field (and related defines) to ++ * RAID Physical Disk Page 0. ++ * Added MPI2_SAS_PHYINFO_PHY_VACANT define. ++ * Added three new DiscoveryStatus bits for SAS IO Unit ++ * Page 0 and SAS Expander Page 0. ++ * Removed multiplexing information from SAS IO Unit pages. ++ * Added BootDeviceWaitTime field to SAS IO Unit Page 4. ++ * Removed Zone Address Resolved bit from PhyInfo and from ++ * Expander Page 0 Flags field. ++ * Added two new AccessStatus values to SAS Device Page 0 ++ * for indicating routing problems. Added 3 reserved words ++ * to this page. ++ * 01-19-09 02.00.10 Fixed defines for GPIOVal field of IO Unit Page 3. ++ * Inserted missing reserved field into structure for IOC ++ * Page 6. ++ * Added more pending task bits to RAID Volume Page 0 ++ * VolumeStatusFlags defines. ++ * Added MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED define. ++ * Added a new DiscoveryStatus bit for SAS IO Unit Page 0 ++ * and SAS Expander Page 0 to flag a downstream initiator ++ * when in simplified routing mode. ++ * Removed SATA Init Failure defines for DiscoveryStatus ++ * fields of SAS IO Unit Page 0 and SAS Expander Page 0. ++ * Added MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED define. ++ * Added PortGroups, DmaGroup, and ControlGroup fields to ++ * SAS Device Page 0. ++ * 05-06-09 02.00.11 Added structures and defines for IO Unit Page 5 and IO ++ * Unit Page 6. ++ * Added expander reduced functionality data to SAS ++ * Expander Page 0. ++ * Added SAS PHY Page 2 and SAS PHY Page 3. ++ * 07-30-09 02.00.12 Added IO Unit Page 7. ++ * Added new device ids. ++ * Added SAS IO Unit Page 5. ++ * Added partial and slumber power management capable flags ++ * to SAS Device Page 0 Flags field. ++ * Added PhyInfo defines for power condition. ++ * Added Ethernet configuration pages. ++ * 10-28-09 02.00.13 Added MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY. ++ * Added SAS PHY Page 4 structure and defines. ++ * 02-10-10 02.00.14 Modified the comments for the configuration page ++ * structures that contain an array of data. The host ++ * should use the "count" field in the page data (e.g. the ++ * NumPhys field) to determine the number of valid elements ++ * in the array. ++ * Added/modified some MPI2_MFGPAGE_DEVID_SAS defines. ++ * Added PowerManagementCapabilities to IO Unit Page 7. ++ * Added PortWidthModGroup field to ++ * MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS. ++ * Added MPI2_CONFIG_PAGE_SASIOUNIT_6 and related defines. ++ * Added MPI2_CONFIG_PAGE_SASIOUNIT_7 and related defines. ++ * Added MPI2_CONFIG_PAGE_SASIOUNIT_8 and related defines. ++ * 05-12-10 02.00.15 Added MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT ++ * define. ++ * Added MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE define. ++ * Added MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY define. ++ * 08-11-10 02.00.16 Removed IO Unit Page 1 device path (multi-pathing) ++ * defines. ++ * 11-10-10 02.00.17 Added ReceptacleID field (replacing Reserved1) to ++ * MPI2_MANPAGE7_CONNECTOR_INFO and reworked defines for ++ * the Pinout field. ++ * Added BoardTemperature and BoardTemperatureUnits fields ++ * to MPI2_CONFIG_PAGE_IO_UNIT_7. ++ * Added MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING define ++ * and MPI2_CONFIG_PAGE_EXT_MAN_PS structure. ++ * 02-23-11 02.00.18 Added ProxyVF_ID field to MPI2_CONFIG_REQUEST. ++ * Added IO Unit Page 8, IO Unit Page 9, ++ * and IO Unit Page 10. ++ * Added SASNotifyPrimitiveMasks field to ++ * MPI2_CONFIG_PAGE_IOC_7. ++ * 03-09-11 02.00.19 Fixed IO Unit Page 10 (to match the spec). ++ * 05-25-11 02.00.20 Cleaned up a few comments. ++ * 08-24-11 02.00.21 Marked the IO Unit Page 7 PowerManagementCapabilities ++ * for PCIe link as obsolete. ++ * Added SpinupFlags field containing a Disable Spin-up bit ++ * to the MPI2_SAS_IOUNIT4_SPINUP_GROUP fields of SAS IO ++ * Unit Page 4. ++ * 11-18-11 02.00.22 Added define MPI2_IOCPAGE6_CAP_FLAGS_4K_SECTORS_SUPPORT. ++ * Added UEFIVersion field to BIOS Page 1 and defined new ++ * BiosOptions bits. ++ * Incorporating additions for MPI v2.5. ++ * 11-27-12 02.00.23 Added MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER. ++ * Added MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID. ++ * 12-20-12 02.00.24 Marked MPI2_SASIOUNIT1_CONTROL_CLEAR_AFFILIATION as ++ * obsolete for MPI v2.5 and later. ++ * Added some defines for 12G SAS speeds. ++ * 04-09-13 02.00.25 Added MPI2_IOUNITPAGE1_ATA_SECURITY_FREEZE_LOCK. ++ * Fixed MPI2_IOUNITPAGE5_DMA_CAP_MASK_MAX_REQUESTS to ++ * match the specification. ++ * 08-19-13 02.00.26 Added reserved words to MPI2_CONFIG_PAGE_IO_UNIT_7 for ++ * future use. ++ * 12-05-13 02.00.27 Added MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL for ++ * MPI2_CONFIG_PAGE_MAN_7. ++ * Added EnclosureLevel and ConnectorName fields to ++ * MPI2_CONFIG_PAGE_SAS_DEV_0. ++ * Added MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID for ++ * MPI2_CONFIG_PAGE_SAS_DEV_0. ++ * Added EnclosureLevel field to ++ * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. ++ * Added MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID for ++ * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. ++ * 01-08-14 02.00.28 Added more defines for the BiosOptions field of ++ * MPI2_CONFIG_PAGE_BIOS_1. ++ * 06-13-14 02.00.29 Added SSUTimeout field to MPI2_CONFIG_PAGE_BIOS_1, and ++ * more defines for the BiosOptions field. ++ * 11-18-14 02.00.30 Updated copyright information. ++ * Added MPI2_BIOSPAGE1_OPTIONS_ADVANCED_CONFIG. ++ * Added AdapterOrderAux fields to BIOS Page 3. ++ * 03-16-15 02.00.31 Updated for MPI v2.6. ++ * Added Flags field to IO Unit Page 7. ++ * Added new SAS Phy Event codes ++ * 05-25-15 02.00.33 Added more defines for the BiosOptions field of ++ * MPI2_CONFIG_PAGE_BIOS_1. ++ * 08-25-15 02.00.34 Bumped Header Version. ++ * 12-18-15 02.00.35 Added SATADeviceWaitTime to SAS IO Unit Page 4. ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_CNFG_H ++#define MPI2_CNFG_H ++ ++/***************************************************************************** ++* Configuration Page Header and defines ++*****************************************************************************/ ++ ++/*Config Page Header */ ++typedef struct _MPI2_CONFIG_PAGE_HEADER { ++ U8 PageVersion; /*0x00 */ ++ U8 PageLength; /*0x01 */ ++ U8 PageNumber; /*0x02 */ ++ U8 PageType; /*0x03 */ ++} MPI2_CONFIG_PAGE_HEADER, *PTR_MPI2_CONFIG_PAGE_HEADER, ++ Mpi2ConfigPageHeader_t, *pMpi2ConfigPageHeader_t; ++ ++typedef union _MPI2_CONFIG_PAGE_HEADER_UNION { ++ MPI2_CONFIG_PAGE_HEADER Struct; ++ U8 Bytes[4]; ++ U16 Word16[2]; ++ U32 Word32; ++} MPI2_CONFIG_PAGE_HEADER_UNION, *PTR_MPI2_CONFIG_PAGE_HEADER_UNION, ++ Mpi2ConfigPageHeaderUnion, *pMpi2ConfigPageHeaderUnion; ++ ++/*Extended Config Page Header */ ++typedef struct _MPI2_CONFIG_EXTENDED_PAGE_HEADER { ++ U8 PageVersion; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 PageNumber; /*0x02 */ ++ U8 PageType; /*0x03 */ ++ U16 ExtPageLength; /*0x04 */ ++ U8 ExtPageType; /*0x06 */ ++ U8 Reserved2; /*0x07 */ ++} MPI2_CONFIG_EXTENDED_PAGE_HEADER, ++ *PTR_MPI2_CONFIG_EXTENDED_PAGE_HEADER, ++ Mpi2ConfigExtendedPageHeader_t, ++ *pMpi2ConfigExtendedPageHeader_t; ++ ++typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION { ++ MPI2_CONFIG_PAGE_HEADER Struct; ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Ext; ++ U8 Bytes[8]; ++ U16 Word16[4]; ++ U32 Word32[2]; ++} MPI2_CONFIG_EXT_PAGE_HEADER_UNION, ++ *PTR_MPI2_CONFIG_EXT_PAGE_HEADER_UNION, ++ Mpi2ConfigPageExtendedHeaderUnion, ++ *pMpi2ConfigPageExtendedHeaderUnion; ++ ++ ++/*PageType field values */ ++#define MPI2_CONFIG_PAGEATTR_READ_ONLY (0x00) ++#define MPI2_CONFIG_PAGEATTR_CHANGEABLE (0x10) ++#define MPI2_CONFIG_PAGEATTR_PERSISTENT (0x20) ++#define MPI2_CONFIG_PAGEATTR_MASK (0xF0) ++ ++#define MPI2_CONFIG_PAGETYPE_IO_UNIT (0x00) ++#define MPI2_CONFIG_PAGETYPE_IOC (0x01) ++#define MPI2_CONFIG_PAGETYPE_BIOS (0x02) ++#define MPI2_CONFIG_PAGETYPE_RAID_VOLUME (0x08) ++#define MPI2_CONFIG_PAGETYPE_MANUFACTURING (0x09) ++#define MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK (0x0A) ++#define MPI2_CONFIG_PAGETYPE_EXTENDED (0x0F) ++#define MPI2_CONFIG_PAGETYPE_MASK (0x0F) ++ ++#define MPI2_CONFIG_TYPENUM_MASK (0x0FFF) ++ ++ ++/*ExtPageType field values */ ++#define MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT (0x10) ++#define MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER (0x11) ++#define MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE (0x12) ++#define MPI2_CONFIG_EXTPAGETYPE_SAS_PHY (0x13) ++#define MPI2_CONFIG_EXTPAGETYPE_LOG (0x14) ++#define MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE (0x15) ++#define MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG (0x16) ++#define MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING (0x17) ++#define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT (0x18) ++#define MPI2_CONFIG_EXTPAGETYPE_ETHERNET (0x19) ++#define MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING (0x1A) ++ ++ ++/***************************************************************************** ++* PageAddress defines ++*****************************************************************************/ ++ ++/*RAID Volume PageAddress format */ ++#define MPI2_RAID_VOLUME_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) ++#define MPI2_RAID_VOLUME_PGAD_FORM_HANDLE (0x10000000) ++ ++#define MPI2_RAID_VOLUME_PGAD_HANDLE_MASK (0x0000FFFF) ++ ++ ++/*RAID Physical Disk PageAddress format */ ++#define MPI2_PHYSDISK_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM (0x00000000) ++#define MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM (0x10000000) ++#define MPI2_PHYSDISK_PGAD_FORM_DEVHANDLE (0x20000000) ++ ++#define MPI2_PHYSDISK_PGAD_PHYSDISKNUM_MASK (0x000000FF) ++#define MPI2_PHYSDISK_PGAD_DEVHANDLE_MASK (0x0000FFFF) ++ ++ ++/*SAS Expander PageAddress format */ ++#define MPI2_SAS_EXPAND_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL (0x00000000) ++#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM (0x10000000) ++#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL (0x20000000) ++ ++#define MPI2_SAS_EXPAND_PGAD_HANDLE_MASK (0x0000FFFF) ++#define MPI2_SAS_EXPAND_PGAD_PHYNUM_MASK (0x00FF0000) ++#define MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT (16) ++ ++ ++/*SAS Device PageAddress format */ ++#define MPI2_SAS_DEVICE_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) ++#define MPI2_SAS_DEVICE_PGAD_FORM_HANDLE (0x20000000) ++ ++#define MPI2_SAS_DEVICE_PGAD_HANDLE_MASK (0x0000FFFF) ++ ++ ++/*SAS PHY PageAddress format */ ++#define MPI2_SAS_PHY_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER (0x00000000) ++#define MPI2_SAS_PHY_PGAD_FORM_PHY_TBL_INDEX (0x10000000) ++ ++#define MPI2_SAS_PHY_PGAD_PHY_NUMBER_MASK (0x000000FF) ++#define MPI2_SAS_PHY_PGAD_PHY_TBL_INDEX_MASK (0x0000FFFF) ++ ++ ++/*SAS Port PageAddress format */ ++#define MPI2_SASPORT_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_SASPORT_PGAD_FORM_GET_NEXT_PORT (0x00000000) ++#define MPI2_SASPORT_PGAD_FORM_PORT_NUM (0x10000000) ++ ++#define MPI2_SASPORT_PGAD_PORTNUMBER_MASK (0x00000FFF) ++ ++ ++/*SAS Enclosure PageAddress format */ ++#define MPI2_SAS_ENCLOS_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) ++#define MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE (0x10000000) ++ ++#define MPI2_SAS_ENCLOS_PGAD_HANDLE_MASK (0x0000FFFF) ++ ++ ++/*RAID Configuration PageAddress format */ ++#define MPI2_RAID_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_RAID_PGAD_FORM_GET_NEXT_CONFIGNUM (0x00000000) ++#define MPI2_RAID_PGAD_FORM_CONFIGNUM (0x10000000) ++#define MPI2_RAID_PGAD_FORM_ACTIVE_CONFIG (0x20000000) ++ ++#define MPI2_RAID_PGAD_CONFIGNUM_MASK (0x000000FF) ++ ++ ++/*Driver Persistent Mapping PageAddress format */ ++#define MPI2_DPM_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_DPM_PGAD_FORM_ENTRY_RANGE (0x00000000) ++ ++#define MPI2_DPM_PGAD_ENTRY_COUNT_MASK (0x0FFF0000) ++#define MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT (16) ++#define MPI2_DPM_PGAD_START_ENTRY_MASK (0x0000FFFF) ++ ++ ++/*Ethernet PageAddress format */ ++#define MPI2_ETHERNET_PGAD_FORM_MASK (0xF0000000) ++#define MPI2_ETHERNET_PGAD_FORM_IF_NUM (0x00000000) ++ ++#define MPI2_ETHERNET_PGAD_IF_NUMBER_MASK (0x000000FF) ++ ++ ++/**************************************************************************** ++* Configuration messages ++****************************************************************************/ ++ ++/*Configuration Request Message */ ++typedef struct _MPI2_CONFIG_REQUEST { ++ U8 Action; /*0x00 */ ++ U8 SGLFlags; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 ExtPageLength; /*0x04 */ ++ U8 ExtPageType; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U8 Reserved2; /*0x0C */ ++ U8 ProxyVF_ID; /*0x0D */ ++ U16 Reserved4; /*0x0E */ ++ U32 Reserved3; /*0x10 */ ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x14 */ ++ U32 PageAddress; /*0x18 */ ++ MPI2_SGE_IO_UNION PageBufferSGE; /*0x1C */ ++} MPI2_CONFIG_REQUEST, *PTR_MPI2_CONFIG_REQUEST, ++ Mpi2ConfigRequest_t, *pMpi2ConfigRequest_t; ++ ++/*values for the Action field */ ++#define MPI2_CONFIG_ACTION_PAGE_HEADER (0x00) ++#define MPI2_CONFIG_ACTION_PAGE_READ_CURRENT (0x01) ++#define MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT (0x02) ++#define MPI2_CONFIG_ACTION_PAGE_DEFAULT (0x03) ++#define MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM (0x04) ++#define MPI2_CONFIG_ACTION_PAGE_READ_DEFAULT (0x05) ++#define MPI2_CONFIG_ACTION_PAGE_READ_NVRAM (0x06) ++#define MPI2_CONFIG_ACTION_PAGE_GET_CHANGEABLE (0x07) ++ ++/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ ++ ++ ++/*Config Reply Message */ ++typedef struct _MPI2_CONFIG_REPLY { ++ U8 Action; /*0x00 */ ++ U8 SGLFlags; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 ExtPageLength; /*0x04 */ ++ U8 ExtPageType; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U16 Reserved2; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x14 */ ++} MPI2_CONFIG_REPLY, *PTR_MPI2_CONFIG_REPLY, ++ Mpi2ConfigReply_t, *pMpi2ConfigReply_t; ++ ++ ++ ++/***************************************************************************** ++* ++* C o n f i g u r a t i o n P a g e s ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* Manufacturing Config pages ++****************************************************************************/ ++ ++#define MPI2_MFGPAGE_VENDORID_LSI (0x1000) ++ ++/*MPI v2.0 SAS products */ ++#define MPI2_MFGPAGE_DEVID_SAS2004 (0x0070) ++#define MPI2_MFGPAGE_DEVID_SAS2008 (0x0072) ++#define MPI2_MFGPAGE_DEVID_SAS2108_1 (0x0074) ++#define MPI2_MFGPAGE_DEVID_SAS2108_2 (0x0076) ++#define MPI2_MFGPAGE_DEVID_SAS2108_3 (0x0077) ++#define MPI2_MFGPAGE_DEVID_SAS2116_1 (0x0064) ++#define MPI2_MFGPAGE_DEVID_SAS2116_2 (0x0065) ++ ++#define MPI2_MFGPAGE_DEVID_SSS6200 (0x007E) ++ ++#define MPI2_MFGPAGE_DEVID_SAS2208_1 (0x0080) ++#define MPI2_MFGPAGE_DEVID_SAS2208_2 (0x0081) ++#define MPI2_MFGPAGE_DEVID_SAS2208_3 (0x0082) ++#define MPI2_MFGPAGE_DEVID_SAS2208_4 (0x0083) ++#define MPI2_MFGPAGE_DEVID_SAS2208_5 (0x0084) ++#define MPI2_MFGPAGE_DEVID_SAS2208_6 (0x0085) ++#define MPI2_MFGPAGE_DEVID_SAS2308_1 (0x0086) ++#define MPI2_MFGPAGE_DEVID_SAS2308_2 (0x0087) ++#define MPI2_MFGPAGE_DEVID_SAS2308_3 (0x006E) ++ ++/*MPI v2.5 SAS products */ ++#define MPI25_MFGPAGE_DEVID_SAS3004 (0x0096) ++#define MPI25_MFGPAGE_DEVID_SAS3008 (0x0097) ++#define MPI25_MFGPAGE_DEVID_SAS3108_1 (0x0090) ++#define MPI25_MFGPAGE_DEVID_SAS3108_2 (0x0091) ++#define MPI25_MFGPAGE_DEVID_SAS3108_5 (0x0094) ++#define MPI25_MFGPAGE_DEVID_SAS3108_6 (0x0095) ++ ++/* MPI v2.6 SAS Products */ ++#define MPI26_MFGPAGE_DEVID_SAS3216 (0x00C9) ++#define MPI26_MFGPAGE_DEVID_SAS3224 (0x00C4) ++#define MPI26_MFGPAGE_DEVID_SAS3316_1 (0x00C5) ++#define MPI26_MFGPAGE_DEVID_SAS3316_2 (0x00C6) ++#define MPI26_MFGPAGE_DEVID_SAS3316_3 (0x00C7) ++#define MPI26_MFGPAGE_DEVID_SAS3316_4 (0x00C8) ++#define MPI26_MFGPAGE_DEVID_SAS3324_1 (0x00C0) ++#define MPI26_MFGPAGE_DEVID_SAS3324_2 (0x00C1) ++#define MPI26_MFGPAGE_DEVID_SAS3324_3 (0x00C2) ++#define MPI26_MFGPAGE_DEVID_SAS3324_4 (0x00C3) ++ ++/*Manufacturing Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_0 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 ChipName[16]; /*0x04 */ ++ U8 ChipRevision[8]; /*0x14 */ ++ U8 BoardName[16]; /*0x1C */ ++ U8 BoardAssembly[16]; /*0x2C */ ++ U8 BoardTracerNumber[16]; /*0x3C */ ++} MPI2_CONFIG_PAGE_MAN_0, ++ *PTR_MPI2_CONFIG_PAGE_MAN_0, ++ Mpi2ManufacturingPage0_t, ++ *pMpi2ManufacturingPage0_t; ++ ++#define MPI2_MANUFACTURING0_PAGEVERSION (0x00) ++ ++ ++/*Manufacturing Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_1 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 VPD[256]; /*0x04 */ ++} MPI2_CONFIG_PAGE_MAN_1, ++ *PTR_MPI2_CONFIG_PAGE_MAN_1, ++ Mpi2ManufacturingPage1_t, ++ *pMpi2ManufacturingPage1_t; ++ ++#define MPI2_MANUFACTURING1_PAGEVERSION (0x00) ++ ++ ++typedef struct _MPI2_CHIP_REVISION_ID { ++ U16 DeviceID; /*0x00 */ ++ U8 PCIRevisionID; /*0x02 */ ++ U8 Reserved; /*0x03 */ ++} MPI2_CHIP_REVISION_ID, *PTR_MPI2_CHIP_REVISION_ID, ++ Mpi2ChipRevisionId_t, *pMpi2ChipRevisionId_t; ++ ++ ++/*Manufacturing Page 2 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check Header.PageLength at runtime. ++ */ ++#ifndef MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS ++#define MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_2 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ MPI2_CHIP_REVISION_ID ChipId; /*0x04 */ ++ U32 ++ HwSettings[MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS];/*0x08 */ ++} MPI2_CONFIG_PAGE_MAN_2, ++ *PTR_MPI2_CONFIG_PAGE_MAN_2, ++ Mpi2ManufacturingPage2_t, ++ *pMpi2ManufacturingPage2_t; ++ ++#define MPI2_MANUFACTURING2_PAGEVERSION (0x00) ++ ++ ++/*Manufacturing Page 3 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check Header.PageLength at runtime. ++ */ ++#ifndef MPI2_MAN_PAGE_3_INFO_WORDS ++#define MPI2_MAN_PAGE_3_INFO_WORDS (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_3 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ MPI2_CHIP_REVISION_ID ChipId; /*0x04 */ ++ U32 ++ Info[MPI2_MAN_PAGE_3_INFO_WORDS];/*0x08 */ ++} MPI2_CONFIG_PAGE_MAN_3, ++ *PTR_MPI2_CONFIG_PAGE_MAN_3, ++ Mpi2ManufacturingPage3_t, ++ *pMpi2ManufacturingPage3_t; ++ ++#define MPI2_MANUFACTURING3_PAGEVERSION (0x00) ++ ++ ++/*Manufacturing Page 4 */ ++ ++typedef struct _MPI2_MANPAGE4_PWR_SAVE_SETTINGS { ++ U8 PowerSaveFlags; /*0x00 */ ++ U8 InternalOperationsSleepTime; /*0x01 */ ++ U8 InternalOperationsRunTime; /*0x02 */ ++ U8 HostIdleTime; /*0x03 */ ++} MPI2_MANPAGE4_PWR_SAVE_SETTINGS, ++ *PTR_MPI2_MANPAGE4_PWR_SAVE_SETTINGS, ++ Mpi2ManPage4PwrSaveSettings_t, ++ *pMpi2ManPage4PwrSaveSettings_t; ++ ++/*defines for the PowerSaveFlags field */ ++#define MPI2_MANPAGE4_MASK_POWERSAVE_MODE (0x03) ++#define MPI2_MANPAGE4_POWERSAVE_MODE_DISABLED (0x00) ++#define MPI2_MANPAGE4_CUSTOM_POWERSAVE_MODE (0x01) ++#define MPI2_MANPAGE4_FULL_POWERSAVE_MODE (0x02) ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_4 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 Flags; /*0x08 */ ++ U8 InquirySize; /*0x0C */ ++ U8 Reserved2; /*0x0D */ ++ U16 Reserved3; /*0x0E */ ++ U8 InquiryData[56]; /*0x10 */ ++ U32 RAID0VolumeSettings; /*0x48 */ ++ U32 RAID1EVolumeSettings; /*0x4C */ ++ U32 RAID1VolumeSettings; /*0x50 */ ++ U32 RAID10VolumeSettings; /*0x54 */ ++ U32 Reserved4; /*0x58 */ ++ U32 Reserved5; /*0x5C */ ++ MPI2_MANPAGE4_PWR_SAVE_SETTINGS PowerSaveSettings; /*0x60 */ ++ U8 MaxOCEDisks; /*0x64 */ ++ U8 ResyncRate; /*0x65 */ ++ U16 DataScrubDuration; /*0x66 */ ++ U8 MaxHotSpares; /*0x68 */ ++ U8 MaxPhysDisksPerVol; /*0x69 */ ++ U8 MaxPhysDisks; /*0x6A */ ++ U8 MaxVolumes; /*0x6B */ ++} MPI2_CONFIG_PAGE_MAN_4, ++ *PTR_MPI2_CONFIG_PAGE_MAN_4, ++ Mpi2ManufacturingPage4_t, ++ *pMpi2ManufacturingPage4_t; ++ ++#define MPI2_MANUFACTURING4_PAGEVERSION (0x0A) ++ ++/*Manufacturing Page 4 Flags field */ ++#define MPI2_MANPAGE4_METADATA_SIZE_MASK (0x00030000) ++#define MPI2_MANPAGE4_METADATA_512MB (0x00000000) ++ ++#define MPI2_MANPAGE4_MIX_SSD_SAS_SATA (0x00008000) ++#define MPI2_MANPAGE4_MIX_SSD_AND_NON_SSD (0x00004000) ++#define MPI2_MANPAGE4_HIDE_PHYSDISK_NON_IR (0x00002000) ++ ++#define MPI2_MANPAGE4_MASK_PHYSDISK_COERCION (0x00001C00) ++#define MPI2_MANPAGE4_PHYSDISK_COERCION_1GB (0x00000000) ++#define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION (0x00000400) ++#define MPI2_MANPAGE4_PHYSDISK_ADAPTIVE_COERCION (0x00000800) ++#define MPI2_MANPAGE4_PHYSDISK_ZERO_COERCION (0x00000C00) ++ ++#define MPI2_MANPAGE4_MASK_BAD_BLOCK_MARKING (0x00000300) ++#define MPI2_MANPAGE4_DEFAULT_BAD_BLOCK_MARKING (0x00000000) ++#define MPI2_MANPAGE4_TABLE_BAD_BLOCK_MARKING (0x00000100) ++#define MPI2_MANPAGE4_WRITE_LONG_BAD_BLOCK_MARKING (0x00000200) ++ ++#define MPI2_MANPAGE4_FORCE_OFFLINE_FAILOVER (0x00000080) ++#define MPI2_MANPAGE4_RAID10_DISABLE (0x00000040) ++#define MPI2_MANPAGE4_RAID1E_DISABLE (0x00000020) ++#define MPI2_MANPAGE4_RAID1_DISABLE (0x00000010) ++#define MPI2_MANPAGE4_RAID0_DISABLE (0x00000008) ++#define MPI2_MANPAGE4_IR_MODEPAGE8_DISABLE (0x00000004) ++#define MPI2_MANPAGE4_IM_RESYNC_CACHE_ENABLE (0x00000002) ++#define MPI2_MANPAGE4_IR_NO_MIX_SAS_SATA (0x00000001) ++ ++ ++/*Manufacturing Page 5 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI2_MAN_PAGE_5_PHY_ENTRIES ++#define MPI2_MAN_PAGE_5_PHY_ENTRIES (1) ++#endif ++ ++typedef struct _MPI2_MANUFACTURING5_ENTRY { ++ U64 WWID; /*0x00 */ ++ U64 DeviceName; /*0x08 */ ++} MPI2_MANUFACTURING5_ENTRY, ++ *PTR_MPI2_MANUFACTURING5_ENTRY, ++ Mpi2Manufacturing5Entry_t, ++ *pMpi2Manufacturing5Entry_t; ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_5 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 NumPhys; /*0x04 */ ++ U8 Reserved1; /*0x05 */ ++ U16 Reserved2; /*0x06 */ ++ U32 Reserved3; /*0x08 */ ++ U32 Reserved4; /*0x0C */ ++ MPI2_MANUFACTURING5_ENTRY ++ Phy[MPI2_MAN_PAGE_5_PHY_ENTRIES];/*0x08 */ ++} MPI2_CONFIG_PAGE_MAN_5, ++ *PTR_MPI2_CONFIG_PAGE_MAN_5, ++ Mpi2ManufacturingPage5_t, ++ *pMpi2ManufacturingPage5_t; ++ ++#define MPI2_MANUFACTURING5_PAGEVERSION (0x03) ++ ++ ++/*Manufacturing Page 6 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_6 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 ProductSpecificInfo;/*0x04 */ ++} MPI2_CONFIG_PAGE_MAN_6, ++ *PTR_MPI2_CONFIG_PAGE_MAN_6, ++ Mpi2ManufacturingPage6_t, ++ *pMpi2ManufacturingPage6_t; ++ ++#define MPI2_MANUFACTURING6_PAGEVERSION (0x00) ++ ++ ++/*Manufacturing Page 7 */ ++ ++typedef struct _MPI2_MANPAGE7_CONNECTOR_INFO { ++ U32 Pinout; /*0x00 */ ++ U8 Connector[16]; /*0x04 */ ++ U8 Location; /*0x14 */ ++ U8 ReceptacleID; /*0x15 */ ++ U16 Slot; /*0x16 */ ++ U32 Reserved2; /*0x18 */ ++} MPI2_MANPAGE7_CONNECTOR_INFO, ++ *PTR_MPI2_MANPAGE7_CONNECTOR_INFO, ++ Mpi2ManPage7ConnectorInfo_t, ++ *pMpi2ManPage7ConnectorInfo_t; ++ ++/*defines for the Pinout field */ ++#define MPI2_MANPAGE7_PINOUT_LANE_MASK (0x0000FF00) ++#define MPI2_MANPAGE7_PINOUT_LANE_SHIFT (8) ++ ++#define MPI2_MANPAGE7_PINOUT_TYPE_MASK (0x000000FF) ++#define MPI2_MANPAGE7_PINOUT_TYPE_UNKNOWN (0x00) ++#define MPI2_MANPAGE7_PINOUT_SATA_SINGLE (0x01) ++#define MPI2_MANPAGE7_PINOUT_SFF_8482 (0x02) ++#define MPI2_MANPAGE7_PINOUT_SFF_8486 (0x03) ++#define MPI2_MANPAGE7_PINOUT_SFF_8484 (0x04) ++#define MPI2_MANPAGE7_PINOUT_SFF_8087 (0x05) ++#define MPI2_MANPAGE7_PINOUT_SFF_8643_4I (0x06) ++#define MPI2_MANPAGE7_PINOUT_SFF_8643_8I (0x07) ++#define MPI2_MANPAGE7_PINOUT_SFF_8470 (0x08) ++#define MPI2_MANPAGE7_PINOUT_SFF_8088 (0x09) ++#define MPI2_MANPAGE7_PINOUT_SFF_8644_4X (0x0A) ++#define MPI2_MANPAGE7_PINOUT_SFF_8644_8X (0x0B) ++#define MPI2_MANPAGE7_PINOUT_SFF_8644_16X (0x0C) ++#define MPI2_MANPAGE7_PINOUT_SFF_8436 (0x0D) ++ ++/*defines for the Location field */ ++#define MPI2_MANPAGE7_LOCATION_UNKNOWN (0x01) ++#define MPI2_MANPAGE7_LOCATION_INTERNAL (0x02) ++#define MPI2_MANPAGE7_LOCATION_EXTERNAL (0x04) ++#define MPI2_MANPAGE7_LOCATION_SWITCHABLE (0x08) ++#define MPI2_MANPAGE7_LOCATION_AUTO (0x10) ++#define MPI2_MANPAGE7_LOCATION_NOT_PRESENT (0x20) ++#define MPI2_MANPAGE7_LOCATION_NOT_CONNECTED (0x80) ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI2_MANPAGE7_CONNECTOR_INFO_MAX ++#define MPI2_MANPAGE7_CONNECTOR_INFO_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_7 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 Reserved2; /*0x08 */ ++ U32 Flags; /*0x0C */ ++ U8 EnclosureName[16]; /*0x10 */ ++ U8 NumPhys; /*0x20 */ ++ U8 Reserved3; /*0x21 */ ++ U16 Reserved4; /*0x22 */ ++ MPI2_MANPAGE7_CONNECTOR_INFO ++ ConnectorInfo[MPI2_MANPAGE7_CONNECTOR_INFO_MAX]; /*0x24 */ ++} MPI2_CONFIG_PAGE_MAN_7, ++ *PTR_MPI2_CONFIG_PAGE_MAN_7, ++ Mpi2ManufacturingPage7_t, ++ *pMpi2ManufacturingPage7_t; ++ ++#define MPI2_MANUFACTURING7_PAGEVERSION (0x01) ++ ++/*defines for the Flags field */ ++#define MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL (0x00000008) ++#define MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER (0x00000002) ++#define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO (0x00000001) ++ ++ ++/* ++ *Generic structure to use for product-specific manufacturing pages ++ *(currently Manufacturing Page 8 through Manufacturing Page 31). ++ */ ++ ++typedef struct _MPI2_CONFIG_PAGE_MAN_PS { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 ProductSpecificInfo;/*0x04 */ ++} MPI2_CONFIG_PAGE_MAN_PS, ++ *PTR_MPI2_CONFIG_PAGE_MAN_PS, ++ Mpi2ManufacturingPagePS_t, ++ *pMpi2ManufacturingPagePS_t; ++ ++#define MPI2_MANUFACTURING8_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING9_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING10_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING11_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING12_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING13_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING14_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING15_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING16_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING17_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING18_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING19_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING20_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING21_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING22_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING23_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING24_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING25_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING26_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING27_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING28_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING29_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING30_PAGEVERSION (0x00) ++#define MPI2_MANUFACTURING31_PAGEVERSION (0x00) ++ ++ ++/**************************************************************************** ++* IO Unit Config Pages ++****************************************************************************/ ++ ++/*IO Unit Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_0 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U64 UniqueValue; /*0x04 */ ++ MPI2_VERSION_UNION NvdataVersionDefault; /*0x08 */ ++ MPI2_VERSION_UNION NvdataVersionPersistent; /*0x0A */ ++} MPI2_CONFIG_PAGE_IO_UNIT_0, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_0, ++ Mpi2IOUnitPage0_t, *pMpi2IOUnitPage0_t; ++ ++#define MPI2_IOUNITPAGE0_PAGEVERSION (0x02) ++ ++ ++/*IO Unit Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_1 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Flags; /*0x04 */ ++} MPI2_CONFIG_PAGE_IO_UNIT_1, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_1, ++ Mpi2IOUnitPage1_t, *pMpi2IOUnitPage1_t; ++ ++#define MPI2_IOUNITPAGE1_PAGEVERSION (0x04) ++ ++/*IO Unit Page 1 Flags defines */ ++#define MPI2_IOUNITPAGE1_ATA_SECURITY_FREEZE_LOCK (0x00004000) ++#define MPI25_IOUNITPAGE1_NEW_DEVICE_FAST_PATH_DISABLE (0x00002000) ++#define MPI25_IOUNITPAGE1_DISABLE_FAST_PATH (0x00001000) ++#define MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY (0x00000800) ++#define MPI2_IOUNITPAGE1_MASK_SATA_WRITE_CACHE (0x00000600) ++#define MPI2_IOUNITPAGE1_SATA_WRITE_CACHE_SHIFT (9) ++#define MPI2_IOUNITPAGE1_ENABLE_SATA_WRITE_CACHE (0x00000000) ++#define MPI2_IOUNITPAGE1_DISABLE_SATA_WRITE_CACHE (0x00000200) ++#define MPI2_IOUNITPAGE1_UNCHANGED_SATA_WRITE_CACHE (0x00000400) ++#define MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE (0x00000100) ++#define MPI2_IOUNITPAGE1_DISABLE_IR (0x00000040) ++#define MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING (0x00000020) ++#define MPI2_IOUNITPAGE1_IR_USE_STATIC_VOLUME_ID (0x00000004) ++ ++ ++/*IO Unit Page 3 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for GPIOCount at runtime. ++ */ ++#ifndef MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX ++#define MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_3 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 GPIOCount; /*0x04 */ ++ U8 Reserved1; /*0x05 */ ++ U16 Reserved2; /*0x06 */ ++ U16 ++ GPIOVal[MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX];/*0x08 */ ++} MPI2_CONFIG_PAGE_IO_UNIT_3, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_3, ++ Mpi2IOUnitPage3_t, *pMpi2IOUnitPage3_t; ++ ++#define MPI2_IOUNITPAGE3_PAGEVERSION (0x01) ++ ++/*defines for IO Unit Page 3 GPIOVal field */ ++#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_MASK (0xFFFC) ++#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_SHIFT (2) ++#define MPI2_IOUNITPAGE3_GPIO_SETTING_OFF (0x0000) ++#define MPI2_IOUNITPAGE3_GPIO_SETTING_ON (0x0001) ++ ++ ++/*IO Unit Page 5 */ ++ ++/* ++ *Upper layer code (drivers, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumDmaEngines at runtime. ++ */ ++#ifndef MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES ++#define MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_5 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U64 ++ RaidAcceleratorBufferBaseAddress; /*0x04 */ ++ U64 ++ RaidAcceleratorBufferSize; /*0x0C */ ++ U64 ++ RaidAcceleratorControlBaseAddress; /*0x14 */ ++ U8 RAControlSize; /*0x1C */ ++ U8 NumDmaEngines; /*0x1D */ ++ U8 RAMinControlSize; /*0x1E */ ++ U8 RAMaxControlSize; /*0x1F */ ++ U32 Reserved1; /*0x20 */ ++ U32 Reserved2; /*0x24 */ ++ U32 Reserved3; /*0x28 */ ++ U32 ++ DmaEngineCapabilities[MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES]; /*0x2C */ ++} MPI2_CONFIG_PAGE_IO_UNIT_5, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_5, ++ Mpi2IOUnitPage5_t, *pMpi2IOUnitPage5_t; ++ ++#define MPI2_IOUNITPAGE5_PAGEVERSION (0x00) ++ ++/*defines for IO Unit Page 5 DmaEngineCapabilities field */ ++#define MPI2_IOUNITPAGE5_DMA_CAP_MASK_MAX_REQUESTS (0xFFFF0000) ++#define MPI2_IOUNITPAGE5_DMA_CAP_SHIFT_MAX_REQUESTS (16) ++ ++#define MPI2_IOUNITPAGE5_DMA_CAP_EEDP (0x0008) ++#define MPI2_IOUNITPAGE5_DMA_CAP_PARITY_GENERATION (0x0004) ++#define MPI2_IOUNITPAGE5_DMA_CAP_HASHING (0x0002) ++#define MPI2_IOUNITPAGE5_DMA_CAP_ENCRYPTION (0x0001) ++ ++ ++/*IO Unit Page 6 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_6 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U16 Flags; /*0x04 */ ++ U8 RAHostControlSize; /*0x06 */ ++ U8 Reserved0; /*0x07 */ ++ U64 ++ RaidAcceleratorHostControlBaseAddress; /*0x08 */ ++ U32 Reserved1; /*0x10 */ ++ U32 Reserved2; /*0x14 */ ++ U32 Reserved3; /*0x18 */ ++} MPI2_CONFIG_PAGE_IO_UNIT_6, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_6, ++ Mpi2IOUnitPage6_t, *pMpi2IOUnitPage6_t; ++ ++#define MPI2_IOUNITPAGE6_PAGEVERSION (0x00) ++ ++/*defines for IO Unit Page 6 Flags field */ ++#define MPI2_IOUNITPAGE6_FLAGS_ENABLE_RAID_ACCELERATOR (0x0001) ++ ++ ++/*IO Unit Page 7 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 CurrentPowerMode; /*0x04 */ ++ U8 PreviousPowerMode; /*0x05 */ ++ U8 PCIeWidth; /*0x06 */ ++ U8 PCIeSpeed; /*0x07 */ ++ U32 ProcessorState; /*0x08 */ ++ U32 ++ PowerManagementCapabilities; /*0x0C */ ++ U16 IOCTemperature; /*0x10 */ ++ U8 ++ IOCTemperatureUnits; /*0x12 */ ++ U8 IOCSpeed; /*0x13 */ ++ U16 BoardTemperature; /*0x14 */ ++ U8 ++ BoardTemperatureUnits; /*0x16 */ ++ U8 Reserved3; /*0x17 */ ++ U32 BoardPowerRequirement; /*0x18 */ ++ U32 PCISlotPowerAllocation; /*0x1C */ ++/* reserved prior to MPI v2.6 */ ++ U8 Flags; /* 0x20 */ ++ U8 Reserved6; /* 0x21 */ ++ U16 Reserved7; /* 0x22 */ ++ U32 Reserved8; /* 0x24 */ ++} MPI2_CONFIG_PAGE_IO_UNIT_7, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_7, ++ Mpi2IOUnitPage7_t, *pMpi2IOUnitPage7_t; ++ ++#define MPI2_IOUNITPAGE7_PAGEVERSION (0x05) ++ ++/*defines for IO Unit Page 7 CurrentPowerMode and PreviousPowerMode fields */ ++#define MPI25_IOUNITPAGE7_PM_INIT_MASK (0xC0) ++#define MPI25_IOUNITPAGE7_PM_INIT_UNAVAILABLE (0x00) ++#define MPI25_IOUNITPAGE7_PM_INIT_HOST (0x40) ++#define MPI25_IOUNITPAGE7_PM_INIT_IO_UNIT (0x80) ++#define MPI25_IOUNITPAGE7_PM_INIT_PCIE_DPA (0xC0) ++ ++#define MPI25_IOUNITPAGE7_PM_MODE_MASK (0x07) ++#define MPI25_IOUNITPAGE7_PM_MODE_UNAVAILABLE (0x00) ++#define MPI25_IOUNITPAGE7_PM_MODE_UNKNOWN (0x01) ++#define MPI25_IOUNITPAGE7_PM_MODE_FULL_POWER (0x04) ++#define MPI25_IOUNITPAGE7_PM_MODE_REDUCED_POWER (0x05) ++#define MPI25_IOUNITPAGE7_PM_MODE_STANDBY (0x06) ++ ++ ++/*defines for IO Unit Page 7 PCIeWidth field */ ++#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X1 (0x01) ++#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X2 (0x02) ++#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X4 (0x04) ++#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X8 (0x08) ++ ++/*defines for IO Unit Page 7 PCIeSpeed field */ ++#define MPI2_IOUNITPAGE7_PCIE_SPEED_2_5_GBPS (0x00) ++#define MPI2_IOUNITPAGE7_PCIE_SPEED_5_0_GBPS (0x01) ++#define MPI2_IOUNITPAGE7_PCIE_SPEED_8_0_GBPS (0x02) ++ ++/*defines for IO Unit Page 7 ProcessorState field */ ++#define MPI2_IOUNITPAGE7_PSTATE_MASK_SECOND (0x0000000F) ++#define MPI2_IOUNITPAGE7_PSTATE_SHIFT_SECOND (0) ++ ++#define MPI2_IOUNITPAGE7_PSTATE_NOT_PRESENT (0x00) ++#define MPI2_IOUNITPAGE7_PSTATE_DISABLED (0x01) ++#define MPI2_IOUNITPAGE7_PSTATE_ENABLED (0x02) ++ ++/*defines for IO Unit Page 7 PowerManagementCapabilities field */ ++#define MPI25_IOUNITPAGE7_PMCAP_DPA_FULL_PWR_MODE (0x00400000) ++#define MPI25_IOUNITPAGE7_PMCAP_DPA_REDUCED_PWR_MODE (0x00200000) ++#define MPI25_IOUNITPAGE7_PMCAP_DPA_STANDBY_MODE (0x00100000) ++#define MPI25_IOUNITPAGE7_PMCAP_HOST_FULL_PWR_MODE (0x00040000) ++#define MPI25_IOUNITPAGE7_PMCAP_HOST_REDUCED_PWR_MODE (0x00020000) ++#define MPI25_IOUNITPAGE7_PMCAP_HOST_STANDBY_MODE (0x00010000) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_FULL_PWR_MODE (0x00004000) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_REDUCED_PWR_MODE (0x00002000) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_STANDBY_MODE (0x00001000) ++#define MPI2_IOUNITPAGE7_PMCAP_HOST_12_5_PCT_IOCSPEED (0x00000400) ++#define MPI2_IOUNITPAGE7_PMCAP_HOST_25_0_PCT_IOCSPEED (0x00000200) ++#define MPI2_IOUNITPAGE7_PMCAP_HOST_50_0_PCT_IOCSPEED (0x00000100) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_12_5_PCT_IOCSPEED (0x00000040) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_25_0_PCT_IOCSPEED (0x00000020) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_50_0_PCT_IOCSPEED (0x00000010) ++#define MPI2_IOUNITPAGE7_PMCAP_HOST_WIDTH_CHANGE_PCIE (0x00000008) ++#define MPI2_IOUNITPAGE7_PMCAP_HOST_SPEED_CHANGE_PCIE (0x00000004) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_WIDTH_CHANGE_PCIE (0x00000002) ++#define MPI25_IOUNITPAGE7_PMCAP_IO_SPEED_CHANGE_PCIE (0x00000001) ++ ++/*obsolete names for the PowerManagementCapabilities bits (above) */ ++#define MPI2_IOUNITPAGE7_PMCAP_12_5_PCT_IOCSPEED (0x00000400) ++#define MPI2_IOUNITPAGE7_PMCAP_25_0_PCT_IOCSPEED (0x00000200) ++#define MPI2_IOUNITPAGE7_PMCAP_50_0_PCT_IOCSPEED (0x00000100) ++#define MPI2_IOUNITPAGE7_PMCAP_PCIE_WIDTH_CHANGE (0x00000008) /*obsolete */ ++#define MPI2_IOUNITPAGE7_PMCAP_PCIE_SPEED_CHANGE (0x00000004) /*obsolete */ ++ ++ ++/*defines for IO Unit Page 7 IOCTemperatureUnits field */ ++#define MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT (0x00) ++#define MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT (0x01) ++#define MPI2_IOUNITPAGE7_IOC_TEMP_CELSIUS (0x02) ++ ++/*defines for IO Unit Page 7 IOCSpeed field */ ++#define MPI2_IOUNITPAGE7_IOC_SPEED_FULL (0x01) ++#define MPI2_IOUNITPAGE7_IOC_SPEED_HALF (0x02) ++#define MPI2_IOUNITPAGE7_IOC_SPEED_QUARTER (0x04) ++#define MPI2_IOUNITPAGE7_IOC_SPEED_EIGHTH (0x08) ++ ++/*defines for IO Unit Page 7 BoardTemperatureUnits field */ ++#define MPI2_IOUNITPAGE7_BOARD_TEMP_NOT_PRESENT (0x00) ++#define MPI2_IOUNITPAGE7_BOARD_TEMP_FAHRENHEIT (0x01) ++#define MPI2_IOUNITPAGE7_BOARD_TEMP_CELSIUS (0x02) ++ ++/* defines for IO Unit Page 7 Flags field */ ++#define MPI2_IOUNITPAGE7_FLAG_CABLE_POWER_EXC (0x01) ++ ++/*IO Unit Page 8 */ ++ ++#define MPI2_IOUNIT8_NUM_THRESHOLDS (4) ++ ++typedef struct _MPI2_IOUNIT8_SENSOR { ++ U16 Flags; /*0x00 */ ++ U16 Reserved1; /*0x02 */ ++ U16 ++ Threshold[MPI2_IOUNIT8_NUM_THRESHOLDS]; /*0x04 */ ++ U32 Reserved2; /*0x0C */ ++ U32 Reserved3; /*0x10 */ ++ U32 Reserved4; /*0x14 */ ++} MPI2_IOUNIT8_SENSOR, *PTR_MPI2_IOUNIT8_SENSOR, ++ Mpi2IOUnit8Sensor_t, *pMpi2IOUnit8Sensor_t; ++ ++/*defines for IO Unit Page 8 Sensor Flags field */ ++#define MPI2_IOUNIT8_SENSOR_FLAGS_T3_ENABLE (0x0008) ++#define MPI2_IOUNIT8_SENSOR_FLAGS_T2_ENABLE (0x0004) ++#define MPI2_IOUNIT8_SENSOR_FLAGS_T1_ENABLE (0x0002) ++#define MPI2_IOUNIT8_SENSOR_FLAGS_T0_ENABLE (0x0001) ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumSensors at runtime. ++ */ ++#ifndef MPI2_IOUNITPAGE8_SENSOR_ENTRIES ++#define MPI2_IOUNITPAGE8_SENSOR_ENTRIES (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_8 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 Reserved2; /*0x08 */ ++ U8 NumSensors; /*0x0C */ ++ U8 PollingInterval; /*0x0D */ ++ U16 Reserved3; /*0x0E */ ++ MPI2_IOUNIT8_SENSOR ++ Sensor[MPI2_IOUNITPAGE8_SENSOR_ENTRIES];/*0x10 */ ++} MPI2_CONFIG_PAGE_IO_UNIT_8, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_8, ++ Mpi2IOUnitPage8_t, *pMpi2IOUnitPage8_t; ++ ++#define MPI2_IOUNITPAGE8_PAGEVERSION (0x00) ++ ++ ++/*IO Unit Page 9 */ ++ ++typedef struct _MPI2_IOUNIT9_SENSOR { ++ U16 CurrentTemperature; /*0x00 */ ++ U16 Reserved1; /*0x02 */ ++ U8 Flags; /*0x04 */ ++ U8 Reserved2; /*0x05 */ ++ U16 Reserved3; /*0x06 */ ++ U32 Reserved4; /*0x08 */ ++ U32 Reserved5; /*0x0C */ ++} MPI2_IOUNIT9_SENSOR, *PTR_MPI2_IOUNIT9_SENSOR, ++ Mpi2IOUnit9Sensor_t, *pMpi2IOUnit9Sensor_t; ++ ++/*defines for IO Unit Page 9 Sensor Flags field */ ++#define MPI2_IOUNIT9_SENSOR_FLAGS_TEMP_VALID (0x01) ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumSensors at runtime. ++ */ ++#ifndef MPI2_IOUNITPAGE9_SENSOR_ENTRIES ++#define MPI2_IOUNITPAGE9_SENSOR_ENTRIES (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_9 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 Reserved2; /*0x08 */ ++ U8 NumSensors; /*0x0C */ ++ U8 Reserved4; /*0x0D */ ++ U16 Reserved3; /*0x0E */ ++ MPI2_IOUNIT9_SENSOR ++ Sensor[MPI2_IOUNITPAGE9_SENSOR_ENTRIES];/*0x10 */ ++} MPI2_CONFIG_PAGE_IO_UNIT_9, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_9, ++ Mpi2IOUnitPage9_t, *pMpi2IOUnitPage9_t; ++ ++#define MPI2_IOUNITPAGE9_PAGEVERSION (0x00) ++ ++ ++/*IO Unit Page 10 */ ++ ++typedef struct _MPI2_IOUNIT10_FUNCTION { ++ U8 CreditPercent; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++} MPI2_IOUNIT10_FUNCTION, ++ *PTR_MPI2_IOUNIT10_FUNCTION, ++ Mpi2IOUnit10Function_t, ++ *pMpi2IOUnit10Function_t; ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumFunctions at runtime. ++ */ ++#ifndef MPI2_IOUNITPAGE10_FUNCTION_ENTRIES ++#define MPI2_IOUNITPAGE10_FUNCTION_ENTRIES (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_10 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 NumFunctions; /*0x04 */ ++ U8 Reserved1; /*0x05 */ ++ U16 Reserved2; /*0x06 */ ++ U32 Reserved3; /*0x08 */ ++ U32 Reserved4; /*0x0C */ ++ MPI2_IOUNIT10_FUNCTION ++ Function[MPI2_IOUNITPAGE10_FUNCTION_ENTRIES];/*0x10 */ ++} MPI2_CONFIG_PAGE_IO_UNIT_10, ++ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_10, ++ Mpi2IOUnitPage10_t, *pMpi2IOUnitPage10_t; ++ ++#define MPI2_IOUNITPAGE10_PAGEVERSION (0x01) ++ ++ ++/* IO Unit Page 11 (for MPI v2.6 and later) */ ++ ++typedef struct _MPI26_IOUNIT11_SPINUP_GROUP { ++ U8 MaxTargetSpinup; /* 0x00 */ ++ U8 SpinupDelay; /* 0x01 */ ++ U8 SpinupFlags; /* 0x02 */ ++ U8 Reserved1; /* 0x03 */ ++} MPI26_IOUNIT11_SPINUP_GROUP, ++ *PTR_MPI26_IOUNIT11_SPINUP_GROUP, ++ Mpi26IOUnit11SpinupGroup_t, ++ *pMpi26IOUnit11SpinupGroup_t; ++ ++/* defines for IO Unit Page 11 SpinupFlags */ ++#define MPI26_IOUNITPAGE11_SPINUP_DISABLE_FLAG (0x01) ++ ++ ++/* ++ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ * four and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI26_IOUNITPAGE11_PHY_MAX ++#define MPI26_IOUNITPAGE11_PHY_MAX (4) ++#endif ++ ++typedef struct _MPI26_CONFIG_PAGE_IO_UNIT_11 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ MPI26_IOUNIT11_SPINUP_GROUP SpinupGroupParameters[4]; /*0x08 */ ++ U32 Reserved2; /*0x18 */ ++ U32 Reserved3; /*0x1C */ ++ U32 Reserved4; /*0x20 */ ++ U8 BootDeviceWaitTime; /*0x24 */ ++ U8 Reserved5; /*0x25 */ ++ U16 Reserved6; /*0x26 */ ++ U8 NumPhys; /*0x28 */ ++ U8 PEInitialSpinupDelay; /*0x29 */ ++ U8 PEReplyDelay; /*0x2A */ ++ U8 Flags; /*0x2B */ ++ U8 PHY[MPI26_IOUNITPAGE11_PHY_MAX];/*0x2C */ ++} MPI26_CONFIG_PAGE_IO_UNIT_11, ++ *PTR_MPI26_CONFIG_PAGE_IO_UNIT_11, ++ Mpi26IOUnitPage11_t, ++ *pMpi26IOUnitPage11_t; ++ ++#define MPI26_IOUNITPAGE11_PAGEVERSION (0x00) ++ ++/* defines for Flags field */ ++#define MPI26_IOUNITPAGE11_FLAGS_AUTO_PORTENABLE (0x01) ++ ++/* defines for PHY field */ ++#define MPI26_IOUNITPAGE11_PHY_SPINUP_GROUP_MASK (0x03) ++ ++ ++ ++ ++ ++ ++/**************************************************************************** ++* IOC Config Pages ++****************************************************************************/ ++ ++/*IOC Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IOC_0 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 Reserved2; /*0x08 */ ++ U16 VendorID; /*0x0C */ ++ U16 DeviceID; /*0x0E */ ++ U8 RevisionID; /*0x10 */ ++ U8 Reserved3; /*0x11 */ ++ U16 Reserved4; /*0x12 */ ++ U32 ClassCode; /*0x14 */ ++ U16 SubsystemVendorID; /*0x18 */ ++ U16 SubsystemID; /*0x1A */ ++} MPI2_CONFIG_PAGE_IOC_0, ++ *PTR_MPI2_CONFIG_PAGE_IOC_0, ++ Mpi2IOCPage0_t, *pMpi2IOCPage0_t; ++ ++#define MPI2_IOCPAGE0_PAGEVERSION (0x02) ++ ++ ++/*IOC Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IOC_1 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Flags; /*0x04 */ ++ U32 CoalescingTimeout; /*0x08 */ ++ U8 CoalescingDepth; /*0x0C */ ++ U8 PCISlotNum; /*0x0D */ ++ U8 PCIBusNum; /*0x0E */ ++ U8 PCIDomainSegment; /*0x0F */ ++ U32 Reserved1; /*0x10 */ ++ U32 Reserved2; /*0x14 */ ++} MPI2_CONFIG_PAGE_IOC_1, ++ *PTR_MPI2_CONFIG_PAGE_IOC_1, ++ Mpi2IOCPage1_t, *pMpi2IOCPage1_t; ++ ++#define MPI2_IOCPAGE1_PAGEVERSION (0x05) ++ ++/*defines for IOC Page 1 Flags field */ ++#define MPI2_IOCPAGE1_REPLY_COALESCING (0x00000001) ++ ++#define MPI2_IOCPAGE1_PCISLOTNUM_UNKNOWN (0xFF) ++#define MPI2_IOCPAGE1_PCIBUSNUM_UNKNOWN (0xFF) ++#define MPI2_IOCPAGE1_PCIDOMAIN_UNKNOWN (0xFF) ++ ++/*IOC Page 6 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IOC_6 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 ++ CapabilitiesFlags; /*0x04 */ ++ U8 MaxDrivesRAID0; /*0x08 */ ++ U8 MaxDrivesRAID1; /*0x09 */ ++ U8 ++ MaxDrivesRAID1E; /*0x0A */ ++ U8 ++ MaxDrivesRAID10; /*0x0B */ ++ U8 MinDrivesRAID0; /*0x0C */ ++ U8 MinDrivesRAID1; /*0x0D */ ++ U8 ++ MinDrivesRAID1E; /*0x0E */ ++ U8 ++ MinDrivesRAID10; /*0x0F */ ++ U32 Reserved1; /*0x10 */ ++ U8 ++ MaxGlobalHotSpares; /*0x14 */ ++ U8 MaxPhysDisks; /*0x15 */ ++ U8 MaxVolumes; /*0x16 */ ++ U8 MaxConfigs; /*0x17 */ ++ U8 MaxOCEDisks; /*0x18 */ ++ U8 Reserved2; /*0x19 */ ++ U16 Reserved3; /*0x1A */ ++ U32 ++ SupportedStripeSizeMapRAID0; /*0x1C */ ++ U32 ++ SupportedStripeSizeMapRAID1E; /*0x20 */ ++ U32 ++ SupportedStripeSizeMapRAID10; /*0x24 */ ++ U32 Reserved4; /*0x28 */ ++ U32 Reserved5; /*0x2C */ ++ U16 ++ DefaultMetadataSize; /*0x30 */ ++ U16 Reserved6; /*0x32 */ ++ U16 ++ MaxBadBlockTableEntries; /*0x34 */ ++ U16 Reserved7; /*0x36 */ ++ U32 ++ IRNvsramVersion; /*0x38 */ ++} MPI2_CONFIG_PAGE_IOC_6, ++ *PTR_MPI2_CONFIG_PAGE_IOC_6, ++ Mpi2IOCPage6_t, *pMpi2IOCPage6_t; ++ ++#define MPI2_IOCPAGE6_PAGEVERSION (0x05) ++ ++/*defines for IOC Page 6 CapabilitiesFlags */ ++#define MPI2_IOCPAGE6_CAP_FLAGS_4K_SECTORS_SUPPORT (0x00000020) ++#define MPI2_IOCPAGE6_CAP_FLAGS_RAID10_SUPPORT (0x00000010) ++#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1_SUPPORT (0x00000008) ++#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1E_SUPPORT (0x00000004) ++#define MPI2_IOCPAGE6_CAP_FLAGS_RAID0_SUPPORT (0x00000002) ++#define MPI2_IOCPAGE6_CAP_FLAGS_GLOBAL_HOT_SPARE (0x00000001) ++ ++ ++/*IOC Page 7 */ ++ ++#define MPI2_IOCPAGE7_EVENTMASK_WORDS (4) ++ ++typedef struct _MPI2_CONFIG_PAGE_IOC_7 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 ++ EventMasks[MPI2_IOCPAGE7_EVENTMASK_WORDS];/*0x08 */ ++ U16 SASBroadcastPrimitiveMasks; /*0x18 */ ++ U16 SASNotifyPrimitiveMasks; /*0x1A */ ++ U32 Reserved3; /*0x1C */ ++} MPI2_CONFIG_PAGE_IOC_7, ++ *PTR_MPI2_CONFIG_PAGE_IOC_7, ++ Mpi2IOCPage7_t, *pMpi2IOCPage7_t; ++ ++#define MPI2_IOCPAGE7_PAGEVERSION (0x02) ++ ++ ++/*IOC Page 8 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_IOC_8 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 NumDevsPerEnclosure; /*0x04 */ ++ U8 Reserved1; /*0x05 */ ++ U16 Reserved2; /*0x06 */ ++ U16 MaxPersistentEntries; /*0x08 */ ++ U16 MaxNumPhysicalMappedIDs; /*0x0A */ ++ U16 Flags; /*0x0C */ ++ U16 Reserved3; /*0x0E */ ++ U16 IRVolumeMappingFlags; /*0x10 */ ++ U16 Reserved4; /*0x12 */ ++ U32 Reserved5; /*0x14 */ ++} MPI2_CONFIG_PAGE_IOC_8, ++ *PTR_MPI2_CONFIG_PAGE_IOC_8, ++ Mpi2IOCPage8_t, *pMpi2IOCPage8_t; ++ ++#define MPI2_IOCPAGE8_PAGEVERSION (0x00) ++ ++/*defines for IOC Page 8 Flags field */ ++#define MPI2_IOCPAGE8_FLAGS_DA_START_SLOT_1 (0x00000020) ++#define MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0 (0x00000010) ++ ++#define MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE (0x0000000E) ++#define MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING (0x00000000) ++#define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING (0x00000002) ++ ++#define MPI2_IOCPAGE8_FLAGS_DISABLE_PERSISTENT_MAPPING (0x00000001) ++#define MPI2_IOCPAGE8_FLAGS_ENABLE_PERSISTENT_MAPPING (0x00000000) ++ ++/*defines for IOC Page 8 IRVolumeMappingFlags */ ++#define MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE (0x00000003) ++#define MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING (0x00000000) ++#define MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING (0x00000001) ++ ++ ++/**************************************************************************** ++* BIOS Config Pages ++****************************************************************************/ ++ ++/*BIOS Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_BIOS_1 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 BiosOptions; /*0x04 */ ++ U32 IOCSettings; /*0x08 */ ++ U8 SSUTimeout; /*0x0C */ ++ U8 Reserved1; /*0x0D */ ++ U16 Reserved2; /*0x0E */ ++ U32 DeviceSettings; /*0x10 */ ++ U16 NumberOfDevices; /*0x14 */ ++ U16 UEFIVersion; /*0x16 */ ++ U16 IOTimeoutBlockDevicesNonRM; /*0x18 */ ++ U16 IOTimeoutSequential; /*0x1A */ ++ U16 IOTimeoutOther; /*0x1C */ ++ U16 IOTimeoutBlockDevicesRM; /*0x1E */ ++} MPI2_CONFIG_PAGE_BIOS_1, ++ *PTR_MPI2_CONFIG_PAGE_BIOS_1, ++ Mpi2BiosPage1_t, *pMpi2BiosPage1_t; ++ ++#define MPI2_BIOSPAGE1_PAGEVERSION (0x07) ++ ++/*values for BIOS Page 1 BiosOptions field */ ++#define MPI2_BIOSPAGE1_OPTIONS_BOOT_LIST_ADD_ALT_BOOT_DEVICE (0x00008000) ++#define MPI2_BIOSPAGE1_OPTIONS_ADVANCED_CONFIG (0x00004000) ++ ++#define MPI2_BIOSPAGE1_OPTIONS_PNS_MASK (0x00003800) ++#define MPI2_BIOSPAGE1_OPTIONS_PNS_MASK (0x00003800) ++#define MPI2_BIOSPAGE1_OPTIONS_PNS_PBDHL (0x00000000) ++#define MPI2_BIOSPAGE1_OPTIONS_PNS_ENCSLOSURE (0x00000800) ++#define MPI2_BIOSPAGE1_OPTIONS_PNS_LWWID (0x00001000) ++#define MPI2_BIOSPAGE1_OPTIONS_PNS_PSENS (0x00001800) ++#define MPI2_BIOSPAGE1_OPTIONS_PNS_ESPHY (0x00002000) ++ ++#define MPI2_BIOSPAGE1_OPTIONS_X86_DISABLE_BIOS (0x00000400) ++ ++#define MPI2_BIOSPAGE1_OPTIONS_MASK_REGISTRATION_UEFI_BSD (0x00000300) ++#define MPI2_BIOSPAGE1_OPTIONS_USE_BIT0_REGISTRATION_UEFI_BSD (0x00000000) ++#define MPI2_BIOSPAGE1_OPTIONS_FULL_REGISTRATION_UEFI_BSD (0x00000100) ++#define MPI2_BIOSPAGE1_OPTIONS_ADAPTER_REGISTRATION_UEFI_BSD (0x00000200) ++#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_REGISTRATION_UEFI_BSD (0x00000300) ++ ++#define MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID (0x000000F0) ++#define MPI2_BIOSPAGE1_OPTIONS_LSI_OEM_ID (0x00000000) ++ ++#define MPI2_BIOSPAGE1_OPTIONS_MASK_UEFI_HII_REGISTRATION (0x00000006) ++#define MPI2_BIOSPAGE1_OPTIONS_ENABLE_UEFI_HII (0x00000000) ++#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_UEFI_HII (0x00000002) ++#define MPI2_BIOSPAGE1_OPTIONS_VERSION_CHECK_UEFI_HII (0x00000004) ++ ++#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_BIOS (0x00000001) ++ ++/*values for BIOS Page 1 IOCSettings field */ ++#define MPI2_BIOSPAGE1_IOCSET_MASK_BOOT_PREFERENCE (0x00030000) ++#define MPI2_BIOSPAGE1_IOCSET_ENCLOSURE_SLOT_BOOT (0x00000000) ++#define MPI2_BIOSPAGE1_IOCSET_SAS_ADDRESS_BOOT (0x00010000) ++ ++#define MPI2_BIOSPAGE1_IOCSET_MASK_RM_SETTING (0x000000C0) ++#define MPI2_BIOSPAGE1_IOCSET_NONE_RM_SETTING (0x00000000) ++#define MPI2_BIOSPAGE1_IOCSET_BOOT_RM_SETTING (0x00000040) ++#define MPI2_BIOSPAGE1_IOCSET_MEDIA_RM_SETTING (0x00000080) ++ ++#define MPI2_BIOSPAGE1_IOCSET_MASK_ADAPTER_SUPPORT (0x00000030) ++#define MPI2_BIOSPAGE1_IOCSET_NO_SUPPORT (0x00000000) ++#define MPI2_BIOSPAGE1_IOCSET_BIOS_SUPPORT (0x00000010) ++#define MPI2_BIOSPAGE1_IOCSET_OS_SUPPORT (0x00000020) ++#define MPI2_BIOSPAGE1_IOCSET_ALL_SUPPORT (0x00000030) ++ ++#define MPI2_BIOSPAGE1_IOCSET_ALTERNATE_CHS (0x00000008) ++ ++/*values for BIOS Page 1 DeviceSettings field */ ++#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SMART_POLLING (0x00000010) ++#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SEQ_LUN (0x00000008) ++#define MPI2_BIOSPAGE1_DEVSET_DISABLE_RM_LUN (0x00000004) ++#define MPI2_BIOSPAGE1_DEVSET_DISABLE_NON_RM_LUN (0x00000002) ++#define MPI2_BIOSPAGE1_DEVSET_DISABLE_OTHER_LUN (0x00000001) ++ ++/*defines for BIOS Page 1 UEFIVersion field */ ++#define MPI2_BIOSPAGE1_UEFI_VER_MAJOR_MASK (0xFF00) ++#define MPI2_BIOSPAGE1_UEFI_VER_MAJOR_SHIFT (8) ++#define MPI2_BIOSPAGE1_UEFI_VER_MINOR_MASK (0x00FF) ++#define MPI2_BIOSPAGE1_UEFI_VER_MINOR_SHIFT (0) ++ ++ ++ ++/*BIOS Page 2 */ ++ ++typedef struct _MPI2_BOOT_DEVICE_ADAPTER_ORDER { ++ U32 Reserved1; /*0x00 */ ++ U32 Reserved2; /*0x04 */ ++ U32 Reserved3; /*0x08 */ ++ U32 Reserved4; /*0x0C */ ++ U32 Reserved5; /*0x10 */ ++ U32 Reserved6; /*0x14 */ ++} MPI2_BOOT_DEVICE_ADAPTER_ORDER, ++ *PTR_MPI2_BOOT_DEVICE_ADAPTER_ORDER, ++ Mpi2BootDeviceAdapterOrder_t, ++ *pMpi2BootDeviceAdapterOrder_t; ++ ++typedef struct _MPI2_BOOT_DEVICE_SAS_WWID { ++ U64 SASAddress; /*0x00 */ ++ U8 LUN[8]; /*0x08 */ ++ U32 Reserved1; /*0x10 */ ++ U32 Reserved2; /*0x14 */ ++} MPI2_BOOT_DEVICE_SAS_WWID, ++ *PTR_MPI2_BOOT_DEVICE_SAS_WWID, ++ Mpi2BootDeviceSasWwid_t, ++ *pMpi2BootDeviceSasWwid_t; ++ ++typedef struct _MPI2_BOOT_DEVICE_ENCLOSURE_SLOT { ++ U64 EnclosureLogicalID; /*0x00 */ ++ U32 Reserved1; /*0x08 */ ++ U32 Reserved2; /*0x0C */ ++ U16 SlotNumber; /*0x10 */ ++ U16 Reserved3; /*0x12 */ ++ U32 Reserved4; /*0x14 */ ++} MPI2_BOOT_DEVICE_ENCLOSURE_SLOT, ++ *PTR_MPI2_BOOT_DEVICE_ENCLOSURE_SLOT, ++ Mpi2BootDeviceEnclosureSlot_t, ++ *pMpi2BootDeviceEnclosureSlot_t; ++ ++typedef struct _MPI2_BOOT_DEVICE_DEVICE_NAME { ++ U64 DeviceName; /*0x00 */ ++ U8 LUN[8]; /*0x08 */ ++ U32 Reserved1; /*0x10 */ ++ U32 Reserved2; /*0x14 */ ++} MPI2_BOOT_DEVICE_DEVICE_NAME, ++ *PTR_MPI2_BOOT_DEVICE_DEVICE_NAME, ++ Mpi2BootDeviceDeviceName_t, ++ *pMpi2BootDeviceDeviceName_t; ++ ++typedef union _MPI2_MPI2_BIOSPAGE2_BOOT_DEVICE { ++ MPI2_BOOT_DEVICE_ADAPTER_ORDER AdapterOrder; ++ MPI2_BOOT_DEVICE_SAS_WWID SasWwid; ++ MPI2_BOOT_DEVICE_ENCLOSURE_SLOT EnclosureSlot; ++ MPI2_BOOT_DEVICE_DEVICE_NAME DeviceName; ++} MPI2_BIOSPAGE2_BOOT_DEVICE, ++ *PTR_MPI2_BIOSPAGE2_BOOT_DEVICE, ++ Mpi2BiosPage2BootDevice_t, ++ *pMpi2BiosPage2BootDevice_t; ++ ++typedef struct _MPI2_CONFIG_PAGE_BIOS_2 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 Reserved2; /*0x08 */ ++ U32 Reserved3; /*0x0C */ ++ U32 Reserved4; /*0x10 */ ++ U32 Reserved5; /*0x14 */ ++ U32 Reserved6; /*0x18 */ ++ U8 ReqBootDeviceForm; /*0x1C */ ++ U8 Reserved7; /*0x1D */ ++ U16 Reserved8; /*0x1E */ ++ MPI2_BIOSPAGE2_BOOT_DEVICE RequestedBootDevice; /*0x20 */ ++ U8 ReqAltBootDeviceForm; /*0x38 */ ++ U8 Reserved9; /*0x39 */ ++ U16 Reserved10; /*0x3A */ ++ MPI2_BIOSPAGE2_BOOT_DEVICE RequestedAltBootDevice; /*0x3C */ ++ U8 CurrentBootDeviceForm; /*0x58 */ ++ U8 Reserved11; /*0x59 */ ++ U16 Reserved12; /*0x5A */ ++ MPI2_BIOSPAGE2_BOOT_DEVICE CurrentBootDevice; /*0x58 */ ++} MPI2_CONFIG_PAGE_BIOS_2, *PTR_MPI2_CONFIG_PAGE_BIOS_2, ++ Mpi2BiosPage2_t, *pMpi2BiosPage2_t; ++ ++#define MPI2_BIOSPAGE2_PAGEVERSION (0x04) ++ ++/*values for BIOS Page 2 BootDeviceForm fields */ ++#define MPI2_BIOSPAGE2_FORM_MASK (0x0F) ++#define MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED (0x00) ++#define MPI2_BIOSPAGE2_FORM_SAS_WWID (0x05) ++#define MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT (0x06) ++#define MPI2_BIOSPAGE2_FORM_DEVICE_NAME (0x07) ++ ++ ++/*BIOS Page 3 */ ++ ++#define MPI2_BIOSPAGE3_NUM_ADAPTER (4) ++ ++typedef struct _MPI2_ADAPTER_INFO { ++ U8 PciBusNumber; /*0x00 */ ++ U8 PciDeviceAndFunctionNumber; /*0x01 */ ++ U16 AdapterFlags; /*0x02 */ ++} MPI2_ADAPTER_INFO, *PTR_MPI2_ADAPTER_INFO, ++ Mpi2AdapterInfo_t, *pMpi2AdapterInfo_t; ++ ++#define MPI2_ADAPTER_INFO_FLAGS_EMBEDDED (0x0001) ++#define MPI2_ADAPTER_INFO_FLAGS_INIT_STATUS (0x0002) ++ ++typedef struct _MPI2_ADAPTER_ORDER_AUX { ++ U64 WWID; /* 0x00 */ ++ U32 Reserved1; /* 0x08 */ ++ U32 Reserved2; /* 0x0C */ ++} MPI2_ADAPTER_ORDER_AUX, *PTR_MPI2_ADAPTER_ORDER_AUX, ++ Mpi2AdapterOrderAux_t, *pMpi2AdapterOrderAux_t; ++ ++ ++typedef struct _MPI2_CONFIG_PAGE_BIOS_3 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U32 GlobalFlags; /*0x04 */ ++ U32 BiosVersion; /*0x08 */ ++ MPI2_ADAPTER_INFO AdapterOrder[MPI2_BIOSPAGE3_NUM_ADAPTER]; ++ U32 Reserved1; /*0x1C */ ++ MPI2_ADAPTER_ORDER_AUX AdapterOrderAux[MPI2_BIOSPAGE3_NUM_ADAPTER]; ++} MPI2_CONFIG_PAGE_BIOS_3, ++ *PTR_MPI2_CONFIG_PAGE_BIOS_3, ++ Mpi2BiosPage3_t, *pMpi2BiosPage3_t; ++ ++#define MPI2_BIOSPAGE3_PAGEVERSION (0x01) ++ ++/*values for BIOS Page 3 GlobalFlags */ ++#define MPI2_BIOSPAGE3_FLAGS_PAUSE_ON_ERROR (0x00000002) ++#define MPI2_BIOSPAGE3_FLAGS_VERBOSE_ENABLE (0x00000004) ++#define MPI2_BIOSPAGE3_FLAGS_HOOK_INT_40_DISABLE (0x00000010) ++ ++#define MPI2_BIOSPAGE3_FLAGS_DEV_LIST_DISPLAY_MASK (0x000000E0) ++#define MPI2_BIOSPAGE3_FLAGS_INSTALLED_DEV_DISPLAY (0x00000000) ++#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DISPLAY (0x00000020) ++#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DEV_DISPLAY (0x00000040) ++ ++ ++/*BIOS Page 4 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI2_BIOS_PAGE_4_PHY_ENTRIES ++#define MPI2_BIOS_PAGE_4_PHY_ENTRIES (1) ++#endif ++ ++typedef struct _MPI2_BIOS4_ENTRY { ++ U64 ReassignmentWWID; /*0x00 */ ++ U64 ReassignmentDeviceName; /*0x08 */ ++} MPI2_BIOS4_ENTRY, *PTR_MPI2_BIOS4_ENTRY, ++ Mpi2MBios4Entry_t, *pMpi2Bios4Entry_t; ++ ++typedef struct _MPI2_CONFIG_PAGE_BIOS_4 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 NumPhys; /*0x04 */ ++ U8 Reserved1; /*0x05 */ ++ U16 Reserved2; /*0x06 */ ++ MPI2_BIOS4_ENTRY ++ Phy[MPI2_BIOS_PAGE_4_PHY_ENTRIES]; /*0x08 */ ++} MPI2_CONFIG_PAGE_BIOS_4, *PTR_MPI2_CONFIG_PAGE_BIOS_4, ++ Mpi2BiosPage4_t, *pMpi2BiosPage4_t; ++ ++#define MPI2_BIOSPAGE4_PAGEVERSION (0x01) ++ ++ ++/**************************************************************************** ++* RAID Volume Config Pages ++****************************************************************************/ ++ ++/*RAID Volume Page 0 */ ++ ++typedef struct _MPI2_RAIDVOL0_PHYS_DISK { ++ U8 RAIDSetNum; /*0x00 */ ++ U8 PhysDiskMap; /*0x01 */ ++ U8 PhysDiskNum; /*0x02 */ ++ U8 Reserved; /*0x03 */ ++} MPI2_RAIDVOL0_PHYS_DISK, *PTR_MPI2_RAIDVOL0_PHYS_DISK, ++ Mpi2RaidVol0PhysDisk_t, *pMpi2RaidVol0PhysDisk_t; ++ ++/*defines for the PhysDiskMap field */ ++#define MPI2_RAIDVOL0_PHYSDISK_PRIMARY (0x01) ++#define MPI2_RAIDVOL0_PHYSDISK_SECONDARY (0x02) ++ ++typedef struct _MPI2_RAIDVOL0_SETTINGS { ++ U16 Settings; /*0x00 */ ++ U8 HotSparePool; /*0x01 */ ++ U8 Reserved; /*0x02 */ ++} MPI2_RAIDVOL0_SETTINGS, *PTR_MPI2_RAIDVOL0_SETTINGS, ++ Mpi2RaidVol0Settings_t, ++ *pMpi2RaidVol0Settings_t; ++ ++/*RAID Volume Page 0 HotSparePool defines, also used in RAID Physical Disk */ ++#define MPI2_RAID_HOT_SPARE_POOL_0 (0x01) ++#define MPI2_RAID_HOT_SPARE_POOL_1 (0x02) ++#define MPI2_RAID_HOT_SPARE_POOL_2 (0x04) ++#define MPI2_RAID_HOT_SPARE_POOL_3 (0x08) ++#define MPI2_RAID_HOT_SPARE_POOL_4 (0x10) ++#define MPI2_RAID_HOT_SPARE_POOL_5 (0x20) ++#define MPI2_RAID_HOT_SPARE_POOL_6 (0x40) ++#define MPI2_RAID_HOT_SPARE_POOL_7 (0x80) ++ ++/*RAID Volume Page 0 VolumeSettings defines */ ++#define MPI2_RAIDVOL0_SETTING_USE_PRODUCT_ID_SUFFIX (0x0008) ++#define MPI2_RAIDVOL0_SETTING_AUTO_CONFIG_HSWAP_DISABLE (0x0004) ++ ++#define MPI2_RAIDVOL0_SETTING_MASK_WRITE_CACHING (0x0003) ++#define MPI2_RAIDVOL0_SETTING_UNCHANGED (0x0000) ++#define MPI2_RAIDVOL0_SETTING_DISABLE_WRITE_CACHING (0x0001) ++#define MPI2_RAIDVOL0_SETTING_ENABLE_WRITE_CACHING (0x0002) ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhysDisks at runtime. ++ */ ++#ifndef MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX ++#define MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_0 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U16 DevHandle; /*0x04 */ ++ U8 VolumeState; /*0x06 */ ++ U8 VolumeType; /*0x07 */ ++ U32 VolumeStatusFlags; /*0x08 */ ++ MPI2_RAIDVOL0_SETTINGS VolumeSettings; /*0x0C */ ++ U64 MaxLBA; /*0x10 */ ++ U32 StripeSize; /*0x18 */ ++ U16 BlockSize; /*0x1C */ ++ U16 Reserved1; /*0x1E */ ++ U8 SupportedPhysDisks;/*0x20 */ ++ U8 ResyncRate; /*0x21 */ ++ U16 DataScrubDuration; /*0x22 */ ++ U8 NumPhysDisks; /*0x24 */ ++ U8 Reserved2; /*0x25 */ ++ U8 Reserved3; /*0x26 */ ++ U8 InactiveStatus; /*0x27 */ ++ MPI2_RAIDVOL0_PHYS_DISK ++ PhysDisk[MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX]; /*0x28 */ ++} MPI2_CONFIG_PAGE_RAID_VOL_0, ++ *PTR_MPI2_CONFIG_PAGE_RAID_VOL_0, ++ Mpi2RaidVolPage0_t, *pMpi2RaidVolPage0_t; ++ ++#define MPI2_RAIDVOLPAGE0_PAGEVERSION (0x0A) ++ ++/*values for RAID VolumeState */ ++#define MPI2_RAID_VOL_STATE_MISSING (0x00) ++#define MPI2_RAID_VOL_STATE_FAILED (0x01) ++#define MPI2_RAID_VOL_STATE_INITIALIZING (0x02) ++#define MPI2_RAID_VOL_STATE_ONLINE (0x03) ++#define MPI2_RAID_VOL_STATE_DEGRADED (0x04) ++#define MPI2_RAID_VOL_STATE_OPTIMAL (0x05) ++ ++/*values for RAID VolumeType */ ++#define MPI2_RAID_VOL_TYPE_RAID0 (0x00) ++#define MPI2_RAID_VOL_TYPE_RAID1E (0x01) ++#define MPI2_RAID_VOL_TYPE_RAID1 (0x02) ++#define MPI2_RAID_VOL_TYPE_RAID10 (0x05) ++#define MPI2_RAID_VOL_TYPE_UNKNOWN (0xFF) ++ ++/*values for RAID Volume Page 0 VolumeStatusFlags field */ ++#define MPI2_RAIDVOL0_STATUS_FLAG_PENDING_RESYNC (0x02000000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_BACKG_INIT_PENDING (0x01000000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_MDC_PENDING (0x00800000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_USER_CONSIST_PENDING (0x00400000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_MAKE_DATA_CONSISTENT (0x00200000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_DATA_SCRUB (0x00100000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_CONSISTENCY_CHECK (0x00080000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_CAPACITY_EXPANSION (0x00040000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_BACKGROUND_INIT (0x00020000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS (0x00010000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT (0x00000080) ++#define MPI2_RAIDVOL0_STATUS_FLAG_OCE_ALLOWED (0x00000040) ++#define MPI2_RAIDVOL0_STATUS_FLAG_BGI_COMPLETE (0x00000020) ++#define MPI2_RAIDVOL0_STATUS_FLAG_1E_OFFSET_MIRROR (0x00000000) ++#define MPI2_RAIDVOL0_STATUS_FLAG_1E_ADJACENT_MIRROR (0x00000010) ++#define MPI2_RAIDVOL0_STATUS_FLAG_BAD_BLOCK_TABLE_FULL (0x00000008) ++#define MPI2_RAIDVOL0_STATUS_FLAG_VOLUME_INACTIVE (0x00000004) ++#define MPI2_RAIDVOL0_STATUS_FLAG_QUIESCED (0x00000002) ++#define MPI2_RAIDVOL0_STATUS_FLAG_ENABLED (0x00000001) ++ ++/*values for RAID Volume Page 0 SupportedPhysDisks field */ ++#define MPI2_RAIDVOL0_SUPPORT_SOLID_STATE_DISKS (0x08) ++#define MPI2_RAIDVOL0_SUPPORT_HARD_DISKS (0x04) ++#define MPI2_RAIDVOL0_SUPPORT_SAS_PROTOCOL (0x02) ++#define MPI2_RAIDVOL0_SUPPORT_SATA_PROTOCOL (0x01) ++ ++/*values for RAID Volume Page 0 InactiveStatus field */ ++#define MPI2_RAIDVOLPAGE0_UNKNOWN_INACTIVE (0x00) ++#define MPI2_RAIDVOLPAGE0_STALE_METADATA_INACTIVE (0x01) ++#define MPI2_RAIDVOLPAGE0_FOREIGN_VOLUME_INACTIVE (0x02) ++#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_RESOURCE_INACTIVE (0x03) ++#define MPI2_RAIDVOLPAGE0_CLONE_VOLUME_INACTIVE (0x04) ++#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_METADATA_INACTIVE (0x05) ++#define MPI2_RAIDVOLPAGE0_PREVIOUSLY_DELETED (0x06) ++ ++ ++/*RAID Volume Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_1 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U16 DevHandle; /*0x04 */ ++ U16 Reserved0; /*0x06 */ ++ U8 GUID[24]; /*0x08 */ ++ U8 Name[16]; /*0x20 */ ++ U64 WWID; /*0x30 */ ++ U32 Reserved1; /*0x38 */ ++ U32 Reserved2; /*0x3C */ ++} MPI2_CONFIG_PAGE_RAID_VOL_1, ++ *PTR_MPI2_CONFIG_PAGE_RAID_VOL_1, ++ Mpi2RaidVolPage1_t, *pMpi2RaidVolPage1_t; ++ ++#define MPI2_RAIDVOLPAGE1_PAGEVERSION (0x03) ++ ++ ++/**************************************************************************** ++* RAID Physical Disk Config Pages ++****************************************************************************/ ++ ++/*RAID Physical Disk Page 0 */ ++ ++typedef struct _MPI2_RAIDPHYSDISK0_SETTINGS { ++ U16 Reserved1; /*0x00 */ ++ U8 HotSparePool; /*0x02 */ ++ U8 Reserved2; /*0x03 */ ++} MPI2_RAIDPHYSDISK0_SETTINGS, ++ *PTR_MPI2_RAIDPHYSDISK0_SETTINGS, ++ Mpi2RaidPhysDisk0Settings_t, ++ *pMpi2RaidPhysDisk0Settings_t; ++ ++/*use MPI2_RAID_HOT_SPARE_POOL_ defines for the HotSparePool field */ ++ ++typedef struct _MPI2_RAIDPHYSDISK0_INQUIRY_DATA { ++ U8 VendorID[8]; /*0x00 */ ++ U8 ProductID[16]; /*0x08 */ ++ U8 ProductRevLevel[4]; /*0x18 */ ++ U8 SerialNum[32]; /*0x1C */ ++} MPI2_RAIDPHYSDISK0_INQUIRY_DATA, ++ *PTR_MPI2_RAIDPHYSDISK0_INQUIRY_DATA, ++ Mpi2RaidPhysDisk0InquiryData_t, ++ *pMpi2RaidPhysDisk0InquiryData_t; ++ ++typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_0 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U16 DevHandle; /*0x04 */ ++ U8 Reserved1; /*0x06 */ ++ U8 PhysDiskNum; /*0x07 */ ++ MPI2_RAIDPHYSDISK0_SETTINGS PhysDiskSettings; /*0x08 */ ++ U32 Reserved2; /*0x0C */ ++ MPI2_RAIDPHYSDISK0_INQUIRY_DATA InquiryData; /*0x10 */ ++ U32 Reserved3; /*0x4C */ ++ U8 PhysDiskState; /*0x50 */ ++ U8 OfflineReason; /*0x51 */ ++ U8 IncompatibleReason; /*0x52 */ ++ U8 PhysDiskAttributes; /*0x53 */ ++ U32 PhysDiskStatusFlags;/*0x54 */ ++ U64 DeviceMaxLBA; /*0x58 */ ++ U64 HostMaxLBA; /*0x60 */ ++ U64 CoercedMaxLBA; /*0x68 */ ++ U16 BlockSize; /*0x70 */ ++ U16 Reserved5; /*0x72 */ ++ U32 Reserved6; /*0x74 */ ++} MPI2_CONFIG_PAGE_RD_PDISK_0, ++ *PTR_MPI2_CONFIG_PAGE_RD_PDISK_0, ++ Mpi2RaidPhysDiskPage0_t, ++ *pMpi2RaidPhysDiskPage0_t; ++ ++#define MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION (0x05) ++ ++/*PhysDiskState defines */ ++#define MPI2_RAID_PD_STATE_NOT_CONFIGURED (0x00) ++#define MPI2_RAID_PD_STATE_NOT_COMPATIBLE (0x01) ++#define MPI2_RAID_PD_STATE_OFFLINE (0x02) ++#define MPI2_RAID_PD_STATE_ONLINE (0x03) ++#define MPI2_RAID_PD_STATE_HOT_SPARE (0x04) ++#define MPI2_RAID_PD_STATE_DEGRADED (0x05) ++#define MPI2_RAID_PD_STATE_REBUILDING (0x06) ++#define MPI2_RAID_PD_STATE_OPTIMAL (0x07) ++ ++/*OfflineReason defines */ ++#define MPI2_PHYSDISK0_ONLINE (0x00) ++#define MPI2_PHYSDISK0_OFFLINE_MISSING (0x01) ++#define MPI2_PHYSDISK0_OFFLINE_FAILED (0x03) ++#define MPI2_PHYSDISK0_OFFLINE_INITIALIZING (0x04) ++#define MPI2_PHYSDISK0_OFFLINE_REQUESTED (0x05) ++#define MPI2_PHYSDISK0_OFFLINE_FAILED_REQUESTED (0x06) ++#define MPI2_PHYSDISK0_OFFLINE_OTHER (0xFF) ++ ++/*IncompatibleReason defines */ ++#define MPI2_PHYSDISK0_COMPATIBLE (0x00) ++#define MPI2_PHYSDISK0_INCOMPATIBLE_PROTOCOL (0x01) ++#define MPI2_PHYSDISK0_INCOMPATIBLE_BLOCKSIZE (0x02) ++#define MPI2_PHYSDISK0_INCOMPATIBLE_MAX_LBA (0x03) ++#define MPI2_PHYSDISK0_INCOMPATIBLE_SATA_EXTENDED_CMD (0x04) ++#define MPI2_PHYSDISK0_INCOMPATIBLE_REMOVEABLE_MEDIA (0x05) ++#define MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE (0x06) ++#define MPI2_PHYSDISK0_INCOMPATIBLE_UNKNOWN (0xFF) ++ ++/*PhysDiskAttributes defines */ ++#define MPI2_PHYSDISK0_ATTRIB_MEDIA_MASK (0x0C) ++#define MPI2_PHYSDISK0_ATTRIB_SOLID_STATE_DRIVE (0x08) ++#define MPI2_PHYSDISK0_ATTRIB_HARD_DISK_DRIVE (0x04) ++ ++#define MPI2_PHYSDISK0_ATTRIB_PROTOCOL_MASK (0x03) ++#define MPI2_PHYSDISK0_ATTRIB_SAS_PROTOCOL (0x02) ++#define MPI2_PHYSDISK0_ATTRIB_SATA_PROTOCOL (0x01) ++ ++/*PhysDiskStatusFlags defines */ ++#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED (0x00000040) ++#define MPI2_PHYSDISK0_STATUS_FLAG_OCE_TARGET (0x00000020) ++#define MPI2_PHYSDISK0_STATUS_FLAG_WRITE_CACHE_ENABLED (0x00000010) ++#define MPI2_PHYSDISK0_STATUS_FLAG_OPTIMAL_PREVIOUS (0x00000000) ++#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_OPTIMAL_PREVIOUS (0x00000008) ++#define MPI2_PHYSDISK0_STATUS_FLAG_INACTIVE_VOLUME (0x00000004) ++#define MPI2_PHYSDISK0_STATUS_FLAG_QUIESCED (0x00000002) ++#define MPI2_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC (0x00000001) ++ ++ ++/*RAID Physical Disk Page 1 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhysDiskPaths at runtime. ++ */ ++#ifndef MPI2_RAID_PHYS_DISK1_PATH_MAX ++#define MPI2_RAID_PHYS_DISK1_PATH_MAX (1) ++#endif ++ ++typedef struct _MPI2_RAIDPHYSDISK1_PATH { ++ U16 DevHandle; /*0x00 */ ++ U16 Reserved1; /*0x02 */ ++ U64 WWID; /*0x04 */ ++ U64 OwnerWWID; /*0x0C */ ++ U8 OwnerIdentifier; /*0x14 */ ++ U8 Reserved2; /*0x15 */ ++ U16 Flags; /*0x16 */ ++} MPI2_RAIDPHYSDISK1_PATH, *PTR_MPI2_RAIDPHYSDISK1_PATH, ++ Mpi2RaidPhysDisk1Path_t, ++ *pMpi2RaidPhysDisk1Path_t; ++ ++/*RAID Physical Disk Page 1 Physical Disk Path Flags field defines */ ++#define MPI2_RAID_PHYSDISK1_FLAG_PRIMARY (0x0004) ++#define MPI2_RAID_PHYSDISK1_FLAG_BROKEN (0x0002) ++#define MPI2_RAID_PHYSDISK1_FLAG_INVALID (0x0001) ++ ++typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1 { ++ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ ++ U8 NumPhysDiskPaths; /*0x04 */ ++ U8 PhysDiskNum; /*0x05 */ ++ U16 Reserved1; /*0x06 */ ++ U32 Reserved2; /*0x08 */ ++ MPI2_RAIDPHYSDISK1_PATH ++ PhysicalDiskPath[MPI2_RAID_PHYS_DISK1_PATH_MAX];/*0x0C */ ++} MPI2_CONFIG_PAGE_RD_PDISK_1, ++ *PTR_MPI2_CONFIG_PAGE_RD_PDISK_1, ++ Mpi2RaidPhysDiskPage1_t, ++ *pMpi2RaidPhysDiskPage1_t; ++ ++#define MPI2_RAIDPHYSDISKPAGE1_PAGEVERSION (0x02) ++ ++ ++/**************************************************************************** ++* values for fields used by several types of SAS Config Pages ++****************************************************************************/ ++ ++/*values for NegotiatedLinkRates fields */ ++#define MPI2_SAS_NEG_LINK_RATE_MASK_LOGICAL (0xF0) ++#define MPI2_SAS_NEG_LINK_RATE_SHIFT_LOGICAL (4) ++#define MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL (0x0F) ++/*link rates used for Negotiated Physical and Logical Link Rate */ ++#define MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE (0x00) ++#define MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED (0x01) ++#define MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED (0x02) ++#define MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE (0x03) ++#define MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR (0x04) ++#define MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS (0x05) ++#define MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY (0x06) ++#define MPI2_SAS_NEG_LINK_RATE_1_5 (0x08) ++#define MPI2_SAS_NEG_LINK_RATE_3_0 (0x09) ++#define MPI2_SAS_NEG_LINK_RATE_6_0 (0x0A) ++#define MPI25_SAS_NEG_LINK_RATE_12_0 (0x0B) ++ ++ ++/*values for AttachedPhyInfo fields */ ++#define MPI2_SAS_APHYINFO_INSIDE_ZPSDS_PERSISTENT (0x00000040) ++#define MPI2_SAS_APHYINFO_REQUESTED_INSIDE_ZPSDS (0x00000020) ++#define MPI2_SAS_APHYINFO_BREAK_REPLY_CAPABLE (0x00000010) ++ ++#define MPI2_SAS_APHYINFO_REASON_MASK (0x0000000F) ++#define MPI2_SAS_APHYINFO_REASON_UNKNOWN (0x00000000) ++#define MPI2_SAS_APHYINFO_REASON_POWER_ON (0x00000001) ++#define MPI2_SAS_APHYINFO_REASON_HARD_RESET (0x00000002) ++#define MPI2_SAS_APHYINFO_REASON_SMP_PHY_CONTROL (0x00000003) ++#define MPI2_SAS_APHYINFO_REASON_LOSS_OF_SYNC (0x00000004) ++#define MPI2_SAS_APHYINFO_REASON_MULTIPLEXING_SEQ (0x00000005) ++#define MPI2_SAS_APHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00000006) ++#define MPI2_SAS_APHYINFO_REASON_BREAK_TIMEOUT (0x00000007) ++#define MPI2_SAS_APHYINFO_REASON_PHY_TEST_STOPPED (0x00000008) ++ ++ ++/*values for PhyInfo fields */ ++#define MPI2_SAS_PHYINFO_PHY_VACANT (0x80000000) ++ ++#define MPI2_SAS_PHYINFO_PHY_POWER_CONDITION_MASK (0x18000000) ++#define MPI2_SAS_PHYINFO_SHIFT_PHY_POWER_CONDITION (27) ++#define MPI2_SAS_PHYINFO_PHY_POWER_ACTIVE (0x00000000) ++#define MPI2_SAS_PHYINFO_PHY_POWER_PARTIAL (0x08000000) ++#define MPI2_SAS_PHYINFO_PHY_POWER_SLUMBER (0x10000000) ++ ++#define MPI2_SAS_PHYINFO_CHANGED_REQ_INSIDE_ZPSDS (0x04000000) ++#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS_PERSISTENT (0x02000000) ++#define MPI2_SAS_PHYINFO_REQ_INSIDE_ZPSDS (0x01000000) ++#define MPI2_SAS_PHYINFO_ZONE_GROUP_PERSISTENT (0x00400000) ++#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS (0x00200000) ++#define MPI2_SAS_PHYINFO_ZONING_ENABLED (0x00100000) ++ ++#define MPI2_SAS_PHYINFO_REASON_MASK (0x000F0000) ++#define MPI2_SAS_PHYINFO_REASON_UNKNOWN (0x00000000) ++#define MPI2_SAS_PHYINFO_REASON_POWER_ON (0x00010000) ++#define MPI2_SAS_PHYINFO_REASON_HARD_RESET (0x00020000) ++#define MPI2_SAS_PHYINFO_REASON_SMP_PHY_CONTROL (0x00030000) ++#define MPI2_SAS_PHYINFO_REASON_LOSS_OF_SYNC (0x00040000) ++#define MPI2_SAS_PHYINFO_REASON_MULTIPLEXING_SEQ (0x00050000) ++#define MPI2_SAS_PHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00060000) ++#define MPI2_SAS_PHYINFO_REASON_BREAK_TIMEOUT (0x00070000) ++#define MPI2_SAS_PHYINFO_REASON_PHY_TEST_STOPPED (0x00080000) ++ ++#define MPI2_SAS_PHYINFO_MULTIPLEXING_SUPPORTED (0x00008000) ++#define MPI2_SAS_PHYINFO_SATA_PORT_ACTIVE (0x00004000) ++#define MPI2_SAS_PHYINFO_SATA_PORT_SELECTOR_PRESENT (0x00002000) ++#define MPI2_SAS_PHYINFO_VIRTUAL_PHY (0x00001000) ++ ++#define MPI2_SAS_PHYINFO_MASK_PARTIAL_PATHWAY_TIME (0x00000F00) ++#define MPI2_SAS_PHYINFO_SHIFT_PARTIAL_PATHWAY_TIME (8) ++ ++#define MPI2_SAS_PHYINFO_MASK_ROUTING_ATTRIBUTE (0x000000F0) ++#define MPI2_SAS_PHYINFO_DIRECT_ROUTING (0x00000000) ++#define MPI2_SAS_PHYINFO_SUBTRACTIVE_ROUTING (0x00000010) ++#define MPI2_SAS_PHYINFO_TABLE_ROUTING (0x00000020) ++ ++ ++/*values for SAS ProgrammedLinkRate fields */ ++#define MPI2_SAS_PRATE_MAX_RATE_MASK (0xF0) ++#define MPI2_SAS_PRATE_MAX_RATE_NOT_PROGRAMMABLE (0x00) ++#define MPI2_SAS_PRATE_MAX_RATE_1_5 (0x80) ++#define MPI2_SAS_PRATE_MAX_RATE_3_0 (0x90) ++#define MPI2_SAS_PRATE_MAX_RATE_6_0 (0xA0) ++#define MPI25_SAS_PRATE_MAX_RATE_12_0 (0xB0) ++#define MPI2_SAS_PRATE_MIN_RATE_MASK (0x0F) ++#define MPI2_SAS_PRATE_MIN_RATE_NOT_PROGRAMMABLE (0x00) ++#define MPI2_SAS_PRATE_MIN_RATE_1_5 (0x08) ++#define MPI2_SAS_PRATE_MIN_RATE_3_0 (0x09) ++#define MPI2_SAS_PRATE_MIN_RATE_6_0 (0x0A) ++#define MPI25_SAS_PRATE_MIN_RATE_12_0 (0x0B) ++ ++ ++/*values for SAS HwLinkRate fields */ ++#define MPI2_SAS_HWRATE_MAX_RATE_MASK (0xF0) ++#define MPI2_SAS_HWRATE_MAX_RATE_1_5 (0x80) ++#define MPI2_SAS_HWRATE_MAX_RATE_3_0 (0x90) ++#define MPI2_SAS_HWRATE_MAX_RATE_6_0 (0xA0) ++#define MPI25_SAS_HWRATE_MAX_RATE_12_0 (0xB0) ++#define MPI2_SAS_HWRATE_MIN_RATE_MASK (0x0F) ++#define MPI2_SAS_HWRATE_MIN_RATE_1_5 (0x08) ++#define MPI2_SAS_HWRATE_MIN_RATE_3_0 (0x09) ++#define MPI2_SAS_HWRATE_MIN_RATE_6_0 (0x0A) ++#define MPI25_SAS_HWRATE_MIN_RATE_12_0 (0x0B) ++ ++ ++ ++/**************************************************************************** ++* SAS IO Unit Config Pages ++****************************************************************************/ ++ ++/*SAS IO Unit Page 0 */ ++ ++typedef struct _MPI2_SAS_IO_UNIT0_PHY_DATA { ++ U8 Port; /*0x00 */ ++ U8 PortFlags; /*0x01 */ ++ U8 PhyFlags; /*0x02 */ ++ U8 NegotiatedLinkRate; /*0x03 */ ++ U32 ControllerPhyDeviceInfo;/*0x04 */ ++ U16 AttachedDevHandle; /*0x08 */ ++ U16 ControllerDevHandle; /*0x0A */ ++ U32 DiscoveryStatus; /*0x0C */ ++ U32 Reserved; /*0x10 */ ++} MPI2_SAS_IO_UNIT0_PHY_DATA, ++ *PTR_MPI2_SAS_IO_UNIT0_PHY_DATA, ++ Mpi2SasIOUnit0PhyData_t, ++ *pMpi2SasIOUnit0PhyData_t; ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI2_SAS_IOUNIT0_PHY_MAX ++#define MPI2_SAS_IOUNIT0_PHY_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1;/*0x08 */ ++ U8 NumPhys; /*0x0C */ ++ U8 Reserved2;/*0x0D */ ++ U16 Reserved3;/*0x0E */ ++ MPI2_SAS_IO_UNIT0_PHY_DATA ++ PhyData[MPI2_SAS_IOUNIT0_PHY_MAX]; /*0x10 */ ++} MPI2_CONFIG_PAGE_SASIOUNIT_0, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_0, ++ Mpi2SasIOUnitPage0_t, *pMpi2SasIOUnitPage0_t; ++ ++#define MPI2_SASIOUNITPAGE0_PAGEVERSION (0x05) ++ ++/*values for SAS IO Unit Page 0 PortFlags */ ++#define MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS (0x08) ++#define MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG (0x01) ++ ++/*values for SAS IO Unit Page 0 PhyFlags */ ++#define MPI2_SASIOUNIT0_PHYFLAGS_INIT_PERSIST_CONNECT (0x40) ++#define MPI2_SASIOUNIT0_PHYFLAGS_TARG_PERSIST_CONNECT (0x20) ++#define MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED (0x10) ++#define MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08) ++ ++/*use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ ++ ++/*see mpi2_sas.h for values for ++ *SAS IO Unit Page 0 ControllerPhyDeviceInfo values */ ++ ++/*values for SAS IO Unit Page 0 DiscoveryStatus */ ++#define MPI2_SASIOUNIT0_DS_MAX_ENCLOSURES_EXCEED (0x80000000) ++#define MPI2_SASIOUNIT0_DS_MAX_EXPANDERS_EXCEED (0x40000000) ++#define MPI2_SASIOUNIT0_DS_MAX_DEVICES_EXCEED (0x20000000) ++#define MPI2_SASIOUNIT0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000) ++#define MPI2_SASIOUNIT0_DS_DOWNSTREAM_INITIATOR (0x08000000) ++#define MPI2_SASIOUNIT0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000) ++#define MPI2_SASIOUNIT0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000) ++#define MPI2_SASIOUNIT0_DS_MULTI_PORT_DOMAIN (0x00002000) ++#define MPI2_SASIOUNIT0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000) ++#define MPI2_SASIOUNIT0_DS_UNSUPPORTED_DEVICE (0x00000800) ++#define MPI2_SASIOUNIT0_DS_TABLE_LINK (0x00000400) ++#define MPI2_SASIOUNIT0_DS_SUBTRACTIVE_LINK (0x00000200) ++#define MPI2_SASIOUNIT0_DS_SMP_CRC_ERROR (0x00000100) ++#define MPI2_SASIOUNIT0_DS_SMP_FUNCTION_FAILED (0x00000080) ++#define MPI2_SASIOUNIT0_DS_INDEX_NOT_EXIST (0x00000040) ++#define MPI2_SASIOUNIT0_DS_OUT_ROUTE_ENTRIES (0x00000020) ++#define MPI2_SASIOUNIT0_DS_SMP_TIMEOUT (0x00000010) ++#define MPI2_SASIOUNIT0_DS_MULTIPLE_PORTS (0x00000004) ++#define MPI2_SASIOUNIT0_DS_UNADDRESSABLE_DEVICE (0x00000002) ++#define MPI2_SASIOUNIT0_DS_LOOP_DETECTED (0x00000001) ++ ++ ++/*SAS IO Unit Page 1 */ ++ ++typedef struct _MPI2_SAS_IO_UNIT1_PHY_DATA { ++ U8 Port; /*0x00 */ ++ U8 PortFlags; /*0x01 */ ++ U8 PhyFlags; /*0x02 */ ++ U8 MaxMinLinkRate; /*0x03 */ ++ U32 ControllerPhyDeviceInfo; /*0x04 */ ++ U16 MaxTargetPortConnectTime; /*0x08 */ ++ U16 Reserved1; /*0x0A */ ++} MPI2_SAS_IO_UNIT1_PHY_DATA, ++ *PTR_MPI2_SAS_IO_UNIT1_PHY_DATA, ++ Mpi2SasIOUnit1PhyData_t, ++ *pMpi2SasIOUnit1PhyData_t; ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI2_SAS_IOUNIT1_PHY_MAX ++#define MPI2_SAS_IOUNIT1_PHY_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_1 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U16 ++ ControlFlags; /*0x08 */ ++ U16 ++ SASNarrowMaxQueueDepth; /*0x0A */ ++ U16 ++ AdditionalControlFlags; /*0x0C */ ++ U16 ++ SASWideMaxQueueDepth; /*0x0E */ ++ U8 ++ NumPhys; /*0x10 */ ++ U8 ++ SATAMaxQDepth; /*0x11 */ ++ U8 ++ ReportDeviceMissingDelay; /*0x12 */ ++ U8 ++ IODeviceMissingDelay; /*0x13 */ ++ MPI2_SAS_IO_UNIT1_PHY_DATA ++ PhyData[MPI2_SAS_IOUNIT1_PHY_MAX]; /*0x14 */ ++} MPI2_CONFIG_PAGE_SASIOUNIT_1, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_1, ++ Mpi2SasIOUnitPage1_t, *pMpi2SasIOUnitPage1_t; ++ ++#define MPI2_SASIOUNITPAGE1_PAGEVERSION (0x09) ++ ++/*values for SAS IO Unit Page 1 ControlFlags */ ++#define MPI2_SASIOUNIT1_CONTROL_DEVICE_SELF_TEST (0x8000) ++#define MPI2_SASIOUNIT1_CONTROL_SATA_3_0_MAX (0x4000) ++#define MPI2_SASIOUNIT1_CONTROL_SATA_1_5_MAX (0x2000) ++#define MPI2_SASIOUNIT1_CONTROL_SATA_SW_PRESERVE (0x1000) ++ ++#define MPI2_SASIOUNIT1_CONTROL_MASK_DEV_SUPPORT (0x0600) ++#define MPI2_SASIOUNIT1_CONTROL_SHIFT_DEV_SUPPORT (9) ++#define MPI2_SASIOUNIT1_CONTROL_DEV_SUPPORT_BOTH (0x0) ++#define MPI2_SASIOUNIT1_CONTROL_DEV_SAS_SUPPORT (0x1) ++#define MPI2_SASIOUNIT1_CONTROL_DEV_SATA_SUPPORT (0x2) ++ ++#define MPI2_SASIOUNIT1_CONTROL_SATA_48BIT_LBA_REQUIRED (0x0080) ++#define MPI2_SASIOUNIT1_CONTROL_SATA_SMART_REQUIRED (0x0040) ++#define MPI2_SASIOUNIT1_CONTROL_SATA_NCQ_REQUIRED (0x0020) ++#define MPI2_SASIOUNIT1_CONTROL_SATA_FUA_REQUIRED (0x0010) ++#define MPI2_SASIOUNIT1_CONTROL_TABLE_SUBTRACTIVE_ILLEGAL (0x0008) ++#define MPI2_SASIOUNIT1_CONTROL_SUBTRACTIVE_ILLEGAL (0x0004) ++#define MPI2_SASIOUNIT1_CONTROL_FIRST_LVL_DISC_ONLY (0x0002) ++#define MPI2_SASIOUNIT1_CONTROL_CLEAR_AFFILIATION (0x0001) ++ ++/*values for SAS IO Unit Page 1 AdditionalControlFlags */ ++#define MPI2_SASIOUNIT1_ACONTROL_DA_PERSIST_CONNECT (0x0100) ++#define MPI2_SASIOUNIT1_ACONTROL_MULTI_PORT_DOMAIN_ILLEGAL (0x0080) ++#define MPI2_SASIOUNIT1_ACONTROL_SATA_ASYNCHROUNOUS_NOTIFICATION (0x0040) ++#define MPI2_SASIOUNIT1_ACONTROL_INVALID_TOPOLOGY_CORRECTION (0x0020) ++#define MPI2_SASIOUNIT1_ACONTROL_PORT_ENABLE_ONLY_SATA_LINK_RESET (0x0010) ++#define MPI2_SASIOUNIT1_ACONTROL_OTHER_AFFILIATION_SATA_LINK_RESET (0x0008) ++#define MPI2_SASIOUNIT1_ACONTROL_SELF_AFFILIATION_SATA_LINK_RESET (0x0004) ++#define MPI2_SASIOUNIT1_ACONTROL_NO_AFFILIATION_SATA_LINK_RESET (0x0002) ++#define MPI2_SASIOUNIT1_ACONTROL_ALLOW_TABLE_TO_TABLE (0x0001) ++ ++/*defines for SAS IO Unit Page 1 ReportDeviceMissingDelay */ ++#define MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK (0x7F) ++#define MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16 (0x80) ++ ++/*values for SAS IO Unit Page 1 PortFlags */ ++#define MPI2_SASIOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG (0x01) ++ ++/*values for SAS IO Unit Page 1 PhyFlags */ ++#define MPI2_SASIOUNIT1_PHYFLAGS_INIT_PERSIST_CONNECT (0x40) ++#define MPI2_SASIOUNIT1_PHYFLAGS_TARG_PERSIST_CONNECT (0x20) ++#define MPI2_SASIOUNIT1_PHYFLAGS_ZONING_ENABLE (0x10) ++#define MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08) ++ ++/*values for SAS IO Unit Page 1 MaxMinLinkRate */ ++#define MPI2_SASIOUNIT1_MAX_RATE_MASK (0xF0) ++#define MPI2_SASIOUNIT1_MAX_RATE_1_5 (0x80) ++#define MPI2_SASIOUNIT1_MAX_RATE_3_0 (0x90) ++#define MPI2_SASIOUNIT1_MAX_RATE_6_0 (0xA0) ++#define MPI25_SASIOUNIT1_MAX_RATE_12_0 (0xB0) ++#define MPI2_SASIOUNIT1_MIN_RATE_MASK (0x0F) ++#define MPI2_SASIOUNIT1_MIN_RATE_1_5 (0x08) ++#define MPI2_SASIOUNIT1_MIN_RATE_3_0 (0x09) ++#define MPI2_SASIOUNIT1_MIN_RATE_6_0 (0x0A) ++#define MPI25_SASIOUNIT1_MIN_RATE_12_0 (0x0B) ++ ++/*see mpi2_sas.h for values for ++ *SAS IO Unit Page 1 ControllerPhyDeviceInfo values */ ++ ++ ++/*SAS IO Unit Page 4 (for MPI v2.5 and earlier) */ ++ ++typedef struct _MPI2_SAS_IOUNIT4_SPINUP_GROUP { ++ U8 MaxTargetSpinup; /*0x00 */ ++ U8 SpinupDelay; /*0x01 */ ++ U8 SpinupFlags; /*0x02 */ ++ U8 Reserved1; /*0x03 */ ++} MPI2_SAS_IOUNIT4_SPINUP_GROUP, ++ *PTR_MPI2_SAS_IOUNIT4_SPINUP_GROUP, ++ Mpi2SasIOUnit4SpinupGroup_t, ++ *pMpi2SasIOUnit4SpinupGroup_t; ++/*defines for SAS IO Unit Page 4 SpinupFlags */ ++#define MPI2_SASIOUNIT4_SPINUP_DISABLE_FLAG (0x01) ++ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI2_SAS_IOUNIT4_PHY_MAX ++#define MPI2_SAS_IOUNIT4_PHY_MAX (4) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_4 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header;/*0x00 */ ++ MPI2_SAS_IOUNIT4_SPINUP_GROUP ++ SpinupGroupParameters[4]; /*0x08 */ ++ U32 ++ Reserved1; /*0x18 */ ++ U32 ++ Reserved2; /*0x1C */ ++ U32 ++ Reserved3; /*0x20 */ ++ U8 ++ BootDeviceWaitTime; /*0x24 */ ++ U8 ++ SATADeviceWaitTime; /*0x25 */ ++ U16 ++ Reserved5; /*0x26 */ ++ U8 ++ NumPhys; /*0x28 */ ++ U8 ++ PEInitialSpinupDelay; /*0x29 */ ++ U8 ++ PEReplyDelay; /*0x2A */ ++ U8 ++ Flags; /*0x2B */ ++ U8 ++ PHY[MPI2_SAS_IOUNIT4_PHY_MAX]; /*0x2C */ ++} MPI2_CONFIG_PAGE_SASIOUNIT_4, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_4, ++ Mpi2SasIOUnitPage4_t, *pMpi2SasIOUnitPage4_t; ++ ++#define MPI2_SASIOUNITPAGE4_PAGEVERSION (0x02) ++ ++/*defines for Flags field */ ++#define MPI2_SASIOUNIT4_FLAGS_AUTO_PORTENABLE (0x01) ++ ++/*defines for PHY field */ ++#define MPI2_SASIOUNIT4_PHY_SPINUP_GROUP_MASK (0x03) ++ ++ ++/*SAS IO Unit Page 5 */ ++ ++typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS { ++ U8 ControlFlags; /*0x00 */ ++ U8 PortWidthModGroup; /*0x01 */ ++ U16 InactivityTimerExponent; /*0x02 */ ++ U8 SATAPartialTimeout; /*0x04 */ ++ U8 Reserved2; /*0x05 */ ++ U8 SATASlumberTimeout; /*0x06 */ ++ U8 Reserved3; /*0x07 */ ++ U8 SASPartialTimeout; /*0x08 */ ++ U8 Reserved4; /*0x09 */ ++ U8 SASSlumberTimeout; /*0x0A */ ++ U8 Reserved5; /*0x0B */ ++} MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS, ++ *PTR_MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS, ++ Mpi2SasIOUnit5PhyPmSettings_t, ++ *pMpi2SasIOUnit5PhyPmSettings_t; ++ ++/*defines for ControlFlags field */ ++#define MPI2_SASIOUNIT5_CONTROL_SAS_SLUMBER_ENABLE (0x08) ++#define MPI2_SASIOUNIT5_CONTROL_SAS_PARTIAL_ENABLE (0x04) ++#define MPI2_SASIOUNIT5_CONTROL_SATA_SLUMBER_ENABLE (0x02) ++#define MPI2_SASIOUNIT5_CONTROL_SATA_PARTIAL_ENABLE (0x01) ++ ++/*defines for PortWidthModeGroup field */ ++#define MPI2_SASIOUNIT5_PWMG_DISABLE (0xFF) ++ ++/*defines for InactivityTimerExponent field */ ++#define MPI2_SASIOUNIT5_ITE_MASK_SAS_SLUMBER (0x7000) ++#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_SLUMBER (12) ++#define MPI2_SASIOUNIT5_ITE_MASK_SAS_PARTIAL (0x0700) ++#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_PARTIAL (8) ++#define MPI2_SASIOUNIT5_ITE_MASK_SATA_SLUMBER (0x0070) ++#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_SLUMBER (4) ++#define MPI2_SASIOUNIT5_ITE_MASK_SATA_PARTIAL (0x0007) ++#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_PARTIAL (0) ++ ++#define MPI2_SASIOUNIT5_ITE_TEN_SECONDS (7) ++#define MPI2_SASIOUNIT5_ITE_ONE_SECOND (6) ++#define MPI2_SASIOUNIT5_ITE_HUNDRED_MILLISECONDS (5) ++#define MPI2_SASIOUNIT5_ITE_TEN_MILLISECONDS (4) ++#define MPI2_SASIOUNIT5_ITE_ONE_MILLISECOND (3) ++#define MPI2_SASIOUNIT5_ITE_HUNDRED_MICROSECONDS (2) ++#define MPI2_SASIOUNIT5_ITE_TEN_MICROSECONDS (1) ++#define MPI2_SASIOUNIT5_ITE_ONE_MICROSECOND (0) ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhys at runtime. ++ */ ++#ifndef MPI2_SAS_IOUNIT5_PHY_MAX ++#define MPI2_SAS_IOUNIT5_PHY_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_5 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U8 NumPhys; /*0x08 */ ++ U8 Reserved1;/*0x09 */ ++ U16 Reserved2;/*0x0A */ ++ U32 Reserved3;/*0x0C */ ++ MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS ++ SASPhyPowerManagementSettings[MPI2_SAS_IOUNIT5_PHY_MAX];/*0x10 */ ++} MPI2_CONFIG_PAGE_SASIOUNIT_5, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_5, ++ Mpi2SasIOUnitPage5_t, *pMpi2SasIOUnitPage5_t; ++ ++#define MPI2_SASIOUNITPAGE5_PAGEVERSION (0x01) ++ ++ ++/*SAS IO Unit Page 6 */ ++ ++typedef struct _MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS { ++ U8 CurrentStatus; /*0x00 */ ++ U8 CurrentModulation; /*0x01 */ ++ U8 CurrentUtilization; /*0x02 */ ++ U8 Reserved1; /*0x03 */ ++ U32 Reserved2; /*0x04 */ ++} MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS, ++ *PTR_MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS, ++ Mpi2SasIOUnit6PortWidthModGroupStatus_t, ++ *pMpi2SasIOUnit6PortWidthModGroupStatus_t; ++ ++/*defines for CurrentStatus field */ ++#define MPI2_SASIOUNIT6_STATUS_UNAVAILABLE (0x00) ++#define MPI2_SASIOUNIT6_STATUS_UNCONFIGURED (0x01) ++#define MPI2_SASIOUNIT6_STATUS_INVALID_CONFIG (0x02) ++#define MPI2_SASIOUNIT6_STATUS_LINK_DOWN (0x03) ++#define MPI2_SASIOUNIT6_STATUS_OBSERVATION_ONLY (0x04) ++#define MPI2_SASIOUNIT6_STATUS_INACTIVE (0x05) ++#define MPI2_SASIOUNIT6_STATUS_ACTIVE_IOUNIT (0x06) ++#define MPI2_SASIOUNIT6_STATUS_ACTIVE_HOST (0x07) ++ ++/*defines for CurrentModulation field */ ++#define MPI2_SASIOUNIT6_MODULATION_25_PERCENT (0x00) ++#define MPI2_SASIOUNIT6_MODULATION_50_PERCENT (0x01) ++#define MPI2_SASIOUNIT6_MODULATION_75_PERCENT (0x02) ++#define MPI2_SASIOUNIT6_MODULATION_100_PERCENT (0x03) ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumGroups at runtime. ++ */ ++#ifndef MPI2_SAS_IOUNIT6_GROUP_MAX ++#define MPI2_SAS_IOUNIT6_GROUP_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_6 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x08 */ ++ U32 Reserved2; /*0x0C */ ++ U8 NumGroups; /*0x10 */ ++ U8 Reserved3; /*0x11 */ ++ U16 Reserved4; /*0x12 */ ++ MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS ++ PortWidthModulationGroupStatus[MPI2_SAS_IOUNIT6_GROUP_MAX]; /*0x14 */ ++} MPI2_CONFIG_PAGE_SASIOUNIT_6, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_6, ++ Mpi2SasIOUnitPage6_t, *pMpi2SasIOUnitPage6_t; ++ ++#define MPI2_SASIOUNITPAGE6_PAGEVERSION (0x00) ++ ++ ++/*SAS IO Unit Page 7 */ ++ ++typedef struct _MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS { ++ U8 Flags; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U8 Threshold75Pct; /*0x04 */ ++ U8 Threshold50Pct; /*0x05 */ ++ U8 Threshold25Pct; /*0x06 */ ++ U8 Reserved3; /*0x07 */ ++} MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS, ++ *PTR_MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS, ++ Mpi2SasIOUnit7PortWidthModGroupSettings_t, ++ *pMpi2SasIOUnit7PortWidthModGroupSettings_t; ++ ++/*defines for Flags field */ ++#define MPI2_SASIOUNIT7_FLAGS_ENABLE_PORT_WIDTH_MODULATION (0x01) ++ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumGroups at runtime. ++ */ ++#ifndef MPI2_SAS_IOUNIT7_GROUP_MAX ++#define MPI2_SAS_IOUNIT7_GROUP_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_7 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U8 SamplingInterval; /*0x08 */ ++ U8 WindowLength; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U32 Reserved2; /*0x0C */ ++ U32 Reserved3; /*0x10 */ ++ U8 NumGroups; /*0x14 */ ++ U8 Reserved4; /*0x15 */ ++ U16 Reserved5; /*0x16 */ ++ MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS ++ PortWidthModulationGroupSettings[MPI2_SAS_IOUNIT7_GROUP_MAX];/*0x18 */ ++} MPI2_CONFIG_PAGE_SASIOUNIT_7, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_7, ++ Mpi2SasIOUnitPage7_t, *pMpi2SasIOUnitPage7_t; ++ ++#define MPI2_SASIOUNITPAGE7_PAGEVERSION (0x00) ++ ++ ++/*SAS IO Unit Page 8 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_8 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ Reserved1; /*0x08 */ ++ U32 ++ PowerManagementCapabilities; /*0x0C */ ++ U8 ++ TxRxSleepStatus; /*0x10 */ ++ U8 ++ Reserved2; /*0x11 */ ++ U16 ++ Reserved3; /*0x12 */ ++} MPI2_CONFIG_PAGE_SASIOUNIT_8, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_8, ++ Mpi2SasIOUnitPage8_t, *pMpi2SasIOUnitPage8_t; ++ ++#define MPI2_SASIOUNITPAGE8_PAGEVERSION (0x00) ++ ++/*defines for PowerManagementCapabilities field */ ++#define MPI2_SASIOUNIT8_PM_HOST_PORT_WIDTH_MOD (0x00001000) ++#define MPI2_SASIOUNIT8_PM_HOST_SAS_SLUMBER_MODE (0x00000800) ++#define MPI2_SASIOUNIT8_PM_HOST_SAS_PARTIAL_MODE (0x00000400) ++#define MPI2_SASIOUNIT8_PM_HOST_SATA_SLUMBER_MODE (0x00000200) ++#define MPI2_SASIOUNIT8_PM_HOST_SATA_PARTIAL_MODE (0x00000100) ++#define MPI2_SASIOUNIT8_PM_IOUNIT_PORT_WIDTH_MOD (0x00000010) ++#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_SLUMBER_MODE (0x00000008) ++#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_PARTIAL_MODE (0x00000004) ++#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_SLUMBER_MODE (0x00000002) ++#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_PARTIAL_MODE (0x00000001) ++ ++/*defines for TxRxSleepStatus field */ ++#define MPI25_SASIOUNIT8_TXRXSLEEP_UNSUPPORTED (0x00) ++#define MPI25_SASIOUNIT8_TXRXSLEEP_DISENGAGED (0x01) ++#define MPI25_SASIOUNIT8_TXRXSLEEP_ACTIVE (0x02) ++#define MPI25_SASIOUNIT8_TXRXSLEEP_SHUTDOWN (0x03) ++ ++ ++ ++/*SAS IO Unit Page 16 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT16 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U64 ++ TimeStamp; /*0x08 */ ++ U32 ++ Reserved1; /*0x10 */ ++ U32 ++ Reserved2; /*0x14 */ ++ U32 ++ FastPathPendedRequests; /*0x18 */ ++ U32 ++ FastPathUnPendedRequests; /*0x1C */ ++ U32 ++ FastPathHostRequestStarts; /*0x20 */ ++ U32 ++ FastPathFirmwareRequestStarts; /*0x24 */ ++ U32 ++ FastPathHostCompletions; /*0x28 */ ++ U32 ++ FastPathFirmwareCompletions; /*0x2C */ ++ U32 ++ NonFastPathRequestStarts; /*0x30 */ ++ U32 ++ NonFastPathHostCompletions; /*0x30 */ ++} MPI2_CONFIG_PAGE_SASIOUNIT16, ++ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT16, ++ Mpi2SasIOUnitPage16_t, *pMpi2SasIOUnitPage16_t; ++ ++#define MPI2_SASIOUNITPAGE16_PAGEVERSION (0x00) ++ ++ ++/**************************************************************************** ++* SAS Expander Config Pages ++****************************************************************************/ ++ ++/*SAS Expander Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_EXPANDER_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U8 ++ PhysicalPort; /*0x08 */ ++ U8 ++ ReportGenLength; /*0x09 */ ++ U16 ++ EnclosureHandle; /*0x0A */ ++ U64 ++ SASAddress; /*0x0C */ ++ U32 ++ DiscoveryStatus; /*0x14 */ ++ U16 ++ DevHandle; /*0x18 */ ++ U16 ++ ParentDevHandle; /*0x1A */ ++ U16 ++ ExpanderChangeCount; /*0x1C */ ++ U16 ++ ExpanderRouteIndexes; /*0x1E */ ++ U8 ++ NumPhys; /*0x20 */ ++ U8 ++ SASLevel; /*0x21 */ ++ U16 ++ Flags; /*0x22 */ ++ U16 ++ STPBusInactivityTimeLimit; /*0x24 */ ++ U16 ++ STPMaxConnectTimeLimit; /*0x26 */ ++ U16 ++ STP_SMP_NexusLossTime; /*0x28 */ ++ U16 ++ MaxNumRoutedSasAddresses; /*0x2A */ ++ U64 ++ ActiveZoneManagerSASAddress;/*0x2C */ ++ U16 ++ ZoneLockInactivityLimit; /*0x34 */ ++ U16 ++ Reserved1; /*0x36 */ ++ U8 ++ TimeToReducedFunc; /*0x38 */ ++ U8 ++ InitialTimeToReducedFunc; /*0x39 */ ++ U8 ++ MaxReducedFuncTime; /*0x3A */ ++ U8 ++ Reserved2; /*0x3B */ ++} MPI2_CONFIG_PAGE_EXPANDER_0, ++ *PTR_MPI2_CONFIG_PAGE_EXPANDER_0, ++ Mpi2ExpanderPage0_t, *pMpi2ExpanderPage0_t; ++ ++#define MPI2_SASEXPANDER0_PAGEVERSION (0x06) ++ ++/*values for SAS Expander Page 0 DiscoveryStatus field */ ++#define MPI2_SAS_EXPANDER0_DS_MAX_ENCLOSURES_EXCEED (0x80000000) ++#define MPI2_SAS_EXPANDER0_DS_MAX_EXPANDERS_EXCEED (0x40000000) ++#define MPI2_SAS_EXPANDER0_DS_MAX_DEVICES_EXCEED (0x20000000) ++#define MPI2_SAS_EXPANDER0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000) ++#define MPI2_SAS_EXPANDER0_DS_DOWNSTREAM_INITIATOR (0x08000000) ++#define MPI2_SAS_EXPANDER0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000) ++#define MPI2_SAS_EXPANDER0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000) ++#define MPI2_SAS_EXPANDER0_DS_MULTI_PORT_DOMAIN (0x00002000) ++#define MPI2_SAS_EXPANDER0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000) ++#define MPI2_SAS_EXPANDER0_DS_UNSUPPORTED_DEVICE (0x00000800) ++#define MPI2_SAS_EXPANDER0_DS_TABLE_LINK (0x00000400) ++#define MPI2_SAS_EXPANDER0_DS_SUBTRACTIVE_LINK (0x00000200) ++#define MPI2_SAS_EXPANDER0_DS_SMP_CRC_ERROR (0x00000100) ++#define MPI2_SAS_EXPANDER0_DS_SMP_FUNCTION_FAILED (0x00000080) ++#define MPI2_SAS_EXPANDER0_DS_INDEX_NOT_EXIST (0x00000040) ++#define MPI2_SAS_EXPANDER0_DS_OUT_ROUTE_ENTRIES (0x00000020) ++#define MPI2_SAS_EXPANDER0_DS_SMP_TIMEOUT (0x00000010) ++#define MPI2_SAS_EXPANDER0_DS_MULTIPLE_PORTS (0x00000004) ++#define MPI2_SAS_EXPANDER0_DS_UNADDRESSABLE_DEVICE (0x00000002) ++#define MPI2_SAS_EXPANDER0_DS_LOOP_DETECTED (0x00000001) ++ ++/*values for SAS Expander Page 0 Flags field */ ++#define MPI2_SAS_EXPANDER0_FLAGS_REDUCED_FUNCTIONALITY (0x2000) ++#define MPI2_SAS_EXPANDER0_FLAGS_ZONE_LOCKED (0x1000) ++#define MPI2_SAS_EXPANDER0_FLAGS_SUPPORTED_PHYSICAL_PRES (0x0800) ++#define MPI2_SAS_EXPANDER0_FLAGS_ASSERTED_PHYSICAL_PRES (0x0400) ++#define MPI2_SAS_EXPANDER0_FLAGS_ZONING_SUPPORT (0x0200) ++#define MPI2_SAS_EXPANDER0_FLAGS_ENABLED_ZONING (0x0100) ++#define MPI2_SAS_EXPANDER0_FLAGS_TABLE_TO_TABLE_SUPPORT (0x0080) ++#define MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE (0x0010) ++#define MPI2_SAS_EXPANDER0_FLAGS_OTHERS_CONFIG (0x0004) ++#define MPI2_SAS_EXPANDER0_FLAGS_CONFIG_IN_PROGRESS (0x0002) ++#define MPI2_SAS_EXPANDER0_FLAGS_ROUTE_TABLE_CONFIG (0x0001) ++ ++ ++/*SAS Expander Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_EXPANDER_1 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U8 ++ PhysicalPort; /*0x08 */ ++ U8 ++ Reserved1; /*0x09 */ ++ U16 ++ Reserved2; /*0x0A */ ++ U8 ++ NumPhys; /*0x0C */ ++ U8 ++ Phy; /*0x0D */ ++ U16 ++ NumTableEntriesProgrammed; /*0x0E */ ++ U8 ++ ProgrammedLinkRate; /*0x10 */ ++ U8 ++ HwLinkRate; /*0x11 */ ++ U16 ++ AttachedDevHandle; /*0x12 */ ++ U32 ++ PhyInfo; /*0x14 */ ++ U32 ++ AttachedDeviceInfo; /*0x18 */ ++ U16 ++ ExpanderDevHandle; /*0x1C */ ++ U8 ++ ChangeCount; /*0x1E */ ++ U8 ++ NegotiatedLinkRate; /*0x1F */ ++ U8 ++ PhyIdentifier; /*0x20 */ ++ U8 ++ AttachedPhyIdentifier; /*0x21 */ ++ U8 ++ Reserved3; /*0x22 */ ++ U8 ++ DiscoveryInfo; /*0x23 */ ++ U32 ++ AttachedPhyInfo; /*0x24 */ ++ U8 ++ ZoneGroup; /*0x28 */ ++ U8 ++ SelfConfigStatus; /*0x29 */ ++ U16 ++ Reserved4; /*0x2A */ ++} MPI2_CONFIG_PAGE_EXPANDER_1, ++ *PTR_MPI2_CONFIG_PAGE_EXPANDER_1, ++ Mpi2ExpanderPage1_t, *pMpi2ExpanderPage1_t; ++ ++#define MPI2_SASEXPANDER1_PAGEVERSION (0x02) ++ ++/*use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */ ++ ++/*use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */ ++ ++/*use MPI2_SAS_PHYINFO_ for the PhyInfo field */ ++ ++/*see mpi2_sas.h for the MPI2_SAS_DEVICE_INFO_ defines ++ *used for the AttachedDeviceInfo field */ ++ ++/*use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ ++ ++/*values for SAS Expander Page 1 DiscoveryInfo field */ ++#define MPI2_SAS_EXPANDER1_DISCINFO_BAD_PHY_DISABLED (0x04) ++#define MPI2_SAS_EXPANDER1_DISCINFO_LINK_STATUS_CHANGE (0x02) ++#define MPI2_SAS_EXPANDER1_DISCINFO_NO_ROUTING_ENTRIES (0x01) ++ ++/*use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */ ++ ++ ++/**************************************************************************** ++* SAS Device Config Pages ++****************************************************************************/ ++ ++/*SAS Device Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U16 ++ Slot; /*0x08 */ ++ U16 ++ EnclosureHandle; /*0x0A */ ++ U64 ++ SASAddress; /*0x0C */ ++ U16 ++ ParentDevHandle; /*0x14 */ ++ U8 ++ PhyNum; /*0x16 */ ++ U8 ++ AccessStatus; /*0x17 */ ++ U16 ++ DevHandle; /*0x18 */ ++ U8 ++ AttachedPhyIdentifier; /*0x1A */ ++ U8 ++ ZoneGroup; /*0x1B */ ++ U32 ++ DeviceInfo; /*0x1C */ ++ U16 ++ Flags; /*0x20 */ ++ U8 ++ PhysicalPort; /*0x22 */ ++ U8 ++ MaxPortConnections; /*0x23 */ ++ U64 ++ DeviceName; /*0x24 */ ++ U8 ++ PortGroups; /*0x2C */ ++ U8 ++ DmaGroup; /*0x2D */ ++ U8 ++ ControlGroup; /*0x2E */ ++ U8 ++ EnclosureLevel; /*0x2F */ ++ U32 ++ ConnectorName[4]; /*0x30 */ ++ U32 ++ Reserved3; /*0x34 */ ++} MPI2_CONFIG_PAGE_SAS_DEV_0, ++ *PTR_MPI2_CONFIG_PAGE_SAS_DEV_0, ++ Mpi2SasDevicePage0_t, ++ *pMpi2SasDevicePage0_t; ++ ++#define MPI2_SASDEVICE0_PAGEVERSION (0x09) ++ ++/*values for SAS Device Page 0 AccessStatus field */ ++#define MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS (0x00) ++#define MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED (0x01) ++#define MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED (0x02) ++#define MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT (0x03) ++#define MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION (0x04) ++#define MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE (0x05) ++#define MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE (0x06) ++#define MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED (0x07) ++/*specific values for SATA Init failures */ ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN (0x10) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT (0x11) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG (0x12) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION (0x13) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER (0x14) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN (0x15) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN (0x16) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN (0x17) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION (0x18) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE (0x19) ++#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX (0x1F) ++ ++/*see mpi2_sas.h for values for SAS Device Page 0 DeviceInfo values */ ++ ++/*values for SAS Device Page 0 Flags field */ ++#define MPI2_SAS_DEVICE0_FLAGS_UNAUTHORIZED_DEVICE (0x8000) ++#define MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH (0x4000) ++#define MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE (0x2000) ++#define MPI2_SAS_DEVICE0_FLAGS_SLUMBER_PM_CAPABLE (0x1000) ++#define MPI2_SAS_DEVICE0_FLAGS_PARTIAL_PM_CAPABLE (0x0800) ++#define MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY (0x0400) ++#define MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE (0x0200) ++#define MPI2_SAS_DEVICE0_FLAGS_UNSUPPORTED_DEVICE (0x0100) ++#define MPI2_SAS_DEVICE0_FLAGS_SATA_48BIT_LBA_SUPPORTED (0x0080) ++#define MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED (0x0040) ++#define MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED (0x0020) ++#define MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED (0x0010) ++#define MPI2_SAS_DEVICE0_FLAGS_PORT_SELECTOR_ATTACH (0x0008) ++#define MPI2_SAS_DEVICE0_FLAGS_PERSIST_CAPABLE (0x0004) ++#define MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID (0x0002) ++#define MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT (0x0001) ++ ++ ++/*SAS Device Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_1 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ Reserved1; /*0x08 */ ++ U64 ++ SASAddress; /*0x0C */ ++ U32 ++ Reserved2; /*0x14 */ ++ U16 ++ DevHandle; /*0x18 */ ++ U16 ++ Reserved3; /*0x1A */ ++ U8 ++ InitialRegDeviceFIS[20];/*0x1C */ ++} MPI2_CONFIG_PAGE_SAS_DEV_1, ++ *PTR_MPI2_CONFIG_PAGE_SAS_DEV_1, ++ Mpi2SasDevicePage1_t, ++ *pMpi2SasDevicePage1_t; ++ ++#define MPI2_SASDEVICE1_PAGEVERSION (0x01) ++ ++ ++/**************************************************************************** ++* SAS PHY Config Pages ++****************************************************************************/ ++ ++/*SAS PHY Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U16 ++ OwnerDevHandle; /*0x08 */ ++ U16 ++ Reserved1; /*0x0A */ ++ U16 ++ AttachedDevHandle; /*0x0C */ ++ U8 ++ AttachedPhyIdentifier; /*0x0E */ ++ U8 ++ Reserved2; /*0x0F */ ++ U32 ++ AttachedPhyInfo; /*0x10 */ ++ U8 ++ ProgrammedLinkRate; /*0x14 */ ++ U8 ++ HwLinkRate; /*0x15 */ ++ U8 ++ ChangeCount; /*0x16 */ ++ U8 ++ Flags; /*0x17 */ ++ U32 ++ PhyInfo; /*0x18 */ ++ U8 ++ NegotiatedLinkRate; /*0x1C */ ++ U8 ++ Reserved3; /*0x1D */ ++ U16 ++ Reserved4; /*0x1E */ ++} MPI2_CONFIG_PAGE_SAS_PHY_0, ++ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_0, ++ Mpi2SasPhyPage0_t, *pMpi2SasPhyPage0_t; ++ ++#define MPI2_SASPHY0_PAGEVERSION (0x03) ++ ++/*use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */ ++ ++/*use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */ ++ ++/*use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */ ++ ++/*values for SAS PHY Page 0 Flags field */ ++#define MPI2_SAS_PHY0_FLAGS_SGPIO_DIRECT_ATTACH_ENC (0x01) ++ ++/*use MPI2_SAS_PHYINFO_ for the PhyInfo field */ ++ ++/*use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ ++ ++ ++/*SAS PHY Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_1 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ Reserved1; /*0x08 */ ++ U32 ++ InvalidDwordCount; /*0x0C */ ++ U32 ++ RunningDisparityErrorCount; /*0x10 */ ++ U32 ++ LossDwordSynchCount; /*0x14 */ ++ U32 ++ PhyResetProblemCount; /*0x18 */ ++} MPI2_CONFIG_PAGE_SAS_PHY_1, ++ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_1, ++ Mpi2SasPhyPage1_t, *pMpi2SasPhyPage1_t; ++ ++#define MPI2_SASPHY1_PAGEVERSION (0x01) ++ ++ ++/*SAS PHY Page 2 */ ++ ++typedef struct _MPI2_SASPHY2_PHY_EVENT { ++ U8 PhyEventCode; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U32 PhyEventInfo; /*0x04 */ ++} MPI2_SASPHY2_PHY_EVENT, *PTR_MPI2_SASPHY2_PHY_EVENT, ++ Mpi2SasPhy2PhyEvent_t, *pMpi2SasPhy2PhyEvent_t; ++ ++/*use MPI2_SASPHY3_EVENT_CODE_ for the PhyEventCode field */ ++ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhyEvents at runtime. ++ */ ++#ifndef MPI2_SASPHY2_PHY_EVENT_MAX ++#define MPI2_SASPHY2_PHY_EVENT_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_2 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ Reserved1; /*0x08 */ ++ U8 ++ NumPhyEvents; /*0x0C */ ++ U8 ++ Reserved2; /*0x0D */ ++ U16 ++ Reserved3; /*0x0E */ ++ MPI2_SASPHY2_PHY_EVENT ++ PhyEvent[MPI2_SASPHY2_PHY_EVENT_MAX]; /*0x10 */ ++} MPI2_CONFIG_PAGE_SAS_PHY_2, ++ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_2, ++ Mpi2SasPhyPage2_t, ++ *pMpi2SasPhyPage2_t; ++ ++#define MPI2_SASPHY2_PAGEVERSION (0x00) ++ ++ ++/*SAS PHY Page 3 */ ++ ++typedef struct _MPI2_SASPHY3_PHY_EVENT_CONFIG { ++ U8 PhyEventCode; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U8 CounterType; /*0x04 */ ++ U8 ThresholdWindow; /*0x05 */ ++ U8 TimeUnits; /*0x06 */ ++ U8 Reserved3; /*0x07 */ ++ U32 EventThreshold; /*0x08 */ ++ U16 ThresholdFlags; /*0x0C */ ++ U16 Reserved4; /*0x0E */ ++} MPI2_SASPHY3_PHY_EVENT_CONFIG, ++ *PTR_MPI2_SASPHY3_PHY_EVENT_CONFIG, ++ Mpi2SasPhy3PhyEventConfig_t, ++ *pMpi2SasPhy3PhyEventConfig_t; ++ ++/*values for PhyEventCode field */ ++#define MPI2_SASPHY3_EVENT_CODE_NO_EVENT (0x00) ++#define MPI2_SASPHY3_EVENT_CODE_INVALID_DWORD (0x01) ++#define MPI2_SASPHY3_EVENT_CODE_RUNNING_DISPARITY_ERROR (0x02) ++#define MPI2_SASPHY3_EVENT_CODE_LOSS_DWORD_SYNC (0x03) ++#define MPI2_SASPHY3_EVENT_CODE_PHY_RESET_PROBLEM (0x04) ++#define MPI2_SASPHY3_EVENT_CODE_ELASTICITY_BUF_OVERFLOW (0x05) ++#define MPI2_SASPHY3_EVENT_CODE_RX_ERROR (0x06) ++#define MPI2_SASPHY3_EVENT_CODE_RX_ADDR_FRAME_ERROR (0x20) ++#define MPI2_SASPHY3_EVENT_CODE_TX_AC_OPEN_REJECT (0x21) ++#define MPI2_SASPHY3_EVENT_CODE_RX_AC_OPEN_REJECT (0x22) ++#define MPI2_SASPHY3_EVENT_CODE_TX_RC_OPEN_REJECT (0x23) ++#define MPI2_SASPHY3_EVENT_CODE_RX_RC_OPEN_REJECT (0x24) ++#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_PARTIAL_WAITING_ON (0x25) ++#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_CONNECT_WAITING_ON (0x26) ++#define MPI2_SASPHY3_EVENT_CODE_TX_BREAK (0x27) ++#define MPI2_SASPHY3_EVENT_CODE_RX_BREAK (0x28) ++#define MPI2_SASPHY3_EVENT_CODE_BREAK_TIMEOUT (0x29) ++#define MPI2_SASPHY3_EVENT_CODE_CONNECTION (0x2A) ++#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_PATHWAY_BLOCKED (0x2B) ++#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_ARB_WAIT_TIME (0x2C) ++#define MPI2_SASPHY3_EVENT_CODE_PEAK_ARB_WAIT_TIME (0x2D) ++#define MPI2_SASPHY3_EVENT_CODE_PEAK_CONNECT_TIME (0x2E) ++#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_FRAMES (0x40) ++#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_FRAMES (0x41) ++#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_ERROR_FRAMES (0x42) ++#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_ERROR_FRAMES (0x43) ++#define MPI2_SASPHY3_EVENT_CODE_TX_CREDIT_BLOCKED (0x44) ++#define MPI2_SASPHY3_EVENT_CODE_RX_CREDIT_BLOCKED (0x45) ++#define MPI2_SASPHY3_EVENT_CODE_TX_SATA_FRAMES (0x50) ++#define MPI2_SASPHY3_EVENT_CODE_RX_SATA_FRAMES (0x51) ++#define MPI2_SASPHY3_EVENT_CODE_SATA_OVERFLOW (0x52) ++#define MPI2_SASPHY3_EVENT_CODE_TX_SMP_FRAMES (0x60) ++#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_FRAMES (0x61) ++#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_ERROR_FRAMES (0x63) ++#define MPI2_SASPHY3_EVENT_CODE_HOTPLUG_TIMEOUT (0xD0) ++#define MPI2_SASPHY3_EVENT_CODE_MISALIGNED_MUX_PRIMITIVE (0xD1) ++#define MPI2_SASPHY3_EVENT_CODE_RX_AIP (0xD2) ++ ++/*Following codes are product specific and in MPI v2.6 and later */ ++#define MPI2_SASPHY3_EVENT_CODE_LCARB_WAIT_TIME (0xD3) ++#define MPI2_SASPHY3_EVENT_CODE_RCVD_CONN_RESP_WAIT_TIME (0xD4) ++#define MPI2_SASPHY3_EVENT_CODE_LCCONN_TIME (0xD5) ++#define MPI2_SASPHY3_EVENT_CODE_SSP_TX_START_TRANSMIT (0xD6) ++#define MPI2_SASPHY3_EVENT_CODE_SATA_TX_START (0xD7) ++#define MPI2_SASPHY3_EVENT_CODE_SMP_TX_START_TRANSMT (0xD8) ++#define MPI2_SASPHY3_EVENT_CODE_TX_SMP_BREAK_CONN (0xD9) ++#define MPI2_SASPHY3_EVENT_CODE_SSP_RX_START_RECEIVE (0xDA) ++#define MPI2_SASPHY3_EVENT_CODE_SATA_RX_START_RECEIVE (0xDB) ++#define MPI2_SASPHY3_EVENT_CODE_SMP_RX_START_RECEIVE (0xDC) ++ ++ ++/*values for the CounterType field */ ++#define MPI2_SASPHY3_COUNTER_TYPE_WRAPPING (0x00) ++#define MPI2_SASPHY3_COUNTER_TYPE_SATURATING (0x01) ++#define MPI2_SASPHY3_COUNTER_TYPE_PEAK_VALUE (0x02) ++ ++/*values for the TimeUnits field */ ++#define MPI2_SASPHY3_TIME_UNITS_10_MICROSECONDS (0x00) ++#define MPI2_SASPHY3_TIME_UNITS_100_MICROSECONDS (0x01) ++#define MPI2_SASPHY3_TIME_UNITS_1_MILLISECOND (0x02) ++#define MPI2_SASPHY3_TIME_UNITS_10_MILLISECONDS (0x03) ++ ++/*values for the ThresholdFlags field */ ++#define MPI2_SASPHY3_TFLAGS_PHY_RESET (0x0002) ++#define MPI2_SASPHY3_TFLAGS_EVENT_NOTIFY (0x0001) ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumPhyEvents at runtime. ++ */ ++#ifndef MPI2_SASPHY3_PHY_EVENT_MAX ++#define MPI2_SASPHY3_PHY_EVENT_MAX (1) ++#endif ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_3 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ Reserved1; /*0x08 */ ++ U8 ++ NumPhyEvents; /*0x0C */ ++ U8 ++ Reserved2; /*0x0D */ ++ U16 ++ Reserved3; /*0x0E */ ++ MPI2_SASPHY3_PHY_EVENT_CONFIG ++ PhyEventConfig[MPI2_SASPHY3_PHY_EVENT_MAX]; /*0x10 */ ++} MPI2_CONFIG_PAGE_SAS_PHY_3, ++ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_3, ++ Mpi2SasPhyPage3_t, *pMpi2SasPhyPage3_t; ++ ++#define MPI2_SASPHY3_PAGEVERSION (0x00) ++ ++ ++/*SAS PHY Page 4 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_4 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U16 ++ Reserved1; /*0x08 */ ++ U8 ++ Reserved2; /*0x0A */ ++ U8 ++ Flags; /*0x0B */ ++ U8 ++ InitialFrame[28]; /*0x0C */ ++} MPI2_CONFIG_PAGE_SAS_PHY_4, ++ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_4, ++ Mpi2SasPhyPage4_t, *pMpi2SasPhyPage4_t; ++ ++#define MPI2_SASPHY4_PAGEVERSION (0x00) ++ ++/*values for the Flags field */ ++#define MPI2_SASPHY4_FLAGS_FRAME_VALID (0x02) ++#define MPI2_SASPHY4_FLAGS_SATA_FRAME (0x01) ++ ++ ++ ++ ++/**************************************************************************** ++* SAS Port Config Pages ++****************************************************************************/ ++ ++/*SAS Port Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_PORT_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U8 ++ PortNumber; /*0x08 */ ++ U8 ++ PhysicalPort; /*0x09 */ ++ U8 ++ PortWidth; /*0x0A */ ++ U8 ++ PhysicalPortWidth; /*0x0B */ ++ U8 ++ ZoneGroup; /*0x0C */ ++ U8 ++ Reserved1; /*0x0D */ ++ U16 ++ Reserved2; /*0x0E */ ++ U64 ++ SASAddress; /*0x10 */ ++ U32 ++ DeviceInfo; /*0x18 */ ++ U32 ++ Reserved3; /*0x1C */ ++ U32 ++ Reserved4; /*0x20 */ ++} MPI2_CONFIG_PAGE_SAS_PORT_0, ++ *PTR_MPI2_CONFIG_PAGE_SAS_PORT_0, ++ Mpi2SasPortPage0_t, *pMpi2SasPortPage0_t; ++ ++#define MPI2_SASPORT0_PAGEVERSION (0x00) ++ ++/*see mpi2_sas.h for values for SAS Port Page 0 DeviceInfo values */ ++ ++ ++/**************************************************************************** ++* SAS Enclosure Config Pages ++****************************************************************************/ ++ ++/*SAS Enclosure Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ Reserved1; /*0x08 */ ++ U64 ++ EnclosureLogicalID; /*0x0C */ ++ U16 ++ Flags; /*0x14 */ ++ U16 ++ EnclosureHandle; /*0x16 */ ++ U16 ++ NumSlots; /*0x18 */ ++ U16 ++ StartSlot; /*0x1A */ ++ U8 ++ Reserved2; /*0x1C */ ++ U8 ++ EnclosureLevel; /*0x1D */ ++ U16 ++ SEPDevHandle; /*0x1E */ ++ U32 ++ Reserved3; /*0x20 */ ++ U32 ++ Reserved4; /*0x24 */ ++} MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0, ++ *PTR_MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0, ++ Mpi2SasEnclosurePage0_t, *pMpi2SasEnclosurePage0_t; ++ ++#define MPI2_SASENCLOSURE0_PAGEVERSION (0x04) ++ ++/*values for SAS Enclosure Page 0 Flags field */ ++#define MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID (0x0010) ++#define MPI2_SAS_ENCLS0_FLAGS_MNG_MASK (0x000F) ++#define MPI2_SAS_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000) ++#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SES (0x0001) ++#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SGPIO (0x0002) ++#define MPI2_SAS_ENCLS0_FLAGS_MNG_EXP_SGPIO (0x0003) ++#define MPI2_SAS_ENCLS0_FLAGS_MNG_SES_ENCLOSURE (0x0004) ++#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_GPIO (0x0005) ++ ++ ++/**************************************************************************** ++* Log Config Page ++****************************************************************************/ ++ ++/*Log Page 0 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumLogEntries at runtime. ++ */ ++#ifndef MPI2_LOG_0_NUM_LOG_ENTRIES ++#define MPI2_LOG_0_NUM_LOG_ENTRIES (1) ++#endif ++ ++#define MPI2_LOG_0_LOG_DATA_LENGTH (0x1C) ++ ++typedef struct _MPI2_LOG_0_ENTRY { ++ U64 TimeStamp; /*0x00 */ ++ U32 Reserved1; /*0x08 */ ++ U16 LogSequence; /*0x0C */ ++ U16 LogEntryQualifier; /*0x0E */ ++ U8 VP_ID; /*0x10 */ ++ U8 VF_ID; /*0x11 */ ++ U16 Reserved2; /*0x12 */ ++ U8 ++ LogData[MPI2_LOG_0_LOG_DATA_LENGTH];/*0x14 */ ++} MPI2_LOG_0_ENTRY, *PTR_MPI2_LOG_0_ENTRY, ++ Mpi2Log0Entry_t, *pMpi2Log0Entry_t; ++ ++/*values for Log Page 0 LogEntry LogEntryQualifier field */ ++#define MPI2_LOG_0_ENTRY_QUAL_ENTRY_UNUSED (0x0000) ++#define MPI2_LOG_0_ENTRY_QUAL_POWER_ON_RESET (0x0001) ++#define MPI2_LOG_0_ENTRY_QUAL_TIMESTAMP_UPDATE (0x0002) ++#define MPI2_LOG_0_ENTRY_QUAL_MIN_IMPLEMENT_SPEC (0x8000) ++#define MPI2_LOG_0_ENTRY_QUAL_MAX_IMPLEMENT_SPEC (0xFFFF) ++ ++typedef struct _MPI2_CONFIG_PAGE_LOG_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U32 Reserved1; /*0x08 */ ++ U32 Reserved2; /*0x0C */ ++ U16 NumLogEntries;/*0x10 */ ++ U16 Reserved3; /*0x12 */ ++ MPI2_LOG_0_ENTRY ++ LogEntry[MPI2_LOG_0_NUM_LOG_ENTRIES]; /*0x14 */ ++} MPI2_CONFIG_PAGE_LOG_0, *PTR_MPI2_CONFIG_PAGE_LOG_0, ++ Mpi2LogPage0_t, *pMpi2LogPage0_t; ++ ++#define MPI2_LOG_0_PAGEVERSION (0x02) ++ ++ ++/**************************************************************************** ++* RAID Config Page ++****************************************************************************/ ++ ++/*RAID Page 0 */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check the value returned for NumElements at runtime. ++ */ ++#ifndef MPI2_RAIDCONFIG0_MAX_ELEMENTS ++#define MPI2_RAIDCONFIG0_MAX_ELEMENTS (1) ++#endif ++ ++typedef struct _MPI2_RAIDCONFIG0_CONFIG_ELEMENT { ++ U16 ElementFlags; /*0x00 */ ++ U16 VolDevHandle; /*0x02 */ ++ U8 HotSparePool; /*0x04 */ ++ U8 PhysDiskNum; /*0x05 */ ++ U16 PhysDiskDevHandle; /*0x06 */ ++} MPI2_RAIDCONFIG0_CONFIG_ELEMENT, ++ *PTR_MPI2_RAIDCONFIG0_CONFIG_ELEMENT, ++ Mpi2RaidConfig0ConfigElement_t, ++ *pMpi2RaidConfig0ConfigElement_t; ++ ++/*values for the ElementFlags field */ ++#define MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE (0x000F) ++#define MPI2_RAIDCONFIG0_EFLAGS_VOLUME_ELEMENT (0x0000) ++#define MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT (0x0001) ++#define MPI2_RAIDCONFIG0_EFLAGS_HOT_SPARE_ELEMENT (0x0002) ++#define MPI2_RAIDCONFIG0_EFLAGS_OCE_ELEMENT (0x0003) ++ ++ ++typedef struct _MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U8 NumHotSpares; /*0x08 */ ++ U8 NumPhysDisks; /*0x09 */ ++ U8 NumVolumes; /*0x0A */ ++ U8 ConfigNum; /*0x0B */ ++ U32 Flags; /*0x0C */ ++ U8 ConfigGUID[24]; /*0x10 */ ++ U32 Reserved1; /*0x28 */ ++ U8 NumElements; /*0x2C */ ++ U8 Reserved2; /*0x2D */ ++ U16 Reserved3; /*0x2E */ ++ MPI2_RAIDCONFIG0_CONFIG_ELEMENT ++ ConfigElement[MPI2_RAIDCONFIG0_MAX_ELEMENTS]; /*0x30 */ ++} MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0, ++ *PTR_MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0, ++ Mpi2RaidConfigurationPage0_t, ++ *pMpi2RaidConfigurationPage0_t; ++ ++#define MPI2_RAIDCONFIG0_PAGEVERSION (0x00) ++ ++/*values for RAID Configuration Page 0 Flags field */ ++#define MPI2_RAIDCONFIG0_FLAG_FOREIGN_CONFIG (0x00000001) ++ ++ ++/**************************************************************************** ++* Driver Persistent Mapping Config Pages ++****************************************************************************/ ++ ++/*Driver Persistent Mapping Page 0 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY { ++ U64 PhysicalIdentifier; /*0x00 */ ++ U16 MappingInformation; /*0x08 */ ++ U16 DeviceIndex; /*0x0A */ ++ U32 PhysicalBitsMapping; /*0x0C */ ++ U32 Reserved1; /*0x10 */ ++} MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY, ++ *PTR_MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY, ++ Mpi2DriverMap0Entry_t, *pMpi2DriverMap0Entry_t; ++ ++typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAPPING_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY Entry; /*0x08 */ ++} MPI2_CONFIG_PAGE_DRIVER_MAPPING_0, ++ *PTR_MPI2_CONFIG_PAGE_DRIVER_MAPPING_0, ++ Mpi2DriverMappingPage0_t, *pMpi2DriverMappingPage0_t; ++ ++#define MPI2_DRIVERMAPPING0_PAGEVERSION (0x00) ++ ++/*values for Driver Persistent Mapping Page 0 MappingInformation field */ ++#define MPI2_DRVMAP0_MAPINFO_SLOT_MASK (0x07F0) ++#define MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT (4) ++#define MPI2_DRVMAP0_MAPINFO_MISSING_MASK (0x000F) ++ ++ ++/**************************************************************************** ++* Ethernet Config Pages ++****************************************************************************/ ++ ++/*Ethernet Page 0 */ ++ ++/*IP address (union of IPv4 and IPv6) */ ++typedef union _MPI2_ETHERNET_IP_ADDR { ++ U32 IPv4Addr; ++ U32 IPv6Addr[4]; ++} MPI2_ETHERNET_IP_ADDR, *PTR_MPI2_ETHERNET_IP_ADDR, ++ Mpi2EthernetIpAddr_t, *pMpi2EthernetIpAddr_t; ++ ++#define MPI2_ETHERNET_HOST_NAME_LENGTH (32) ++ ++typedef struct _MPI2_CONFIG_PAGE_ETHERNET_0 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ ++ U8 NumInterfaces; /*0x08 */ ++ U8 Reserved0; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U32 Status; /*0x0C */ ++ U8 MediaState; /*0x10 */ ++ U8 Reserved2; /*0x11 */ ++ U16 Reserved3; /*0x12 */ ++ U8 MacAddress[6]; /*0x14 */ ++ U8 Reserved4; /*0x1A */ ++ U8 Reserved5; /*0x1B */ ++ MPI2_ETHERNET_IP_ADDR IpAddress; /*0x1C */ ++ MPI2_ETHERNET_IP_ADDR SubnetMask; /*0x2C */ ++ MPI2_ETHERNET_IP_ADDR GatewayIpAddress;/*0x3C */ ++ MPI2_ETHERNET_IP_ADDR DNS1IpAddress; /*0x4C */ ++ MPI2_ETHERNET_IP_ADDR DNS2IpAddress; /*0x5C */ ++ MPI2_ETHERNET_IP_ADDR DhcpIpAddress; /*0x6C */ ++ U8 ++ HostName[MPI2_ETHERNET_HOST_NAME_LENGTH];/*0x7C */ ++} MPI2_CONFIG_PAGE_ETHERNET_0, ++ *PTR_MPI2_CONFIG_PAGE_ETHERNET_0, ++ Mpi2EthernetPage0_t, *pMpi2EthernetPage0_t; ++ ++#define MPI2_ETHERNETPAGE0_PAGEVERSION (0x00) ++ ++/*values for Ethernet Page 0 Status field */ ++#define MPI2_ETHPG0_STATUS_IPV6_CAPABLE (0x80000000) ++#define MPI2_ETHPG0_STATUS_IPV4_CAPABLE (0x40000000) ++#define MPI2_ETHPG0_STATUS_CONSOLE_CONNECTED (0x20000000) ++#define MPI2_ETHPG0_STATUS_DEFAULT_IF (0x00000100) ++#define MPI2_ETHPG0_STATUS_FW_DWNLD_ENABLED (0x00000080) ++#define MPI2_ETHPG0_STATUS_TELNET_ENABLED (0x00000040) ++#define MPI2_ETHPG0_STATUS_SSH2_ENABLED (0x00000020) ++#define MPI2_ETHPG0_STATUS_DHCP_CLIENT_ENABLED (0x00000010) ++#define MPI2_ETHPG0_STATUS_IPV6_ENABLED (0x00000008) ++#define MPI2_ETHPG0_STATUS_IPV4_ENABLED (0x00000004) ++#define MPI2_ETHPG0_STATUS_IPV6_ADDRESSES (0x00000002) ++#define MPI2_ETHPG0_STATUS_ETH_IF_ENABLED (0x00000001) ++ ++/*values for Ethernet Page 0 MediaState field */ ++#define MPI2_ETHPG0_MS_DUPLEX_MASK (0x80) ++#define MPI2_ETHPG0_MS_HALF_DUPLEX (0x00) ++#define MPI2_ETHPG0_MS_FULL_DUPLEX (0x80) ++ ++#define MPI2_ETHPG0_MS_CONNECT_SPEED_MASK (0x07) ++#define MPI2_ETHPG0_MS_NOT_CONNECTED (0x00) ++#define MPI2_ETHPG0_MS_10MBIT (0x01) ++#define MPI2_ETHPG0_MS_100MBIT (0x02) ++#define MPI2_ETHPG0_MS_1GBIT (0x03) ++ ++ ++/*Ethernet Page 1 */ ++ ++typedef struct _MPI2_CONFIG_PAGE_ETHERNET_1 { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ Reserved0; /*0x08 */ ++ U32 ++ Flags; /*0x0C */ ++ U8 ++ MediaState; /*0x10 */ ++ U8 ++ Reserved1; /*0x11 */ ++ U16 ++ Reserved2; /*0x12 */ ++ U8 ++ MacAddress[6]; /*0x14 */ ++ U8 ++ Reserved3; /*0x1A */ ++ U8 ++ Reserved4; /*0x1B */ ++ MPI2_ETHERNET_IP_ADDR ++ StaticIpAddress; /*0x1C */ ++ MPI2_ETHERNET_IP_ADDR ++ StaticSubnetMask; /*0x2C */ ++ MPI2_ETHERNET_IP_ADDR ++ StaticGatewayIpAddress; /*0x3C */ ++ MPI2_ETHERNET_IP_ADDR ++ StaticDNS1IpAddress; /*0x4C */ ++ MPI2_ETHERNET_IP_ADDR ++ StaticDNS2IpAddress; /*0x5C */ ++ U32 ++ Reserved5; /*0x6C */ ++ U32 ++ Reserved6; /*0x70 */ ++ U32 ++ Reserved7; /*0x74 */ ++ U32 ++ Reserved8; /*0x78 */ ++ U8 ++ HostName[MPI2_ETHERNET_HOST_NAME_LENGTH];/*0x7C */ ++} MPI2_CONFIG_PAGE_ETHERNET_1, ++ *PTR_MPI2_CONFIG_PAGE_ETHERNET_1, ++ Mpi2EthernetPage1_t, *pMpi2EthernetPage1_t; ++ ++#define MPI2_ETHERNETPAGE1_PAGEVERSION (0x00) ++ ++/*values for Ethernet Page 1 Flags field */ ++#define MPI2_ETHPG1_FLAG_SET_DEFAULT_IF (0x00000100) ++#define MPI2_ETHPG1_FLAG_ENABLE_FW_DOWNLOAD (0x00000080) ++#define MPI2_ETHPG1_FLAG_ENABLE_TELNET (0x00000040) ++#define MPI2_ETHPG1_FLAG_ENABLE_SSH2 (0x00000020) ++#define MPI2_ETHPG1_FLAG_ENABLE_DHCP_CLIENT (0x00000010) ++#define MPI2_ETHPG1_FLAG_ENABLE_IPV6 (0x00000008) ++#define MPI2_ETHPG1_FLAG_ENABLE_IPV4 (0x00000004) ++#define MPI2_ETHPG1_FLAG_USE_IPV6_ADDRESSES (0x00000002) ++#define MPI2_ETHPG1_FLAG_ENABLE_ETH_IF (0x00000001) ++ ++/*values for Ethernet Page 1 MediaState field */ ++#define MPI2_ETHPG1_MS_DUPLEX_MASK (0x80) ++#define MPI2_ETHPG1_MS_HALF_DUPLEX (0x00) ++#define MPI2_ETHPG1_MS_FULL_DUPLEX (0x80) ++ ++#define MPI2_ETHPG1_MS_DATA_RATE_MASK (0x07) ++#define MPI2_ETHPG1_MS_DATA_RATE_AUTO (0x00) ++#define MPI2_ETHPG1_MS_DATA_RATE_10MBIT (0x01) ++#define MPI2_ETHPG1_MS_DATA_RATE_100MBIT (0x02) ++#define MPI2_ETHPG1_MS_DATA_RATE_1GBIT (0x03) ++ ++ ++/**************************************************************************** ++* Extended Manufacturing Config Pages ++****************************************************************************/ ++ ++/* ++ *Generic structure to use for product-specific extended manufacturing pages ++ *(currently Extended Manufacturing Page 40 through Extended Manufacturing ++ *Page 60). ++ */ ++ ++typedef struct _MPI2_CONFIG_PAGE_EXT_MAN_PS { ++ MPI2_CONFIG_EXTENDED_PAGE_HEADER ++ Header; /*0x00 */ ++ U32 ++ ProductSpecificInfo; /*0x08 */ ++} MPI2_CONFIG_PAGE_EXT_MAN_PS, ++ *PTR_MPI2_CONFIG_PAGE_EXT_MAN_PS, ++ Mpi2ExtManufacturingPagePS_t, ++ *pMpi2ExtManufacturingPagePS_t; ++ ++/*PageVersion should be provided by product-specific code */ ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_init.h b/drivers/scsi/mpt2sas/mpi/mpi2_init.h +new file mode 100644 +index 0000000..bba56b6 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2_init.h +@@ -0,0 +1,581 @@ ++/* ++ * Copyright 2000-2015 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2_init.h ++ * Title: MPI SCSI initiator mode messages and structures ++ * Creation Date: June 23, 2006 ++ * ++ * mpi2_init.h Version: 02.00.20 ++ * ++ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 ++ * prefix are for use only on MPI v2.5 products, and must not be used ++ * with MPI v2.0 products. Unless otherwise noted, names beginning with ++ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products. ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 10-31-07 02.00.01 Fixed name for pMpi2SCSITaskManagementRequest_t. ++ * 12-18-07 02.00.02 Modified Task Management Target Reset Method defines. ++ * 02-29-08 02.00.03 Added Query Task Set and Query Unit Attention. ++ * 03-03-08 02.00.04 Fixed name of struct _MPI2_SCSI_TASK_MANAGE_REPLY. ++ * 05-21-08 02.00.05 Fixed typo in name of Mpi2SepRequest_t. ++ * 10-02-08 02.00.06 Removed Untagged and No Disconnect values from SCSI IO ++ * Control field Task Attribute flags. ++ * Moved LUN field defines to mpi2.h becasue they are ++ * common to many structures. ++ * 05-06-09 02.00.07 Changed task management type of Query Unit Attention to ++ * Query Asynchronous Event. ++ * Defined two new bits in the SlotStatus field of the SCSI ++ * Enclosure Processor Request and Reply. ++ * 10-28-09 02.00.08 Added defines for decoding the ResponseInfo bytes for ++ * both SCSI IO Error Reply and SCSI Task Management Reply. ++ * Added ResponseInfo field to MPI2_SCSI_TASK_MANAGE_REPLY. ++ * Added MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG define. ++ * 02-10-10 02.00.09 Removed unused structure that had "#if 0" around it. ++ * 05-12-10 02.00.10 Added optional vendor-unique region to SCSI IO Request. ++ * 11-10-10 02.00.11 Added MPI2_SCSIIO_NUM_SGLOFFSETS define. ++ * 11-18-11 02.00.12 Incorporating additions for MPI v2.5. ++ * 02-06-12 02.00.13 Added alternate defines for Task Priority / Command ++ * Priority to match SAM-4. ++ * Added EEDPErrorOffset to MPI2_SCSI_IO_REPLY. ++ * 07-10-12 02.00.14 Added MPI2_SCSIIO_CONTROL_SHIFT_DATADIRECTION. ++ * 04-09-13 02.00.15 Added SCSIStatusQualifier field to MPI2_SCSI_IO_REPLY, ++ * replacing the Reserved4 field. ++ * 11-18-14 02.00.16 Updated copyright information. ++ * 03-16-15 02.00.17 Updated for MPI v2.6. ++ * Added MPI26_SCSIIO_IOFLAGS_ESCAPE_PASSTHROUGH. ++ * Added MPI2_SEP_REQ_SLOTSTATUS_DEV_OFF and ++ * MPI2_SEP_REPLY_SLOTSTATUS_DEV_OFF. ++ * 08-26-15 02.00.18 Added SCSITASKMGMT_MSGFLAGS for Target Reset. ++ * 12-18-15 02.00.19 Added EEDPObservedValue added to SCSI IO Reply message. ++ * 01-04-16 02.00.20 Modified EEDP reported values in SCSI IO Reply message. ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_INIT_H ++#define MPI2_INIT_H ++ ++/***************************************************************************** ++* ++* SCSI Initiator Messages ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* SCSI IO messages and associated structures ++****************************************************************************/ ++ ++typedef struct _MPI2_SCSI_IO_CDB_EEDP32 { ++ U8 CDB[20]; /*0x00 */ ++ U32 PrimaryReferenceTag; /*0x14 */ ++ U16 PrimaryApplicationTag; /*0x18 */ ++ U16 PrimaryApplicationTagMask; /*0x1A */ ++ U32 TransferLength; /*0x1C */ ++} MPI2_SCSI_IO_CDB_EEDP32, *PTR_MPI2_SCSI_IO_CDB_EEDP32, ++ Mpi2ScsiIoCdbEedp32_t, *pMpi2ScsiIoCdbEedp32_t; ++ ++/*MPI v2.0 CDB field */ ++typedef union _MPI2_SCSI_IO_CDB_UNION { ++ U8 CDB32[32]; ++ MPI2_SCSI_IO_CDB_EEDP32 EEDP32; ++ MPI2_SGE_SIMPLE_UNION SGE; ++} MPI2_SCSI_IO_CDB_UNION, *PTR_MPI2_SCSI_IO_CDB_UNION, ++ Mpi2ScsiIoCdb_t, *pMpi2ScsiIoCdb_t; ++ ++/*MPI v2.0 SCSI IO Request Message */ ++typedef struct _MPI2_SCSI_IO_REQUEST { ++ U16 DevHandle; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved1; /*0x04 */ ++ U8 Reserved2; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U32 SenseBufferLowAddress; /*0x0C */ ++ U16 SGLFlags; /*0x10 */ ++ U8 SenseBufferLength; /*0x12 */ ++ U8 Reserved4; /*0x13 */ ++ U8 SGLOffset0; /*0x14 */ ++ U8 SGLOffset1; /*0x15 */ ++ U8 SGLOffset2; /*0x16 */ ++ U8 SGLOffset3; /*0x17 */ ++ U32 SkipCount; /*0x18 */ ++ U32 DataLength; /*0x1C */ ++ U32 BidirectionalDataLength; /*0x20 */ ++ U16 IoFlags; /*0x24 */ ++ U16 EEDPFlags; /*0x26 */ ++ U32 EEDPBlockSize; /*0x28 */ ++ U32 SecondaryReferenceTag; /*0x2C */ ++ U16 SecondaryApplicationTag; /*0x30 */ ++ U16 ApplicationTagTranslationMask; /*0x32 */ ++ U8 LUN[8]; /*0x34 */ ++ U32 Control; /*0x3C */ ++ MPI2_SCSI_IO_CDB_UNION CDB; /*0x40 */ ++ ++#ifdef MPI2_SCSI_IO_VENDOR_UNIQUE_REGION /*typically this is left undefined */ ++ MPI2_SCSI_IO_VENDOR_UNIQUE VendorRegion; ++#endif ++ ++ MPI2_SGE_IO_UNION SGL; /*0x60 */ ++ ++} MPI2_SCSI_IO_REQUEST, *PTR_MPI2_SCSI_IO_REQUEST, ++ Mpi2SCSIIORequest_t, *pMpi2SCSIIORequest_t; ++ ++/*SCSI IO MsgFlags bits */ ++ ++/*MsgFlags for SenseBufferAddressSpace */ ++#define MPI2_SCSIIO_MSGFLAGS_MASK_SENSE_ADDR (0x0C) ++#define MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR (0x00) ++#define MPI2_SCSIIO_MSGFLAGS_IOCDDR_SENSE_ADDR (0x04) ++#define MPI2_SCSIIO_MSGFLAGS_IOCPLB_SENSE_ADDR (0x08) ++#define MPI2_SCSIIO_MSGFLAGS_IOCPLBNTA_SENSE_ADDR (0x0C) ++#define MPI26_SCSIIO_MSGFLAGS_IOCCTL_SENSE_ADDR (0x08) ++ ++/*SCSI IO SGLFlags bits */ ++ ++/*base values for Data Location Address Space */ ++#define MPI2_SCSIIO_SGLFLAGS_ADDR_MASK (0x0C) ++#define MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR (0x00) ++#define MPI2_SCSIIO_SGLFLAGS_IOCDDR_ADDR (0x04) ++#define MPI2_SCSIIO_SGLFLAGS_IOCPLB_ADDR (0x08) ++#define MPI2_SCSIIO_SGLFLAGS_IOCPLBNTA_ADDR (0x0C) ++ ++/*base values for Type */ ++#define MPI2_SCSIIO_SGLFLAGS_TYPE_MASK (0x03) ++#define MPI2_SCSIIO_SGLFLAGS_TYPE_MPI (0x00) ++#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE32 (0x01) ++#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE64 (0x02) ++ ++/*shift values for each sub-field */ ++#define MPI2_SCSIIO_SGLFLAGS_SGL3_SHIFT (12) ++#define MPI2_SCSIIO_SGLFLAGS_SGL2_SHIFT (8) ++#define MPI2_SCSIIO_SGLFLAGS_SGL1_SHIFT (4) ++#define MPI2_SCSIIO_SGLFLAGS_SGL0_SHIFT (0) ++ ++/*number of SGLOffset fields */ ++#define MPI2_SCSIIO_NUM_SGLOFFSETS (4) ++ ++/*SCSI IO IoFlags bits */ ++ ++/*Large CDB Address Space */ ++#define MPI2_SCSIIO_CDB_ADDR_MASK (0x6000) ++#define MPI2_SCSIIO_CDB_ADDR_SYSTEM (0x0000) ++#define MPI2_SCSIIO_CDB_ADDR_IOCDDR (0x2000) ++#define MPI2_SCSIIO_CDB_ADDR_IOCPLB (0x4000) ++#define MPI2_SCSIIO_CDB_ADDR_IOCPLBNTA (0x6000) ++ ++#define MPI2_SCSIIO_IOFLAGS_LARGE_CDB (0x1000) ++#define MPI2_SCSIIO_IOFLAGS_BIDIRECTIONAL (0x0800) ++#define MPI2_SCSIIO_IOFLAGS_MULTICAST (0x0400) ++#define MPI2_SCSIIO_IOFLAGS_CMD_DETERMINES_DATA_DIR (0x0200) ++#define MPI2_SCSIIO_IOFLAGS_CDBLENGTH_MASK (0x01FF) ++ ++/*SCSI IO EEDPFlags bits */ ++ ++#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG (0x8000) ++#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_REFTAG (0x4000) ++#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_APPTAG (0x2000) ++#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_APPTAG (0x1000) ++ ++#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG (0x0400) ++#define MPI2_SCSIIO_EEDPFLAGS_CHECK_APPTAG (0x0200) ++#define MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD (0x0100) ++ ++#define MPI2_SCSIIO_EEDPFLAGS_PASSTHRU_REFTAG (0x0008) ++ ++#define MPI2_SCSIIO_EEDPFLAGS_MASK_OP (0x0007) ++#define MPI2_SCSIIO_EEDPFLAGS_NOOP_OP (0x0000) ++#define MPI2_SCSIIO_EEDPFLAGS_CHECK_OP (0x0001) ++#define MPI2_SCSIIO_EEDPFLAGS_STRIP_OP (0x0002) ++#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP (0x0003) ++#define MPI2_SCSIIO_EEDPFLAGS_INSERT_OP (0x0004) ++#define MPI2_SCSIIO_EEDPFLAGS_REPLACE_OP (0x0006) ++#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REGEN_OP (0x0007) ++ ++/*SCSI IO LUN fields: use MPI2_LUN_ from mpi2.h */ ++ ++/*SCSI IO Control bits */ ++#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_MASK (0xFC000000) ++#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26) ++ ++#define MPI2_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000) ++#define MPI2_SCSIIO_CONTROL_SHIFT_DATADIRECTION (24) ++#define MPI2_SCSIIO_CONTROL_NODATATRANSFER (0x00000000) ++#define MPI2_SCSIIO_CONTROL_WRITE (0x01000000) ++#define MPI2_SCSIIO_CONTROL_READ (0x02000000) ++#define MPI2_SCSIIO_CONTROL_BIDIRECTIONAL (0x03000000) ++ ++#define MPI2_SCSIIO_CONTROL_TASKPRI_MASK (0x00007800) ++#define MPI2_SCSIIO_CONTROL_TASKPRI_SHIFT (11) ++/*alternate name for the previous field; called Command Priority in SAM-4 */ ++#define MPI2_SCSIIO_CONTROL_CMDPRI_MASK (0x00007800) ++#define MPI2_SCSIIO_CONTROL_CMDPRI_SHIFT (11) ++ ++#define MPI2_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700) ++#define MPI2_SCSIIO_CONTROL_SIMPLEQ (0x00000000) ++#define MPI2_SCSIIO_CONTROL_HEADOFQ (0x00000100) ++#define MPI2_SCSIIO_CONTROL_ORDEREDQ (0x00000200) ++#define MPI2_SCSIIO_CONTROL_ACAQ (0x00000400) ++ ++#define MPI2_SCSIIO_CONTROL_TLR_MASK (0x000000C0) ++#define MPI2_SCSIIO_CONTROL_NO_TLR (0x00000000) ++#define MPI2_SCSIIO_CONTROL_TLR_ON (0x00000040) ++#define MPI2_SCSIIO_CONTROL_TLR_OFF (0x00000080) ++ ++/*MPI v2.5 CDB field */ ++typedef union _MPI25_SCSI_IO_CDB_UNION { ++ U8 CDB32[32]; ++ MPI2_SCSI_IO_CDB_EEDP32 EEDP32; ++ MPI2_IEEE_SGE_SIMPLE64 SGE; ++} MPI25_SCSI_IO_CDB_UNION, *PTR_MPI25_SCSI_IO_CDB_UNION, ++ Mpi25ScsiIoCdb_t, *pMpi25ScsiIoCdb_t; ++ ++/*MPI v2.5/2.6 SCSI IO Request Message */ ++typedef struct _MPI25_SCSI_IO_REQUEST { ++ U16 DevHandle; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved1; /*0x04 */ ++ U8 Reserved2; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U32 SenseBufferLowAddress; /*0x0C */ ++ U8 DMAFlags; /*0x10 */ ++ U8 Reserved5; /*0x11 */ ++ U8 SenseBufferLength; /*0x12 */ ++ U8 Reserved4; /*0x13 */ ++ U8 SGLOffset0; /*0x14 */ ++ U8 SGLOffset1; /*0x15 */ ++ U8 SGLOffset2; /*0x16 */ ++ U8 SGLOffset3; /*0x17 */ ++ U32 SkipCount; /*0x18 */ ++ U32 DataLength; /*0x1C */ ++ U32 BidirectionalDataLength; /*0x20 */ ++ U16 IoFlags; /*0x24 */ ++ U16 EEDPFlags; /*0x26 */ ++ U16 EEDPBlockSize; /*0x28 */ ++ U16 Reserved6; /*0x2A */ ++ U32 SecondaryReferenceTag; /*0x2C */ ++ U16 SecondaryApplicationTag; /*0x30 */ ++ U16 ApplicationTagTranslationMask; /*0x32 */ ++ U8 LUN[8]; /*0x34 */ ++ U32 Control; /*0x3C */ ++ MPI25_SCSI_IO_CDB_UNION CDB; /*0x40 */ ++ ++#ifdef MPI25_SCSI_IO_VENDOR_UNIQUE_REGION /*typically this is left undefined */ ++ MPI25_SCSI_IO_VENDOR_UNIQUE VendorRegion; ++#endif ++ ++ MPI25_SGE_IO_UNION SGL; /*0x60 */ ++ ++} MPI25_SCSI_IO_REQUEST, *PTR_MPI25_SCSI_IO_REQUEST, ++ Mpi25SCSIIORequest_t, *pMpi25SCSIIORequest_t; ++ ++/*use MPI2_SCSIIO_MSGFLAGS_ defines for the MsgFlags field */ ++ ++/*Defines for the DMAFlags field ++ * Each setting affects 4 SGLS, from SGL0 to SGL3. ++ * D = Data ++ * C = Cache DIF ++ * I = Interleaved ++ * H = Host DIF ++ */ ++#define MPI25_SCSIIO_DMAFLAGS_OP_MASK (0x0F) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_D_D (0x00) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_D_C (0x01) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_D_I (0x02) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_C_C (0x03) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_C_I (0x04) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_I_I (0x05) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_C_C_C (0x06) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_C_C_I (0x07) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_C_I_I (0x08) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_I_I_I (0x09) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_D_D (0x0A) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_D_C (0x0B) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_D_I (0x0C) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_C_C (0x0D) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_C_I (0x0E) ++#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_I_I (0x0F) ++ ++/*number of SGLOffset fields */ ++#define MPI25_SCSIIO_NUM_SGLOFFSETS (4) ++ ++/*defines for the IoFlags field */ ++#define MPI25_SCSIIO_IOFLAGS_IO_PATH_MASK (0xC000) ++#define MPI25_SCSIIO_IOFLAGS_NORMAL_PATH (0x0000) ++#define MPI25_SCSIIO_IOFLAGS_FAST_PATH (0x4000) ++ ++#define MPI26_SCSIIO_IOFLAGS_ESCAPE_PASSTHROUGH (0x2000) ++#define MPI25_SCSIIO_IOFLAGS_LARGE_CDB (0x1000) ++#define MPI25_SCSIIO_IOFLAGS_BIDIRECTIONAL (0x0800) ++#define MPI26_SCSIIO_IOFLAGS_PORT_REQUEST (0x0400) ++#define MPI25_SCSIIO_IOFLAGS_CDBLENGTH_MASK (0x01FF) ++ ++/*MPI v2.5 defines for the EEDPFlags bits */ ++/*use MPI2_SCSIIO_EEDPFLAGS_ defines for the other EEDPFlags bits */ ++#define MPI25_SCSIIO_EEDPFLAGS_ESCAPE_MODE_MASK (0x00C0) ++#define MPI25_SCSIIO_EEDPFLAGS_COMPATIBLE_MODE (0x0000) ++#define MPI25_SCSIIO_EEDPFLAGS_DO_NOT_DISABLE_MODE (0x0040) ++#define MPI25_SCSIIO_EEDPFLAGS_APPTAG_DISABLE_MODE (0x0080) ++#define MPI25_SCSIIO_EEDPFLAGS_APPTAG_REFTAG_DISABLE_MODE (0x00C0) ++ ++#define MPI25_SCSIIO_EEDPFLAGS_HOST_GUARD_METHOD_MASK (0x0030) ++#define MPI25_SCSIIO_EEDPFLAGS_T10_CRC_HOST_GUARD (0x0000) ++#define MPI25_SCSIIO_EEDPFLAGS_IP_CHKSUM_HOST_GUARD (0x0010) ++ ++/*use MPI2_LUN_ defines from mpi2.h for the LUN field */ ++ ++/*use MPI2_SCSIIO_CONTROL_ defines for the Control field */ ++ ++/*NOTE: The SCSI IO Reply is nearly the same for MPI 2.0 and MPI 2.5, so ++ * MPI2_SCSI_IO_REPLY is used for both. ++ */ ++ ++/*SCSI IO Error Reply Message */ ++typedef struct _MPI2_SCSI_IO_REPLY { ++ U16 DevHandle; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved1; /*0x04 */ ++ U8 Reserved2; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U8 SCSIStatus; /*0x0C */ ++ U8 SCSIState; /*0x0D */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U32 TransferCount; /*0x14 */ ++ U32 SenseCount; /*0x18 */ ++ U32 ResponseInfo; /*0x1C */ ++ U16 TaskTag; /*0x20 */ ++ U16 SCSIStatusQualifier; /* 0x22 */ ++ U32 BidirectionalTransferCount; /*0x24 */ ++ /* MPI 2.5+ only; Reserved in MPI 2.0 */ ++ U32 EEDPErrorOffset; /* 0x28 */ ++ /* MPI 2.5+ only; Reserved in MPI 2.0 */ ++ U16 EEDPObservedAppTag; /* 0x2C */ ++ /* MPI 2.5+ only; Reserved in MPI 2.0 */ ++ U16 EEDPObservedGuard; /* 0x2E */ ++ /* MPI 2.5+ only; Reserved in MPI 2.0 */ ++ U32 EEDPObservedRefTag; /* 0x30 */ ++} MPI2_SCSI_IO_REPLY, *PTR_MPI2_SCSI_IO_REPLY, ++ Mpi2SCSIIOReply_t, *pMpi2SCSIIOReply_t; ++ ++/*SCSI IO Reply SCSIStatus values (SAM-4 status codes) */ ++ ++#define MPI2_SCSI_STATUS_GOOD (0x00) ++#define MPI2_SCSI_STATUS_CHECK_CONDITION (0x02) ++#define MPI2_SCSI_STATUS_CONDITION_MET (0x04) ++#define MPI2_SCSI_STATUS_BUSY (0x08) ++#define MPI2_SCSI_STATUS_INTERMEDIATE (0x10) ++#define MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14) ++#define MPI2_SCSI_STATUS_RESERVATION_CONFLICT (0x18) ++#define MPI2_SCSI_STATUS_COMMAND_TERMINATED (0x22) /*obsolete */ ++#define MPI2_SCSI_STATUS_TASK_SET_FULL (0x28) ++#define MPI2_SCSI_STATUS_ACA_ACTIVE (0x30) ++#define MPI2_SCSI_STATUS_TASK_ABORTED (0x40) ++ ++/*SCSI IO Reply SCSIState flags */ ++ ++#define MPI2_SCSI_STATE_RESPONSE_INFO_VALID (0x10) ++#define MPI2_SCSI_STATE_TERMINATED (0x08) ++#define MPI2_SCSI_STATE_NO_SCSI_STATUS (0x04) ++#define MPI2_SCSI_STATE_AUTOSENSE_FAILED (0x02) ++#define MPI2_SCSI_STATE_AUTOSENSE_VALID (0x01) ++ ++/*masks and shifts for the ResponseInfo field */ ++ ++#define MPI2_SCSI_RI_MASK_REASONCODE (0x000000FF) ++#define MPI2_SCSI_RI_SHIFT_REASONCODE (0) ++ ++#define MPI2_SCSI_TASKTAG_UNKNOWN (0xFFFF) ++ ++/**************************************************************************** ++* SCSI Task Management messages ++****************************************************************************/ ++ ++/*SCSI Task Management Request Message */ ++typedef struct _MPI2_SCSI_TASK_MANAGE_REQUEST { ++ U16 DevHandle; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U8 Reserved1; /*0x04 */ ++ U8 TaskType; /*0x05 */ ++ U8 Reserved2; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U8 LUN[8]; /*0x0C */ ++ U32 Reserved4[7]; /*0x14 */ ++ U16 TaskMID; /*0x30 */ ++ U16 Reserved5; /*0x32 */ ++} MPI2_SCSI_TASK_MANAGE_REQUEST, ++ *PTR_MPI2_SCSI_TASK_MANAGE_REQUEST, ++ Mpi2SCSITaskManagementRequest_t, ++ *pMpi2SCSITaskManagementRequest_t; ++ ++/*TaskType values */ ++ ++#define MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK (0x01) ++#define MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET (0x02) ++#define MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x03) ++#define MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET (0x05) ++#define MPI2_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET (0x06) ++#define MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK (0x07) ++#define MPI2_SCSITASKMGMT_TASKTYPE_CLR_ACA (0x08) ++#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_TASK_SET (0x09) ++#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT (0x0A) ++ ++/*obsolete TaskType name */ ++#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_UNIT_ATTENTION \ ++ (MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT) ++ ++/*MsgFlags bits */ ++ ++#define MPI2_SCSITASKMGMT_MSGFLAGS_MASK_TARGET_RESET (0x18) ++#define MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET (0x00) ++#define MPI2_SCSITASKMGMT_MSGFLAGS_NEXUS_RESET_SRST (0x08) ++#define MPI2_SCSITASKMGMT_MSGFLAGS_SAS_HARD_LINK_RESET (0x10) ++ ++#define MPI2_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU (0x01) ++ ++/*SCSI Task Management Reply Message */ ++typedef struct _MPI2_SCSI_TASK_MANAGE_REPLY { ++ U16 DevHandle; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U8 ResponseCode; /*0x04 */ ++ U8 TaskType; /*0x05 */ ++ U8 Reserved1; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved2; /*0x0A */ ++ U16 Reserved3; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U32 TerminationCount; /*0x14 */ ++ U32 ResponseInfo; /*0x18 */ ++} MPI2_SCSI_TASK_MANAGE_REPLY, ++ *PTR_MPI2_SCSI_TASK_MANAGE_REPLY, ++ Mpi2SCSITaskManagementReply_t, *pMpi2SCSIManagementReply_t; ++ ++/*ResponseCode values */ ++ ++#define MPI2_SCSITASKMGMT_RSP_TM_COMPLETE (0x00) ++#define MPI2_SCSITASKMGMT_RSP_INVALID_FRAME (0x02) ++#define MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED (0x04) ++#define MPI2_SCSITASKMGMT_RSP_TM_FAILED (0x05) ++#define MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED (0x08) ++#define MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN (0x09) ++#define MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG (0x0A) ++#define MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC (0x80) ++ ++/*masks and shifts for the ResponseInfo field */ ++ ++#define MPI2_SCSITASKMGMT_RI_MASK_REASONCODE (0x000000FF) ++#define MPI2_SCSITASKMGMT_RI_SHIFT_REASONCODE (0) ++#define MPI2_SCSITASKMGMT_RI_MASK_ARI2 (0x0000FF00) ++#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI2 (8) ++#define MPI2_SCSITASKMGMT_RI_MASK_ARI1 (0x00FF0000) ++#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI1 (16) ++#define MPI2_SCSITASKMGMT_RI_MASK_ARI0 (0xFF000000) ++#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI0 (24) ++ ++/**************************************************************************** ++* SCSI Enclosure Processor messages ++****************************************************************************/ ++ ++/*SCSI Enclosure Processor Request Message */ ++typedef struct _MPI2_SEP_REQUEST { ++ U16 DevHandle; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U8 Action; /*0x04 */ ++ U8 Flags; /*0x05 */ ++ U8 Reserved1; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved2; /*0x0A */ ++ U32 SlotStatus; /*0x0C */ ++ U32 Reserved3; /*0x10 */ ++ U32 Reserved4; /*0x14 */ ++ U32 Reserved5; /*0x18 */ ++ U16 Slot; /*0x1C */ ++ U16 EnclosureHandle; /*0x1E */ ++} MPI2_SEP_REQUEST, *PTR_MPI2_SEP_REQUEST, ++ Mpi2SepRequest_t, *pMpi2SepRequest_t; ++ ++/*Action defines */ ++#define MPI2_SEP_REQ_ACTION_WRITE_STATUS (0x00) ++#define MPI2_SEP_REQ_ACTION_READ_STATUS (0x01) ++ ++/*Flags defines */ ++#define MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS (0x00) ++#define MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS (0x01) ++ ++/*SlotStatus defines */ ++#define MPI2_SEP_REQ_SLOTSTATUS_DEV_OFF (0x00080000) ++#define MPI2_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE (0x00040000) ++#define MPI2_SEP_REQ_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000) ++#define MPI2_SEP_REQ_SLOTSTATUS_REBUILD_STOPPED (0x00000200) ++#define MPI2_SEP_REQ_SLOTSTATUS_HOT_SPARE (0x00000100) ++#define MPI2_SEP_REQ_SLOTSTATUS_UNCONFIGURED (0x00000080) ++#define MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT (0x00000040) ++#define MPI2_SEP_REQ_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010) ++#define MPI2_SEP_REQ_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008) ++#define MPI2_SEP_REQ_SLOTSTATUS_DEV_REBUILDING (0x00000004) ++#define MPI2_SEP_REQ_SLOTSTATUS_DEV_FAULTY (0x00000002) ++#define MPI2_SEP_REQ_SLOTSTATUS_NO_ERROR (0x00000001) ++ ++/*SCSI Enclosure Processor Reply Message */ ++typedef struct _MPI2_SEP_REPLY { ++ U16 DevHandle; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U8 Action; /*0x04 */ ++ U8 Flags; /*0x05 */ ++ U8 Reserved1; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved2; /*0x0A */ ++ U16 Reserved3; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U32 SlotStatus; /*0x14 */ ++ U32 Reserved4; /*0x18 */ ++ U16 Slot; /*0x1C */ ++ U16 EnclosureHandle; /*0x1E */ ++} MPI2_SEP_REPLY, *PTR_MPI2_SEP_REPLY, ++ Mpi2SepReply_t, *pMpi2SepReply_t; ++ ++/*SlotStatus defines */ ++#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_OFF (0x00080000) ++#define MPI2_SEP_REPLY_SLOTSTATUS_REMOVE_READY (0x00040000) ++#define MPI2_SEP_REPLY_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000) ++#define MPI2_SEP_REPLY_SLOTSTATUS_REBUILD_STOPPED (0x00000200) ++#define MPI2_SEP_REPLY_SLOTSTATUS_HOT_SPARE (0x00000100) ++#define MPI2_SEP_REPLY_SLOTSTATUS_UNCONFIGURED (0x00000080) ++#define MPI2_SEP_REPLY_SLOTSTATUS_PREDICTED_FAULT (0x00000040) ++#define MPI2_SEP_REPLY_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010) ++#define MPI2_SEP_REPLY_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008) ++#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_REBUILDING (0x00000004) ++#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_FAULTY (0x00000002) ++#define MPI2_SEP_REPLY_SLOTSTATUS_NO_ERROR (0x00000001) ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h +new file mode 100644 +index 0000000..8bae305 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h +@@ -0,0 +1,1860 @@ ++/* ++ * Copyright 2000-2015 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2_ioc.h ++ * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages ++ * Creation Date: October 11, 2006 ++ * ++ * mpi2_ioc.h Version: 02.00.27 ++ * ++ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 ++ * prefix are for use only on MPI v2.5 products, and must not be used ++ * with MPI v2.0 products. Unless otherwise noted, names beginning with ++ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products. ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 06-04-07 02.00.01 In IOCFacts Reply structure, renamed MaxDevices to ++ * MaxTargets. ++ * Added TotalImageSize field to FWDownload Request. ++ * Added reserved words to FWUpload Request. ++ * 06-26-07 02.00.02 Added IR Configuration Change List Event. ++ * 08-31-07 02.00.03 Removed SystemReplyQueueDepth field from the IOCInit ++ * request and replaced it with ++ * ReplyDescriptorPostQueueDepth and ReplyFreeQueueDepth. ++ * Replaced the MinReplyQueueDepth field of the IOCFacts ++ * reply with MaxReplyDescriptorPostQueueDepth. ++ * Added MPI2_RDPQ_DEPTH_MIN define to specify the minimum ++ * depth for the Reply Descriptor Post Queue. ++ * Added SASAddress field to Initiator Device Table ++ * Overflow Event data. ++ * 10-31-07 02.00.04 Added ReasonCode MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING ++ * for SAS Initiator Device Status Change Event data. ++ * Modified Reason Code defines for SAS Topology Change ++ * List Event data, including adding a bit for PHY Vacant ++ * status, and adding a mask for the Reason Code. ++ * Added define for ++ * MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING. ++ * Added define for MPI2_EXT_IMAGE_TYPE_MEGARAID. ++ * 12-18-07 02.00.05 Added Boot Status defines for the IOCExceptions field of ++ * the IOCFacts Reply. ++ * Removed MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define. ++ * Moved MPI2_VERSION_UNION to mpi2.h. ++ * Changed MPI2_EVENT_NOTIFICATION_REQUEST to use masks ++ * instead of enables, and added SASBroadcastPrimitiveMasks ++ * field. ++ * Added Log Entry Added Event and related structure. ++ * 02-29-08 02.00.06 Added define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID. ++ * Removed define MPI2_IOCFACTS_PROTOCOL_SMP_TARGET. ++ * Added MaxVolumes and MaxPersistentEntries fields to ++ * IOCFacts reply. ++ * Added ProtocalFlags and IOCCapabilities fields to ++ * MPI2_FW_IMAGE_HEADER. ++ * Removed MPI2_PORTENABLE_FLAGS_ENABLE_SINGLE_PORT. ++ * 03-03-08 02.00.07 Fixed MPI2_FW_IMAGE_HEADER by changing Reserved26 to ++ * a U16 (from a U32). ++ * Removed extra 's' from EventMasks name. ++ * 06-27-08 02.00.08 Fixed an offset in a comment. ++ * 10-02-08 02.00.09 Removed SystemReplyFrameSize from MPI2_IOC_INIT_REQUEST. ++ * Removed CurReplyFrameSize from MPI2_IOC_FACTS_REPLY and ++ * renamed MinReplyFrameSize to ReplyFrameSize. ++ * Added MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX. ++ * Added two new RAIDOperation values for Integrated RAID ++ * Operations Status Event data. ++ * Added four new IR Configuration Change List Event data ++ * ReasonCode values. ++ * Added two new ReasonCode defines for SAS Device Status ++ * Change Event data. ++ * Added three new DiscoveryStatus bits for the SAS ++ * Discovery event data. ++ * Added Multiplexing Status Change bit to the PhyStatus ++ * field of the SAS Topology Change List event data. ++ * Removed define for MPI2_INIT_IMAGE_BOOTFLAGS_XMEMCOPY. ++ * BootFlags are now product-specific. ++ * Added defines for the indivdual signature bytes ++ * for MPI2_INIT_IMAGE_FOOTER. ++ * 01-19-09 02.00.10 Added MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY define. ++ * Added MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR ++ * define. ++ * Added MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE ++ * define. ++ * Removed MPI2_EVENT_SAS_DISC_DS_SATA_INIT_FAILURE define. ++ * 05-06-09 02.00.11 Added MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR define. ++ * Added MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX define. ++ * Added two new reason codes for SAS Device Status Change ++ * Event. ++ * Added new event: SAS PHY Counter. ++ * 07-30-09 02.00.12 Added GPIO Interrupt event define and structure. ++ * Added MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define. ++ * Added new product id family for 2208. ++ * 10-28-09 02.00.13 Added HostMSIxVectors field to MPI2_IOC_INIT_REQUEST. ++ * Added MaxMSIxVectors field to MPI2_IOC_FACTS_REPLY. ++ * Added MinDevHandle field to MPI2_IOC_FACTS_REPLY. ++ * Added MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY. ++ * Added MPI2_EVENT_HOST_BASED_DISCOVERY_PHY define. ++ * Added MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER define. ++ * Added Host Based Discovery Phy Event data. ++ * Added defines for ProductID Product field ++ * (MPI2_FW_HEADER_PID_). ++ * Modified values for SAS ProductID Family ++ * (MPI2_FW_HEADER_PID_FAMILY_). ++ * 02-10-10 02.00.14 Added SAS Quiesce Event structure and defines. ++ * Added PowerManagementControl Request structures and ++ * defines. ++ * 05-12-10 02.00.15 Marked Task Set Full Event as obsolete. ++ * Added MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY define. ++ * 11-10-10 02.00.16 Added MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC. ++ * 02-23-11 02.00.17 Added SAS NOTIFY Primitive event, and added ++ * SASNotifyPrimitiveMasks field to ++ * MPI2_EVENT_NOTIFICATION_REQUEST. ++ * Added Temperature Threshold Event. ++ * Added Host Message Event. ++ * Added Send Host Message request and reply. ++ * 05-25-11 02.00.18 For Extended Image Header, added ++ * MPI2_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC and ++ * MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC defines. ++ * Deprecated MPI2_EXT_IMAGE_TYPE_MAX define. ++ * 08-24-11 02.00.19 Added PhysicalPort field to ++ * MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE structure. ++ * Marked MPI2_PM_CONTROL_FEATURE_PCIE_LINK as obsolete. ++ * 11-18-11 02.00.20 Incorporating additions for MPI v2.5. ++ * 03-29-12 02.00.21 Added a product specific range to event values. ++ * 07-26-12 02.00.22 Added MPI2_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE. ++ * Added ElapsedSeconds field to ++ * MPI2_EVENT_DATA_IR_OPERATION_STATUS. ++ * 08-19-13 02.00.23 For IOCInit, added MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE ++ * and MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY. ++ * Added MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE. ++ * Added MPI2_FW_DOWNLOAD_ITYPE_PUBLIC_KEY. ++ * Added Encrypted Hash Extended Image. ++ * 12-05-13 02.00.24 Added MPI25_HASH_IMAGE_TYPE_BIOS. ++ * 11-18-14 02.00.25 Updated copyright information. ++ * 03-16-15 02.00.26 Updated for MPI v2.6. ++ * Added MPI2_EVENT_ACTIVE_CABLE_EXCEPTION and ++ * MPI26_EVENT_DATA_ACTIVE_CABLE_EXCEPT. ++ * Added MPI26_FW_HEADER_PID_FAMILY_3324_SAS and ++ * MPI26_FW_HEADER_PID_FAMILY_3516_SAS. ++ * Added MPI26_CTRL_OP_SHUTDOWN. ++ * 08-25-15 02.00.27 Added IC ARCH Class based signature defines ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_IOC_H ++#define MPI2_IOC_H ++ ++/***************************************************************************** ++* ++* IOC Messages ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* IOCInit message ++****************************************************************************/ ++ ++/*IOCInit Request message */ ++typedef struct _MPI2_IOC_INIT_REQUEST { ++ U8 WhoInit; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 MsgVersion; /*0x0C */ ++ U16 HeaderVersion; /*0x0E */ ++ U32 Reserved5; /*0x10 */ ++ U16 ConfigurationFlags; /* 0x14 */ ++ U8 HostPageSize; /*0x16 */ ++ U8 HostMSIxVectors; /*0x17 */ ++ U16 Reserved8; /*0x18 */ ++ U16 SystemRequestFrameSize; /*0x1A */ ++ U16 ReplyDescriptorPostQueueDepth; /*0x1C */ ++ U16 ReplyFreeQueueDepth; /*0x1E */ ++ U32 SenseBufferAddressHigh; /*0x20 */ ++ U32 SystemReplyAddressHigh; /*0x24 */ ++ U64 SystemRequestFrameBaseAddress; /*0x28 */ ++ U64 ReplyDescriptorPostQueueAddress; /*0x30 */ ++ U64 ReplyFreeQueueAddress; /*0x38 */ ++ U64 TimeStamp; /*0x40 */ ++} MPI2_IOC_INIT_REQUEST, *PTR_MPI2_IOC_INIT_REQUEST, ++ Mpi2IOCInitRequest_t, *pMpi2IOCInitRequest_t; ++ ++/*WhoInit values */ ++#define MPI2_WHOINIT_NOT_INITIALIZED (0x00) ++#define MPI2_WHOINIT_SYSTEM_BIOS (0x01) ++#define MPI2_WHOINIT_ROM_BIOS (0x02) ++#define MPI2_WHOINIT_PCI_PEER (0x03) ++#define MPI2_WHOINIT_HOST_DRIVER (0x04) ++#define MPI2_WHOINIT_MANUFACTURER (0x05) ++ ++/* MsgFlags */ ++#define MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE (0x01) ++ ++ ++/*MsgVersion */ ++#define MPI2_IOCINIT_MSGVERSION_MAJOR_MASK (0xFF00) ++#define MPI2_IOCINIT_MSGVERSION_MAJOR_SHIFT (8) ++#define MPI2_IOCINIT_MSGVERSION_MINOR_MASK (0x00FF) ++#define MPI2_IOCINIT_MSGVERSION_MINOR_SHIFT (0) ++ ++/*HeaderVersion */ ++#define MPI2_IOCINIT_HDRVERSION_UNIT_MASK (0xFF00) ++#define MPI2_IOCINIT_HDRVERSION_UNIT_SHIFT (8) ++#define MPI2_IOCINIT_HDRVERSION_DEV_MASK (0x00FF) ++#define MPI2_IOCINIT_HDRVERSION_DEV_SHIFT (0) ++ ++/*minimum depth for a Reply Descriptor Post Queue */ ++#define MPI2_RDPQ_DEPTH_MIN (16) ++ ++/* Reply Descriptor Post Queue Array Entry */ ++typedef struct _MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY { ++ U64 RDPQBaseAddress; /* 0x00 */ ++ U32 Reserved1; /* 0x08 */ ++ U32 Reserved2; /* 0x0C */ ++} MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY, ++*PTR_MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY, ++Mpi2IOCInitRDPQArrayEntry, *pMpi2IOCInitRDPQArrayEntry; ++ ++ ++/*IOCInit Reply message */ ++typedef struct _MPI2_IOC_INIT_REPLY { ++ U8 WhoInit; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_IOC_INIT_REPLY, *PTR_MPI2_IOC_INIT_REPLY, ++ Mpi2IOCInitReply_t, *pMpi2IOCInitReply_t; ++ ++/**************************************************************************** ++* IOCFacts message ++****************************************************************************/ ++ ++/*IOCFacts Request message */ ++typedef struct _MPI2_IOC_FACTS_REQUEST { ++ U16 Reserved1; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++} MPI2_IOC_FACTS_REQUEST, *PTR_MPI2_IOC_FACTS_REQUEST, ++ Mpi2IOCFactsRequest_t, *pMpi2IOCFactsRequest_t; ++ ++/*IOCFacts Reply message */ ++typedef struct _MPI2_IOC_FACTS_REPLY { ++ U16 MsgVersion; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 HeaderVersion; /*0x04 */ ++ U8 IOCNumber; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U16 IOCExceptions; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U8 MaxChainDepth; /*0x14 */ ++ U8 WhoInit; /*0x15 */ ++ U8 NumberOfPorts; /*0x16 */ ++ U8 MaxMSIxVectors; /*0x17 */ ++ U16 RequestCredit; /*0x18 */ ++ U16 ProductID; /*0x1A */ ++ U32 IOCCapabilities; /*0x1C */ ++ MPI2_VERSION_UNION FWVersion; /*0x20 */ ++ U16 IOCRequestFrameSize; /*0x24 */ ++ U16 IOCMaxChainSegmentSize; /*0x26 */ ++ U16 MaxInitiators; /*0x28 */ ++ U16 MaxTargets; /*0x2A */ ++ U16 MaxSasExpanders; /*0x2C */ ++ U16 MaxEnclosures; /*0x2E */ ++ U16 ProtocolFlags; /*0x30 */ ++ U16 HighPriorityCredit; /*0x32 */ ++ U16 MaxReplyDescriptorPostQueueDepth; /*0x34 */ ++ U8 ReplyFrameSize; /*0x36 */ ++ U8 MaxVolumes; /*0x37 */ ++ U16 MaxDevHandle; /*0x38 */ ++ U16 MaxPersistentEntries; /*0x3A */ ++ U16 MinDevHandle; /*0x3C */ ++ U8 CurrentHostPageSize; /* 0x3E */ ++ U8 Reserved4; /* 0x3F */ ++} MPI2_IOC_FACTS_REPLY, *PTR_MPI2_IOC_FACTS_REPLY, ++ Mpi2IOCFactsReply_t, *pMpi2IOCFactsReply_t; ++ ++/*MsgVersion */ ++#define MPI2_IOCFACTS_MSGVERSION_MAJOR_MASK (0xFF00) ++#define MPI2_IOCFACTS_MSGVERSION_MAJOR_SHIFT (8) ++#define MPI2_IOCFACTS_MSGVERSION_MINOR_MASK (0x00FF) ++#define MPI2_IOCFACTS_MSGVERSION_MINOR_SHIFT (0) ++ ++/*HeaderVersion */ ++#define MPI2_IOCFACTS_HDRVERSION_UNIT_MASK (0xFF00) ++#define MPI2_IOCFACTS_HDRVERSION_UNIT_SHIFT (8) ++#define MPI2_IOCFACTS_HDRVERSION_DEV_MASK (0x00FF) ++#define MPI2_IOCFACTS_HDRVERSION_DEV_SHIFT (0) ++ ++/*IOCExceptions */ ++#define MPI2_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE (0x0200) ++#define MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX (0x0100) ++ ++#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_MASK (0x00E0) ++#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_GOOD (0x0000) ++#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_BACKUP (0x0020) ++#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_RESTORED (0x0040) ++#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_CORRUPT_BACKUP (0x0060) ++ ++#define MPI2_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED (0x0010) ++#define MPI2_IOCFACTS_EXCEPT_MANUFACT_CHECKSUM_FAIL (0x0008) ++#define MPI2_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL (0x0004) ++#define MPI2_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID (0x0002) ++#define MPI2_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (0x0001) ++ ++/*defines for WhoInit field are after the IOCInit Request */ ++ ++/*ProductID field uses MPI2_FW_HEADER_PID_ */ ++ ++/*IOCCapabilities */ ++#define MPI26_IOCFACTS_CAPABILITY_ATOMIC_REQ (0x00080000) ++#define MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE (0x00040000) ++#define MPI25_IOCFACTS_CAPABILITY_FAST_PATH_CAPABLE (0x00020000) ++#define MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY (0x00010000) ++#define MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX (0x00008000) ++#define MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR (0x00004000) ++#define MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY (0x00002000) ++#define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID (0x00001000) ++#define MPI2_IOCFACTS_CAPABILITY_TLR (0x00000800) ++#define MPI2_IOCFACTS_CAPABILITY_MULTICAST (0x00000100) ++#define MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET (0x00000080) ++#define MPI2_IOCFACTS_CAPABILITY_EEDP (0x00000040) ++#define MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER (0x00000020) ++#define MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER (0x00000010) ++#define MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER (0x00000008) ++#define MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING (0x00000004) ++ ++/*ProtocolFlags */ ++#define MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR (0x0002) ++#define MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET (0x0001) ++ ++/**************************************************************************** ++* PortFacts message ++****************************************************************************/ ++ ++/*PortFacts Request message */ ++typedef struct _MPI2_PORT_FACTS_REQUEST { ++ U16 Reserved1; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 PortNumber; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++} MPI2_PORT_FACTS_REQUEST, *PTR_MPI2_PORT_FACTS_REQUEST, ++ Mpi2PortFactsRequest_t, *pMpi2PortFactsRequest_t; ++ ++/*PortFacts Reply message */ ++typedef struct _MPI2_PORT_FACTS_REPLY { ++ U16 Reserved1; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 PortNumber; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U16 Reserved4; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U8 Reserved5; /*0x14 */ ++ U8 PortType; /*0x15 */ ++ U16 Reserved6; /*0x16 */ ++ U16 MaxPostedCmdBuffers; /*0x18 */ ++ U16 Reserved7; /*0x1A */ ++} MPI2_PORT_FACTS_REPLY, *PTR_MPI2_PORT_FACTS_REPLY, ++ Mpi2PortFactsReply_t, *pMpi2PortFactsReply_t; ++ ++/*PortType values */ ++#define MPI2_PORTFACTS_PORTTYPE_INACTIVE (0x00) ++#define MPI2_PORTFACTS_PORTTYPE_FC (0x10) ++#define MPI2_PORTFACTS_PORTTYPE_ISCSI (0x20) ++#define MPI2_PORTFACTS_PORTTYPE_SAS_PHYSICAL (0x30) ++#define MPI2_PORTFACTS_PORTTYPE_SAS_VIRTUAL (0x31) ++ ++/**************************************************************************** ++* PortEnable message ++****************************************************************************/ ++ ++/*PortEnable Request message */ ++typedef struct _MPI2_PORT_ENABLE_REQUEST { ++ U16 Reserved1; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U8 Reserved2; /*0x04 */ ++ U8 PortFlags; /*0x05 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++} MPI2_PORT_ENABLE_REQUEST, *PTR_MPI2_PORT_ENABLE_REQUEST, ++ Mpi2PortEnableRequest_t, *pMpi2PortEnableRequest_t; ++ ++/*PortEnable Reply message */ ++typedef struct _MPI2_PORT_ENABLE_REPLY { ++ U16 Reserved1; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U8 Reserved2; /*0x04 */ ++ U8 PortFlags; /*0x05 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_PORT_ENABLE_REPLY, *PTR_MPI2_PORT_ENABLE_REPLY, ++ Mpi2PortEnableReply_t, *pMpi2PortEnableReply_t; ++ ++/**************************************************************************** ++* EventNotification message ++****************************************************************************/ ++ ++/*EventNotification Request message */ ++#define MPI2_EVENT_NOTIFY_EVENTMASK_WORDS (4) ++ ++typedef struct _MPI2_EVENT_NOTIFICATION_REQUEST { ++ U16 Reserved1; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 Reserved5; /*0x0C */ ++ U32 Reserved6; /*0x10 */ ++ U32 EventMasks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; /*0x14 */ ++ U16 SASBroadcastPrimitiveMasks; /*0x24 */ ++ U16 SASNotifyPrimitiveMasks; /*0x26 */ ++ U32 Reserved8; /*0x28 */ ++} MPI2_EVENT_NOTIFICATION_REQUEST, ++ *PTR_MPI2_EVENT_NOTIFICATION_REQUEST, ++ Mpi2EventNotificationRequest_t, ++ *pMpi2EventNotificationRequest_t; ++ ++/*EventNotification Reply message */ ++typedef struct _MPI2_EVENT_NOTIFICATION_REPLY { ++ U16 EventDataLength; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved1; /*0x04 */ ++ U8 AckRequired; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved2; /*0x0A */ ++ U16 Reserved3; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U16 Event; /*0x14 */ ++ U16 Reserved4; /*0x16 */ ++ U32 EventContext; /*0x18 */ ++ U32 EventData[1]; /*0x1C */ ++} MPI2_EVENT_NOTIFICATION_REPLY, *PTR_MPI2_EVENT_NOTIFICATION_REPLY, ++ Mpi2EventNotificationReply_t, ++ *pMpi2EventNotificationReply_t; ++ ++/*AckRequired */ ++#define MPI2_EVENT_NOTIFICATION_ACK_NOT_REQUIRED (0x00) ++#define MPI2_EVENT_NOTIFICATION_ACK_REQUIRED (0x01) ++ ++/*Event */ ++#define MPI2_EVENT_LOG_DATA (0x0001) ++#define MPI2_EVENT_STATE_CHANGE (0x0002) ++#define MPI2_EVENT_HARD_RESET_RECEIVED (0x0005) ++#define MPI2_EVENT_EVENT_CHANGE (0x000A) ++#define MPI2_EVENT_TASK_SET_FULL (0x000E) /*obsolete */ ++#define MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE (0x000F) ++#define MPI2_EVENT_IR_OPERATION_STATUS (0x0014) ++#define MPI2_EVENT_SAS_DISCOVERY (0x0016) ++#define MPI2_EVENT_SAS_BROADCAST_PRIMITIVE (0x0017) ++#define MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE (0x0018) ++#define MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW (0x0019) ++#define MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST (0x001C) ++#define MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE (0x001D) ++#define MPI2_EVENT_IR_VOLUME (0x001E) ++#define MPI2_EVENT_IR_PHYSICAL_DISK (0x001F) ++#define MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST (0x0020) ++#define MPI2_EVENT_LOG_ENTRY_ADDED (0x0021) ++#define MPI2_EVENT_SAS_PHY_COUNTER (0x0022) ++#define MPI2_EVENT_GPIO_INTERRUPT (0x0023) ++#define MPI2_EVENT_HOST_BASED_DISCOVERY_PHY (0x0024) ++#define MPI2_EVENT_SAS_QUIESCE (0x0025) ++#define MPI2_EVENT_SAS_NOTIFY_PRIMITIVE (0x0026) ++#define MPI2_EVENT_TEMP_THRESHOLD (0x0027) ++#define MPI2_EVENT_HOST_MESSAGE (0x0028) ++#define MPI2_EVENT_POWER_PERFORMANCE_CHANGE (0x0029) ++#define MPI2_EVENT_ACTIVE_CABLE_EXCEPTION (0x0034) ++#define MPI2_EVENT_MIN_PRODUCT_SPECIFIC (0x006E) ++#define MPI2_EVENT_MAX_PRODUCT_SPECIFIC (0x007F) ++ ++/*Log Entry Added Event data */ ++ ++/*the following structure matches MPI2_LOG_0_ENTRY in mpi2_cnfg.h */ ++#define MPI2_EVENT_DATA_LOG_DATA_LENGTH (0x1C) ++ ++typedef struct _MPI2_EVENT_DATA_LOG_ENTRY_ADDED { ++ U64 TimeStamp; /*0x00 */ ++ U32 Reserved1; /*0x08 */ ++ U16 LogSequence; /*0x0C */ ++ U16 LogEntryQualifier; /*0x0E */ ++ U8 VP_ID; /*0x10 */ ++ U8 VF_ID; /*0x11 */ ++ U16 Reserved2; /*0x12 */ ++ U8 LogData[MPI2_EVENT_DATA_LOG_DATA_LENGTH]; /*0x14 */ ++} MPI2_EVENT_DATA_LOG_ENTRY_ADDED, ++ *PTR_MPI2_EVENT_DATA_LOG_ENTRY_ADDED, ++ Mpi2EventDataLogEntryAdded_t, ++ *pMpi2EventDataLogEntryAdded_t; ++ ++/*GPIO Interrupt Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_GPIO_INTERRUPT { ++ U8 GPIONum; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++} MPI2_EVENT_DATA_GPIO_INTERRUPT, ++ *PTR_MPI2_EVENT_DATA_GPIO_INTERRUPT, ++ Mpi2EventDataGpioInterrupt_t, ++ *pMpi2EventDataGpioInterrupt_t; ++ ++/*Temperature Threshold Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_TEMPERATURE { ++ U16 Status; /*0x00 */ ++ U8 SensorNum; /*0x02 */ ++ U8 Reserved1; /*0x03 */ ++ U16 CurrentTemperature; /*0x04 */ ++ U16 Reserved2; /*0x06 */ ++ U32 Reserved3; /*0x08 */ ++ U32 Reserved4; /*0x0C */ ++} MPI2_EVENT_DATA_TEMPERATURE, ++ *PTR_MPI2_EVENT_DATA_TEMPERATURE, ++ Mpi2EventDataTemperature_t, *pMpi2EventDataTemperature_t; ++ ++/*Temperature Threshold Event data Status bits */ ++#define MPI2_EVENT_TEMPERATURE3_EXCEEDED (0x0008) ++#define MPI2_EVENT_TEMPERATURE2_EXCEEDED (0x0004) ++#define MPI2_EVENT_TEMPERATURE1_EXCEEDED (0x0002) ++#define MPI2_EVENT_TEMPERATURE0_EXCEEDED (0x0001) ++ ++/*Host Message Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_HOST_MESSAGE { ++ U8 SourceVF_ID; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U32 Reserved3; /*0x04 */ ++ U32 HostData[1]; /*0x08 */ ++} MPI2_EVENT_DATA_HOST_MESSAGE, *PTR_MPI2_EVENT_DATA_HOST_MESSAGE, ++ Mpi2EventDataHostMessage_t, *pMpi2EventDataHostMessage_t; ++ ++/*Power Performance Change Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_POWER_PERF_CHANGE { ++ U8 CurrentPowerMode; /*0x00 */ ++ U8 PreviousPowerMode; /*0x01 */ ++ U16 Reserved1; /*0x02 */ ++} MPI2_EVENT_DATA_POWER_PERF_CHANGE, ++ *PTR_MPI2_EVENT_DATA_POWER_PERF_CHANGE, ++ Mpi2EventDataPowerPerfChange_t, ++ *pMpi2EventDataPowerPerfChange_t; ++ ++/*defines for CurrentPowerMode and PreviousPowerMode fields */ ++#define MPI2_EVENT_PM_INIT_MASK (0xC0) ++#define MPI2_EVENT_PM_INIT_UNAVAILABLE (0x00) ++#define MPI2_EVENT_PM_INIT_HOST (0x40) ++#define MPI2_EVENT_PM_INIT_IO_UNIT (0x80) ++#define MPI2_EVENT_PM_INIT_PCIE_DPA (0xC0) ++ ++#define MPI2_EVENT_PM_MODE_MASK (0x07) ++#define MPI2_EVENT_PM_MODE_UNAVAILABLE (0x00) ++#define MPI2_EVENT_PM_MODE_UNKNOWN (0x01) ++#define MPI2_EVENT_PM_MODE_FULL_POWER (0x04) ++#define MPI2_EVENT_PM_MODE_REDUCED_POWER (0x05) ++#define MPI2_EVENT_PM_MODE_STANDBY (0x06) ++ ++/* Active Cable Exception Event data */ ++ ++typedef struct _MPI26_EVENT_DATA_ACTIVE_CABLE_EXCEPT { ++ U32 ActiveCablePowerRequirement; /* 0x00 */ ++ U8 ReasonCode; /* 0x04 */ ++ U8 ReceptacleID; /* 0x05 */ ++ U16 Reserved1; /* 0x06 */ ++} MPI26_EVENT_DATA_ACTIVE_CABLE_EXCEPT, ++ *PTR_MPI26_EVENT_DATA_ACTIVE_CABLE_EXCEPT, ++ Mpi26EventDataActiveCableExcept_t, ++ *pMpi26EventDataActiveCableExcept_t; ++ ++/* defines for ReasonCode field */ ++#define MPI26_EVENT_ACTIVE_CABLE_INSUFFICIENT_POWER (0x00) ++ ++/*Hard Reset Received Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_HARD_RESET_RECEIVED { ++ U8 Reserved1; /*0x00 */ ++ U8 Port; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++} MPI2_EVENT_DATA_HARD_RESET_RECEIVED, ++ *PTR_MPI2_EVENT_DATA_HARD_RESET_RECEIVED, ++ Mpi2EventDataHardResetReceived_t, ++ *pMpi2EventDataHardResetReceived_t; ++ ++/*Task Set Full Event data */ ++/* this event is obsolete */ ++ ++typedef struct _MPI2_EVENT_DATA_TASK_SET_FULL { ++ U16 DevHandle; /*0x00 */ ++ U16 CurrentDepth; /*0x02 */ ++} MPI2_EVENT_DATA_TASK_SET_FULL, *PTR_MPI2_EVENT_DATA_TASK_SET_FULL, ++ Mpi2EventDataTaskSetFull_t, *pMpi2EventDataTaskSetFull_t; ++ ++/*SAS Device Status Change Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE { ++ U16 TaskTag; /*0x00 */ ++ U8 ReasonCode; /*0x02 */ ++ U8 PhysicalPort; /*0x03 */ ++ U8 ASC; /*0x04 */ ++ U8 ASCQ; /*0x05 */ ++ U16 DevHandle; /*0x06 */ ++ U32 Reserved2; /*0x08 */ ++ U64 SASAddress; /*0x0C */ ++ U8 LUN[8]; /*0x14 */ ++} MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE, ++ *PTR_MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE, ++ Mpi2EventDataSasDeviceStatusChange_t, ++ *pMpi2EventDataSasDeviceStatusChange_t; ++ ++/*SAS Device Status Change Event data ReasonCode values */ ++#define MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA (0x05) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED (0x07) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET (0x08) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL (0x09) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL (0x0A) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL (0x0B) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL (0x0C) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION (0x0D) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET (0x0E) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL (0x0F) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE (0x10) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY (0x11) ++#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY (0x12) ++ ++/*Integrated RAID Operation Status Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_IR_OPERATION_STATUS { ++ U16 VolDevHandle; /*0x00 */ ++ U16 Reserved1; /*0x02 */ ++ U8 RAIDOperation; /*0x04 */ ++ U8 PercentComplete; /*0x05 */ ++ U16 Reserved2; /*0x06 */ ++ U32 ElapsedSeconds; /*0x08 */ ++} MPI2_EVENT_DATA_IR_OPERATION_STATUS, ++ *PTR_MPI2_EVENT_DATA_IR_OPERATION_STATUS, ++ Mpi2EventDataIrOperationStatus_t, ++ *pMpi2EventDataIrOperationStatus_t; ++ ++/*Integrated RAID Operation Status Event data RAIDOperation values */ ++#define MPI2_EVENT_IR_RAIDOP_RESYNC (0x00) ++#define MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION (0x01) ++#define MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK (0x02) ++#define MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT (0x03) ++#define MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT (0x04) ++ ++/*Integrated RAID Volume Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_IR_VOLUME { ++ U16 VolDevHandle; /*0x00 */ ++ U8 ReasonCode; /*0x02 */ ++ U8 Reserved1; /*0x03 */ ++ U32 NewValue; /*0x04 */ ++ U32 PreviousValue; /*0x08 */ ++} MPI2_EVENT_DATA_IR_VOLUME, *PTR_MPI2_EVENT_DATA_IR_VOLUME, ++ Mpi2EventDataIrVolume_t, *pMpi2EventDataIrVolume_t; ++ ++/*Integrated RAID Volume Event data ReasonCode values */ ++#define MPI2_EVENT_IR_VOLUME_RC_SETTINGS_CHANGED (0x01) ++#define MPI2_EVENT_IR_VOLUME_RC_STATUS_FLAGS_CHANGED (0x02) ++#define MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED (0x03) ++ ++/*Integrated RAID Physical Disk Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_IR_PHYSICAL_DISK { ++ U16 Reserved1; /*0x00 */ ++ U8 ReasonCode; /*0x02 */ ++ U8 PhysDiskNum; /*0x03 */ ++ U16 PhysDiskDevHandle; /*0x04 */ ++ U16 Reserved2; /*0x06 */ ++ U16 Slot; /*0x08 */ ++ U16 EnclosureHandle; /*0x0A */ ++ U32 NewValue; /*0x0C */ ++ U32 PreviousValue; /*0x10 */ ++} MPI2_EVENT_DATA_IR_PHYSICAL_DISK, ++ *PTR_MPI2_EVENT_DATA_IR_PHYSICAL_DISK, ++ Mpi2EventDataIrPhysicalDisk_t, ++ *pMpi2EventDataIrPhysicalDisk_t; ++ ++/*Integrated RAID Physical Disk Event data ReasonCode values */ ++#define MPI2_EVENT_IR_PHYSDISK_RC_SETTINGS_CHANGED (0x01) ++#define MPI2_EVENT_IR_PHYSDISK_RC_STATUS_FLAGS_CHANGED (0x02) ++#define MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED (0x03) ++ ++/*Integrated RAID Configuration Change List Event data */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check NumElements at runtime. ++ */ ++#ifndef MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT ++#define MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT (1) ++#endif ++ ++typedef struct _MPI2_EVENT_IR_CONFIG_ELEMENT { ++ U16 ElementFlags; /*0x00 */ ++ U16 VolDevHandle; /*0x02 */ ++ U8 ReasonCode; /*0x04 */ ++ U8 PhysDiskNum; /*0x05 */ ++ U16 PhysDiskDevHandle; /*0x06 */ ++} MPI2_EVENT_IR_CONFIG_ELEMENT, *PTR_MPI2_EVENT_IR_CONFIG_ELEMENT, ++ Mpi2EventIrConfigElement_t, *pMpi2EventIrConfigElement_t; ++ ++/*IR Configuration Change List Event data ElementFlags values */ ++#define MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK (0x000F) ++#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT (0x0000) ++#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT (0x0001) ++#define MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT (0x0002) ++ ++/*IR Configuration Change List Event data ReasonCode values */ ++#define MPI2_EVENT_IR_CHANGE_RC_ADDED (0x01) ++#define MPI2_EVENT_IR_CHANGE_RC_REMOVED (0x02) ++#define MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE (0x03) ++#define MPI2_EVENT_IR_CHANGE_RC_HIDE (0x04) ++#define MPI2_EVENT_IR_CHANGE_RC_UNHIDE (0x05) ++#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED (0x06) ++#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED (0x07) ++#define MPI2_EVENT_IR_CHANGE_RC_PD_CREATED (0x08) ++#define MPI2_EVENT_IR_CHANGE_RC_PD_DELETED (0x09) ++ ++typedef struct _MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST { ++ U8 NumElements; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 Reserved2; /*0x02 */ ++ U8 ConfigNum; /*0x03 */ ++ U32 Flags; /*0x04 */ ++ MPI2_EVENT_IR_CONFIG_ELEMENT ++ ConfigElement[MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT];/*0x08 */ ++} MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST, ++ *PTR_MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST, ++ Mpi2EventDataIrConfigChangeList_t, ++ *pMpi2EventDataIrConfigChangeList_t; ++ ++/*IR Configuration Change List Event data Flags values */ ++#define MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG (0x00000001) ++ ++/*SAS Discovery Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_DISCOVERY { ++ U8 Flags; /*0x00 */ ++ U8 ReasonCode; /*0x01 */ ++ U8 PhysicalPort; /*0x02 */ ++ U8 Reserved1; /*0x03 */ ++ U32 DiscoveryStatus; /*0x04 */ ++} MPI2_EVENT_DATA_SAS_DISCOVERY, ++ *PTR_MPI2_EVENT_DATA_SAS_DISCOVERY, ++ Mpi2EventDataSasDiscovery_t, *pMpi2EventDataSasDiscovery_t; ++ ++/*SAS Discovery Event data Flags values */ ++#define MPI2_EVENT_SAS_DISC_DEVICE_CHANGE (0x02) ++#define MPI2_EVENT_SAS_DISC_IN_PROGRESS (0x01) ++ ++/*SAS Discovery Event data ReasonCode values */ ++#define MPI2_EVENT_SAS_DISC_RC_STARTED (0x01) ++#define MPI2_EVENT_SAS_DISC_RC_COMPLETED (0x02) ++ ++/*SAS Discovery Event data DiscoveryStatus values */ ++#define MPI2_EVENT_SAS_DISC_DS_MAX_ENCLOSURES_EXCEED (0x80000000) ++#define MPI2_EVENT_SAS_DISC_DS_MAX_EXPANDERS_EXCEED (0x40000000) ++#define MPI2_EVENT_SAS_DISC_DS_MAX_DEVICES_EXCEED (0x20000000) ++#define MPI2_EVENT_SAS_DISC_DS_MAX_TOPO_PHYS_EXCEED (0x10000000) ++#define MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR (0x08000000) ++#define MPI2_EVENT_SAS_DISC_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000) ++#define MPI2_EVENT_SAS_DISC_DS_EXP_MULTI_SUBTRACTIVE (0x00004000) ++#define MPI2_EVENT_SAS_DISC_DS_MULTI_PORT_DOMAIN (0x00002000) ++#define MPI2_EVENT_SAS_DISC_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000) ++#define MPI2_EVENT_SAS_DISC_DS_UNSUPPORTED_DEVICE (0x00000800) ++#define MPI2_EVENT_SAS_DISC_DS_TABLE_LINK (0x00000400) ++#define MPI2_EVENT_SAS_DISC_DS_SUBTRACTIVE_LINK (0x00000200) ++#define MPI2_EVENT_SAS_DISC_DS_SMP_CRC_ERROR (0x00000100) ++#define MPI2_EVENT_SAS_DISC_DS_SMP_FUNCTION_FAILED (0x00000080) ++#define MPI2_EVENT_SAS_DISC_DS_INDEX_NOT_EXIST (0x00000040) ++#define MPI2_EVENT_SAS_DISC_DS_OUT_ROUTE_ENTRIES (0x00000020) ++#define MPI2_EVENT_SAS_DISC_DS_SMP_TIMEOUT (0x00000010) ++#define MPI2_EVENT_SAS_DISC_DS_MULTIPLE_PORTS (0x00000004) ++#define MPI2_EVENT_SAS_DISC_DS_UNADDRESSABLE_DEVICE (0x00000002) ++#define MPI2_EVENT_SAS_DISC_DS_LOOP_DETECTED (0x00000001) ++ ++/*SAS Broadcast Primitive Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE { ++ U8 PhyNum; /*0x00 */ ++ U8 Port; /*0x01 */ ++ U8 PortWidth; /*0x02 */ ++ U8 Primitive; /*0x03 */ ++} MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE, ++ *PTR_MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE, ++ Mpi2EventDataSasBroadcastPrimitive_t, ++ *pMpi2EventDataSasBroadcastPrimitive_t; ++ ++/*defines for the Primitive field */ ++#define MPI2_EVENT_PRIMITIVE_CHANGE (0x01) ++#define MPI2_EVENT_PRIMITIVE_SES (0x02) ++#define MPI2_EVENT_PRIMITIVE_EXPANDER (0x03) ++#define MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT (0x04) ++#define MPI2_EVENT_PRIMITIVE_RESERVED3 (0x05) ++#define MPI2_EVENT_PRIMITIVE_RESERVED4 (0x06) ++#define MPI2_EVENT_PRIMITIVE_CHANGE0_RESERVED (0x07) ++#define MPI2_EVENT_PRIMITIVE_CHANGE1_RESERVED (0x08) ++ ++/*SAS Notify Primitive Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE { ++ U8 PhyNum; /*0x00 */ ++ U8 Port; /*0x01 */ ++ U8 Reserved1; /*0x02 */ ++ U8 Primitive; /*0x03 */ ++} MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE, ++ *PTR_MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE, ++ Mpi2EventDataSasNotifyPrimitive_t, ++ *pMpi2EventDataSasNotifyPrimitive_t; ++ ++/*defines for the Primitive field */ ++#define MPI2_EVENT_NOTIFY_ENABLE_SPINUP (0x01) ++#define MPI2_EVENT_NOTIFY_POWER_LOSS_EXPECTED (0x02) ++#define MPI2_EVENT_NOTIFY_RESERVED1 (0x03) ++#define MPI2_EVENT_NOTIFY_RESERVED2 (0x04) ++ ++/*SAS Initiator Device Status Change Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE { ++ U8 ReasonCode; /*0x00 */ ++ U8 PhysicalPort; /*0x01 */ ++ U16 DevHandle; /*0x02 */ ++ U64 SASAddress; /*0x04 */ ++} MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE, ++ *PTR_MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE, ++ Mpi2EventDataSasInitDevStatusChange_t, ++ *pMpi2EventDataSasInitDevStatusChange_t; ++ ++/*SAS Initiator Device Status Change event ReasonCode values */ ++#define MPI2_EVENT_SAS_INIT_RC_ADDED (0x01) ++#define MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING (0x02) ++ ++/*SAS Initiator Device Table Overflow Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW { ++ U16 MaxInit; /*0x00 */ ++ U16 CurrentInit; /*0x02 */ ++ U64 SASAddress; /*0x04 */ ++} MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW, ++ *PTR_MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW, ++ Mpi2EventDataSasInitTableOverflow_t, ++ *pMpi2EventDataSasInitTableOverflow_t; ++ ++/*SAS Topology Change List Event data */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check NumEntries at runtime. ++ */ ++#ifndef MPI2_EVENT_SAS_TOPO_PHY_COUNT ++#define MPI2_EVENT_SAS_TOPO_PHY_COUNT (1) ++#endif ++ ++typedef struct _MPI2_EVENT_SAS_TOPO_PHY_ENTRY { ++ U16 AttachedDevHandle; /*0x00 */ ++ U8 LinkRate; /*0x02 */ ++ U8 PhyStatus; /*0x03 */ ++} MPI2_EVENT_SAS_TOPO_PHY_ENTRY, *PTR_MPI2_EVENT_SAS_TOPO_PHY_ENTRY, ++ Mpi2EventSasTopoPhyEntry_t, *pMpi2EventSasTopoPhyEntry_t; ++ ++typedef struct _MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST { ++ U16 EnclosureHandle; /*0x00 */ ++ U16 ExpanderDevHandle; /*0x02 */ ++ U8 NumPhys; /*0x04 */ ++ U8 Reserved1; /*0x05 */ ++ U16 Reserved2; /*0x06 */ ++ U8 NumEntries; /*0x08 */ ++ U8 StartPhyNum; /*0x09 */ ++ U8 ExpStatus; /*0x0A */ ++ U8 PhysicalPort; /*0x0B */ ++ MPI2_EVENT_SAS_TOPO_PHY_ENTRY ++ PHY[MPI2_EVENT_SAS_TOPO_PHY_COUNT]; /*0x0C */ ++} MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST, ++ *PTR_MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST, ++ Mpi2EventDataSasTopologyChangeList_t, ++ *pMpi2EventDataSasTopologyChangeList_t; ++ ++/*values for the ExpStatus field */ ++#define MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER (0x00) ++#define MPI2_EVENT_SAS_TOPO_ES_ADDED (0x01) ++#define MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING (0x02) ++#define MPI2_EVENT_SAS_TOPO_ES_RESPONDING (0x03) ++#define MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING (0x04) ++ ++/*defines for the LinkRate field */ ++#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_MASK (0xF0) ++#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_SHIFT (4) ++#define MPI2_EVENT_SAS_TOPO_LR_PREV_MASK (0x0F) ++#define MPI2_EVENT_SAS_TOPO_LR_PREV_SHIFT (0) ++ ++#define MPI2_EVENT_SAS_TOPO_LR_UNKNOWN_LINK_RATE (0x00) ++#define MPI2_EVENT_SAS_TOPO_LR_PHY_DISABLED (0x01) ++#define MPI2_EVENT_SAS_TOPO_LR_NEGOTIATION_FAILED (0x02) ++#define MPI2_EVENT_SAS_TOPO_LR_SATA_OOB_COMPLETE (0x03) ++#define MPI2_EVENT_SAS_TOPO_LR_PORT_SELECTOR (0x04) ++#define MPI2_EVENT_SAS_TOPO_LR_SMP_RESET_IN_PROGRESS (0x05) ++#define MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY (0x06) ++#define MPI2_EVENT_SAS_TOPO_LR_RATE_1_5 (0x08) ++#define MPI2_EVENT_SAS_TOPO_LR_RATE_3_0 (0x09) ++#define MPI2_EVENT_SAS_TOPO_LR_RATE_6_0 (0x0A) ++#define MPI25_EVENT_SAS_TOPO_LR_RATE_12_0 (0x0B) ++ ++/*values for the PhyStatus field */ ++#define MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT (0x80) ++#define MPI2_EVENT_SAS_TOPO_PS_MULTIPLEX_CHANGE (0x10) ++/*values for the PhyStatus ReasonCode sub-field */ ++#define MPI2_EVENT_SAS_TOPO_RC_MASK (0x0F) ++#define MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED (0x01) ++#define MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING (0x02) ++#define MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED (0x03) ++#define MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE (0x04) ++#define MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING (0x05) ++ ++/*SAS Enclosure Device Status Change Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE { ++ U16 EnclosureHandle; /*0x00 */ ++ U8 ReasonCode; /*0x02 */ ++ U8 PhysicalPort; /*0x03 */ ++ U64 EnclosureLogicalID; /*0x04 */ ++ U16 NumSlots; /*0x0C */ ++ U16 StartSlot; /*0x0E */ ++ U32 PhyBits; /*0x10 */ ++} MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE, ++ *PTR_MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE, ++ Mpi2EventDataSasEnclDevStatusChange_t, ++ *pMpi2EventDataSasEnclDevStatusChange_t; ++ ++/*SAS Enclosure Device Status Change event ReasonCode values */ ++#define MPI2_EVENT_SAS_ENCL_RC_ADDED (0x01) ++#define MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING (0x02) ++ ++/*SAS PHY Counter Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_PHY_COUNTER { ++ U64 TimeStamp; /*0x00 */ ++ U32 Reserved1; /*0x08 */ ++ U8 PhyEventCode; /*0x0C */ ++ U8 PhyNum; /*0x0D */ ++ U16 Reserved2; /*0x0E */ ++ U32 PhyEventInfo; /*0x10 */ ++ U8 CounterType; /*0x14 */ ++ U8 ThresholdWindow; /*0x15 */ ++ U8 TimeUnits; /*0x16 */ ++ U8 Reserved3; /*0x17 */ ++ U32 EventThreshold; /*0x18 */ ++ U16 ThresholdFlags; /*0x1C */ ++ U16 Reserved4; /*0x1E */ ++} MPI2_EVENT_DATA_SAS_PHY_COUNTER, ++ *PTR_MPI2_EVENT_DATA_SAS_PHY_COUNTER, ++ Mpi2EventDataSasPhyCounter_t, ++ *pMpi2EventDataSasPhyCounter_t; ++ ++/*use MPI2_SASPHY3_EVENT_CODE_ values from mpi2_cnfg.h ++ *for the PhyEventCode field */ ++ ++/*use MPI2_SASPHY3_COUNTER_TYPE_ values from mpi2_cnfg.h ++ *for the CounterType field */ ++ ++/*use MPI2_SASPHY3_TIME_UNITS_ values from mpi2_cnfg.h ++ *for the TimeUnits field */ ++ ++/*use MPI2_SASPHY3_TFLAGS_ values from mpi2_cnfg.h ++ *for the ThresholdFlags field */ ++ ++/*SAS Quiesce Event data */ ++ ++typedef struct _MPI2_EVENT_DATA_SAS_QUIESCE { ++ U8 ReasonCode; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U32 Reserved3; /*0x04 */ ++} MPI2_EVENT_DATA_SAS_QUIESCE, ++ *PTR_MPI2_EVENT_DATA_SAS_QUIESCE, ++ Mpi2EventDataSasQuiesce_t, *pMpi2EventDataSasQuiesce_t; ++ ++/*SAS Quiesce Event data ReasonCode values */ ++#define MPI2_EVENT_SAS_QUIESCE_RC_STARTED (0x01) ++#define MPI2_EVENT_SAS_QUIESCE_RC_COMPLETED (0x02) ++ ++/*Host Based Discovery Phy Event data */ ++ ++typedef struct _MPI2_EVENT_HBD_PHY_SAS { ++ U8 Flags; /*0x00 */ ++ U8 NegotiatedLinkRate; /*0x01 */ ++ U8 PhyNum; /*0x02 */ ++ U8 PhysicalPort; /*0x03 */ ++ U32 Reserved1; /*0x04 */ ++ U8 InitialFrame[28]; /*0x08 */ ++} MPI2_EVENT_HBD_PHY_SAS, *PTR_MPI2_EVENT_HBD_PHY_SAS, ++ Mpi2EventHbdPhySas_t, *pMpi2EventHbdPhySas_t; ++ ++/*values for the Flags field */ ++#define MPI2_EVENT_HBD_SAS_FLAGS_FRAME_VALID (0x02) ++#define MPI2_EVENT_HBD_SAS_FLAGS_SATA_FRAME (0x01) ++ ++/*use MPI2_SAS_NEG_LINK_RATE_ defines from mpi2_cnfg.h ++ *for the NegotiatedLinkRate field */ ++ ++typedef union _MPI2_EVENT_HBD_DESCRIPTOR { ++ MPI2_EVENT_HBD_PHY_SAS Sas; ++} MPI2_EVENT_HBD_DESCRIPTOR, *PTR_MPI2_EVENT_HBD_DESCRIPTOR, ++ Mpi2EventHbdDescriptor_t, *pMpi2EventHbdDescriptor_t; ++ ++typedef struct _MPI2_EVENT_DATA_HBD_PHY { ++ U8 DescriptorType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U32 Reserved3; /*0x04 */ ++ MPI2_EVENT_HBD_DESCRIPTOR Descriptor; /*0x08 */ ++} MPI2_EVENT_DATA_HBD_PHY, *PTR_MPI2_EVENT_DATA_HBD_PHY, ++ Mpi2EventDataHbdPhy_t, ++ *pMpi2EventDataMpi2EventDataHbdPhy_t; ++ ++/*values for the DescriptorType field */ ++#define MPI2_EVENT_HBD_DT_SAS (0x01) ++ ++/**************************************************************************** ++* EventAck message ++****************************************************************************/ ++ ++/*EventAck Request message */ ++typedef struct _MPI2_EVENT_ACK_REQUEST { ++ U16 Reserved1; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Event; /*0x0C */ ++ U16 Reserved5; /*0x0E */ ++ U32 EventContext; /*0x10 */ ++} MPI2_EVENT_ACK_REQUEST, *PTR_MPI2_EVENT_ACK_REQUEST, ++ Mpi2EventAckRequest_t, *pMpi2EventAckRequest_t; ++ ++/*EventAck Reply message */ ++typedef struct _MPI2_EVENT_ACK_REPLY { ++ U16 Reserved1; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_EVENT_ACK_REPLY, *PTR_MPI2_EVENT_ACK_REPLY, ++ Mpi2EventAckReply_t, *pMpi2EventAckReply_t; ++ ++/**************************************************************************** ++* SendHostMessage message ++****************************************************************************/ ++ ++/*SendHostMessage Request message */ ++typedef struct _MPI2_SEND_HOST_MESSAGE_REQUEST { ++ U16 HostDataLength; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved1; /*0x04 */ ++ U8 Reserved2; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U8 Reserved4; /*0x0C */ ++ U8 DestVF_ID; /*0x0D */ ++ U16 Reserved5; /*0x0E */ ++ U32 Reserved6; /*0x10 */ ++ U32 Reserved7; /*0x14 */ ++ U32 Reserved8; /*0x18 */ ++ U32 Reserved9; /*0x1C */ ++ U32 Reserved10; /*0x20 */ ++ U32 HostData[1]; /*0x24 */ ++} MPI2_SEND_HOST_MESSAGE_REQUEST, ++ *PTR_MPI2_SEND_HOST_MESSAGE_REQUEST, ++ Mpi2SendHostMessageRequest_t, ++ *pMpi2SendHostMessageRequest_t; ++ ++/*SendHostMessage Reply message */ ++typedef struct _MPI2_SEND_HOST_MESSAGE_REPLY { ++ U16 HostDataLength; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved1; /*0x04 */ ++ U8 Reserved2; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U16 Reserved4; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_SEND_HOST_MESSAGE_REPLY, *PTR_MPI2_SEND_HOST_MESSAGE_REPLY, ++ Mpi2SendHostMessageReply_t, *pMpi2SendHostMessageReply_t; ++ ++/**************************************************************************** ++* FWDownload message ++****************************************************************************/ ++ ++/*MPI v2.0 FWDownload Request message */ ++typedef struct _MPI2_FW_DOWNLOAD_REQUEST { ++ U8 ImageType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 TotalImageSize; /*0x0C */ ++ U32 Reserved5; /*0x10 */ ++ MPI2_MPI_SGE_UNION SGL; /*0x14 */ ++} MPI2_FW_DOWNLOAD_REQUEST, *PTR_MPI2_FW_DOWNLOAD_REQUEST, ++ Mpi2FWDownloadRequest, *pMpi2FWDownloadRequest; ++ ++#define MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT (0x01) ++ ++#define MPI2_FW_DOWNLOAD_ITYPE_FW (0x01) ++#define MPI2_FW_DOWNLOAD_ITYPE_BIOS (0x02) ++#define MPI2_FW_DOWNLOAD_ITYPE_MANUFACTURING (0x06) ++#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_1 (0x07) ++#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_2 (0x08) ++#define MPI2_FW_DOWNLOAD_ITYPE_MEGARAID (0x09) ++#define MPI2_FW_DOWNLOAD_ITYPE_COMPLETE (0x0A) ++#define MPI2_FW_DOWNLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B) ++#define MPI2_FW_DOWNLOAD_ITYPE_PUBLIC_KEY (0x0C) ++#define MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC (0xF0) ++ ++/*MPI v2.0 FWDownload TransactionContext Element */ ++typedef struct _MPI2_FW_DOWNLOAD_TCSGE { ++ U8 Reserved1; /*0x00 */ ++ U8 ContextSize; /*0x01 */ ++ U8 DetailsLength; /*0x02 */ ++ U8 Flags; /*0x03 */ ++ U32 Reserved2; /*0x04 */ ++ U32 ImageOffset; /*0x08 */ ++ U32 ImageSize; /*0x0C */ ++} MPI2_FW_DOWNLOAD_TCSGE, *PTR_MPI2_FW_DOWNLOAD_TCSGE, ++ Mpi2FWDownloadTCSGE_t, *pMpi2FWDownloadTCSGE_t; ++ ++/*MPI v2.5 FWDownload Request message */ ++typedef struct _MPI25_FW_DOWNLOAD_REQUEST { ++ U8 ImageType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 TotalImageSize; /*0x0C */ ++ U32 Reserved5; /*0x10 */ ++ U32 Reserved6; /*0x14 */ ++ U32 ImageOffset; /*0x18 */ ++ U32 ImageSize; /*0x1C */ ++ MPI25_SGE_IO_UNION SGL; /*0x20 */ ++} MPI25_FW_DOWNLOAD_REQUEST, *PTR_MPI25_FW_DOWNLOAD_REQUEST, ++ Mpi25FWDownloadRequest, *pMpi25FWDownloadRequest; ++ ++/*FWDownload Reply message */ ++typedef struct _MPI2_FW_DOWNLOAD_REPLY { ++ U8 ImageType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_FW_DOWNLOAD_REPLY, *PTR_MPI2_FW_DOWNLOAD_REPLY, ++ Mpi2FWDownloadReply_t, *pMpi2FWDownloadReply_t; ++ ++/**************************************************************************** ++* FWUpload message ++****************************************************************************/ ++ ++/*MPI v2.0 FWUpload Request message */ ++typedef struct _MPI2_FW_UPLOAD_REQUEST { ++ U8 ImageType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 Reserved5; /*0x0C */ ++ U32 Reserved6; /*0x10 */ ++ MPI2_MPI_SGE_UNION SGL; /*0x14 */ ++} MPI2_FW_UPLOAD_REQUEST, *PTR_MPI2_FW_UPLOAD_REQUEST, ++ Mpi2FWUploadRequest_t, *pMpi2FWUploadRequest_t; ++ ++#define MPI2_FW_UPLOAD_ITYPE_FW_CURRENT (0x00) ++#define MPI2_FW_UPLOAD_ITYPE_FW_FLASH (0x01) ++#define MPI2_FW_UPLOAD_ITYPE_BIOS_FLASH (0x02) ++#define MPI2_FW_UPLOAD_ITYPE_FW_BACKUP (0x05) ++#define MPI2_FW_UPLOAD_ITYPE_MANUFACTURING (0x06) ++#define MPI2_FW_UPLOAD_ITYPE_CONFIG_1 (0x07) ++#define MPI2_FW_UPLOAD_ITYPE_CONFIG_2 (0x08) ++#define MPI2_FW_UPLOAD_ITYPE_MEGARAID (0x09) ++#define MPI2_FW_UPLOAD_ITYPE_COMPLETE (0x0A) ++#define MPI2_FW_UPLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B) ++#define MPI2_FW_UPLOAD_ITYPE_CBB_BACKUP (0x0D) ++ ++/*MPI v2.0 FWUpload TransactionContext Element */ ++typedef struct _MPI2_FW_UPLOAD_TCSGE { ++ U8 Reserved1; /*0x00 */ ++ U8 ContextSize; /*0x01 */ ++ U8 DetailsLength; /*0x02 */ ++ U8 Flags; /*0x03 */ ++ U32 Reserved2; /*0x04 */ ++ U32 ImageOffset; /*0x08 */ ++ U32 ImageSize; /*0x0C */ ++} MPI2_FW_UPLOAD_TCSGE, *PTR_MPI2_FW_UPLOAD_TCSGE, ++ Mpi2FWUploadTCSGE_t, *pMpi2FWUploadTCSGE_t; ++ ++/*MPI v2.5 FWUpload Request message */ ++typedef struct _MPI25_FW_UPLOAD_REQUEST { ++ U8 ImageType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 Reserved5; /*0x0C */ ++ U32 Reserved6; /*0x10 */ ++ U32 Reserved7; /*0x14 */ ++ U32 ImageOffset; /*0x18 */ ++ U32 ImageSize; /*0x1C */ ++ MPI25_SGE_IO_UNION SGL; /*0x20 */ ++} MPI25_FW_UPLOAD_REQUEST, *PTR_MPI25_FW_UPLOAD_REQUEST, ++ Mpi25FWUploadRequest_t, *pMpi25FWUploadRequest_t; ++ ++/*FWUpload Reply message */ ++typedef struct _MPI2_FW_UPLOAD_REPLY { ++ U8 ImageType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U32 ActualImageSize; /*0x14 */ ++} MPI2_FW_UPLOAD_REPLY, *PTR_MPI2_FW_UPLOAD_REPLY, ++ Mpi2FWUploadReply_t, *pMPi2FWUploadReply_t; ++ ++/*FW Image Header */ ++typedef struct _MPI2_FW_IMAGE_HEADER { ++ U32 Signature; /*0x00 */ ++ U32 Signature0; /*0x04 */ ++ U32 Signature1; /*0x08 */ ++ U32 Signature2; /*0x0C */ ++ MPI2_VERSION_UNION MPIVersion; /*0x10 */ ++ MPI2_VERSION_UNION FWVersion; /*0x14 */ ++ MPI2_VERSION_UNION NVDATAVersion; /*0x18 */ ++ MPI2_VERSION_UNION PackageVersion; /*0x1C */ ++ U16 VendorID; /*0x20 */ ++ U16 ProductID; /*0x22 */ ++ U16 ProtocolFlags; /*0x24 */ ++ U16 Reserved26; /*0x26 */ ++ U32 IOCCapabilities; /*0x28 */ ++ U32 ImageSize; /*0x2C */ ++ U32 NextImageHeaderOffset; /*0x30 */ ++ U32 Checksum; /*0x34 */ ++ U32 Reserved38; /*0x38 */ ++ U32 Reserved3C; /*0x3C */ ++ U32 Reserved40; /*0x40 */ ++ U32 Reserved44; /*0x44 */ ++ U32 Reserved48; /*0x48 */ ++ U32 Reserved4C; /*0x4C */ ++ U32 Reserved50; /*0x50 */ ++ U32 Reserved54; /*0x54 */ ++ U32 Reserved58; /*0x58 */ ++ U32 Reserved5C; /*0x5C */ ++ U32 BootFlags; /*0x60 */ ++ U32 FirmwareVersionNameWhat; /*0x64 */ ++ U8 FirmwareVersionName[32]; /*0x68 */ ++ U32 VendorNameWhat; /*0x88 */ ++ U8 VendorName[32]; /*0x8C */ ++ U32 PackageNameWhat; /*0x88 */ ++ U8 PackageName[32]; /*0x8C */ ++ U32 ReservedD0; /*0xD0 */ ++ U32 ReservedD4; /*0xD4 */ ++ U32 ReservedD8; /*0xD8 */ ++ U32 ReservedDC; /*0xDC */ ++ U32 ReservedE0; /*0xE0 */ ++ U32 ReservedE4; /*0xE4 */ ++ U32 ReservedE8; /*0xE8 */ ++ U32 ReservedEC; /*0xEC */ ++ U32 ReservedF0; /*0xF0 */ ++ U32 ReservedF4; /*0xF4 */ ++ U32 ReservedF8; /*0xF8 */ ++ U32 ReservedFC; /*0xFC */ ++} MPI2_FW_IMAGE_HEADER, *PTR_MPI2_FW_IMAGE_HEADER, ++ Mpi2FWImageHeader_t, *pMpi2FWImageHeader_t; ++ ++/*Signature field */ ++#define MPI2_FW_HEADER_SIGNATURE_OFFSET (0x00) ++#define MPI2_FW_HEADER_SIGNATURE_MASK (0xFF000000) ++#define MPI2_FW_HEADER_SIGNATURE (0xEA000000) ++#define MPI26_FW_HEADER_SIGNATURE (0xEB000000) ++ ++/*Signature0 field */ ++#define MPI2_FW_HEADER_SIGNATURE0_OFFSET (0x04) ++#define MPI2_FW_HEADER_SIGNATURE0 (0x5AFAA55A) ++/* Last byte is defined by architecture */ ++#define MPI26_FW_HEADER_SIGNATURE0_BASE (0x5AEAA500) ++#define MPI26_FW_HEADER_SIGNATURE0_ARC_0 (0x5A) ++#define MPI26_FW_HEADER_SIGNATURE0_ARC_1 (0x00) ++#define MPI26_FW_HEADER_SIGNATURE0_ARC_2 (0x01) ++/* legacy (0x5AEAA55A) */ ++#define MPI26_FW_HEADER_SIGNATURE0 \ ++ (MPI26_FW_HEADER_SIGNATURE0_BASE+MPI26_FW_HEADER_SIGNATURE0_ARC_0) ++#define MPI26_FW_HEADER_SIGNATURE0_3516 \ ++ (MPI26_FW_HEADER_SIGNATURE0_BASE+MPI26_FW_HEADER_SIGNATURE0_ARC_1) ++ ++/*Signature1 field */ ++#define MPI2_FW_HEADER_SIGNATURE1_OFFSET (0x08) ++#define MPI2_FW_HEADER_SIGNATURE1 (0xA55AFAA5) ++#define MPI26_FW_HEADER_SIGNATURE1 (0xA55AEAA5) ++ ++/*Signature2 field */ ++#define MPI2_FW_HEADER_SIGNATURE2_OFFSET (0x0C) ++#define MPI2_FW_HEADER_SIGNATURE2 (0x5AA55AFA) ++#define MPI26_FW_HEADER_SIGNATURE2 (0x5AA55AEA) ++ ++/*defines for using the ProductID field */ ++#define MPI2_FW_HEADER_PID_TYPE_MASK (0xF000) ++#define MPI2_FW_HEADER_PID_TYPE_SAS (0x2000) ++ ++#define MPI2_FW_HEADER_PID_PROD_MASK (0x0F00) ++#define MPI2_FW_HEADER_PID_PROD_A (0x0000) ++#define MPI2_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI (0x0200) ++#define MPI2_FW_HEADER_PID_PROD_IR_SCSI (0x0700) ++ ++#define MPI2_FW_HEADER_PID_FAMILY_MASK (0x00FF) ++/*SAS ProductID Family bits */ ++#define MPI2_FW_HEADER_PID_FAMILY_2108_SAS (0x0013) ++#define MPI2_FW_HEADER_PID_FAMILY_2208_SAS (0x0014) ++#define MPI25_FW_HEADER_PID_FAMILY_3108_SAS (0x0021) ++#define MPI26_FW_HEADER_PID_FAMILY_3324_SAS (0x0028) ++#define MPI26_FW_HEADER_PID_FAMILY_3516_SAS (0x0031) ++ ++/*use MPI2_IOCFACTS_PROTOCOL_ defines for ProtocolFlags field */ ++ ++/*use MPI2_IOCFACTS_CAPABILITY_ defines for IOCCapabilities field */ ++ ++#define MPI2_FW_HEADER_IMAGESIZE_OFFSET (0x2C) ++#define MPI2_FW_HEADER_NEXTIMAGE_OFFSET (0x30) ++#define MPI26_FW_HEADER_BOOTFLAGS_OFFSET (0x60) ++#define MPI2_FW_HEADER_VERNMHWAT_OFFSET (0x64) ++ ++#define MPI2_FW_HEADER_WHAT_SIGNATURE (0x29232840) ++ ++#define MPI2_FW_HEADER_SIZE (0x100) ++ ++/*Extended Image Header */ ++typedef struct _MPI2_EXT_IMAGE_HEADER { ++ U8 ImageType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U32 Checksum; /*0x04 */ ++ U32 ImageSize; /*0x08 */ ++ U32 NextImageHeaderOffset; /*0x0C */ ++ U32 PackageVersion; /*0x10 */ ++ U32 Reserved3; /*0x14 */ ++ U32 Reserved4; /*0x18 */ ++ U32 Reserved5; /*0x1C */ ++ U8 IdentifyString[32]; /*0x20 */ ++} MPI2_EXT_IMAGE_HEADER, *PTR_MPI2_EXT_IMAGE_HEADER, ++ Mpi2ExtImageHeader_t, *pMpi2ExtImageHeader_t; ++ ++/*useful offsets */ ++#define MPI2_EXT_IMAGE_IMAGETYPE_OFFSET (0x00) ++#define MPI2_EXT_IMAGE_IMAGESIZE_OFFSET (0x08) ++#define MPI2_EXT_IMAGE_NEXTIMAGE_OFFSET (0x0C) ++ ++#define MPI2_EXT_IMAGE_HEADER_SIZE (0x40) ++ ++/*defines for the ImageType field */ ++#define MPI2_EXT_IMAGE_TYPE_UNSPECIFIED (0x00) ++#define MPI2_EXT_IMAGE_TYPE_FW (0x01) ++#define MPI2_EXT_IMAGE_TYPE_NVDATA (0x03) ++#define MPI2_EXT_IMAGE_TYPE_BOOTLOADER (0x04) ++#define MPI2_EXT_IMAGE_TYPE_INITIALIZATION (0x05) ++#define MPI2_EXT_IMAGE_TYPE_FLASH_LAYOUT (0x06) ++#define MPI2_EXT_IMAGE_TYPE_SUPPORTED_DEVICES (0x07) ++#define MPI2_EXT_IMAGE_TYPE_MEGARAID (0x08) ++#define MPI2_EXT_IMAGE_TYPE_ENCRYPTED_HASH (0x09) ++#define MPI2_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC (0x80) ++#define MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC (0xFF) ++ ++#define MPI2_EXT_IMAGE_TYPE_MAX (MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC) ++ ++/*FLASH Layout Extended Image Data */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check RegionsPerLayout at runtime. ++ */ ++#ifndef MPI2_FLASH_NUMBER_OF_REGIONS ++#define MPI2_FLASH_NUMBER_OF_REGIONS (1) ++#endif ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check NumberOfLayouts at runtime. ++ */ ++#ifndef MPI2_FLASH_NUMBER_OF_LAYOUTS ++#define MPI2_FLASH_NUMBER_OF_LAYOUTS (1) ++#endif ++ ++typedef struct _MPI2_FLASH_REGION { ++ U8 RegionType; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U32 RegionOffset; /*0x04 */ ++ U32 RegionSize; /*0x08 */ ++ U32 Reserved3; /*0x0C */ ++} MPI2_FLASH_REGION, *PTR_MPI2_FLASH_REGION, ++ Mpi2FlashRegion_t, *pMpi2FlashRegion_t; ++ ++typedef struct _MPI2_FLASH_LAYOUT { ++ U32 FlashSize; /*0x00 */ ++ U32 Reserved1; /*0x04 */ ++ U32 Reserved2; /*0x08 */ ++ U32 Reserved3; /*0x0C */ ++ MPI2_FLASH_REGION Region[MPI2_FLASH_NUMBER_OF_REGIONS]; /*0x10 */ ++} MPI2_FLASH_LAYOUT, *PTR_MPI2_FLASH_LAYOUT, ++ Mpi2FlashLayout_t, *pMpi2FlashLayout_t; ++ ++typedef struct _MPI2_FLASH_LAYOUT_DATA { ++ U8 ImageRevision; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 SizeOfRegion; /*0x02 */ ++ U8 Reserved2; /*0x03 */ ++ U16 NumberOfLayouts; /*0x04 */ ++ U16 RegionsPerLayout; /*0x06 */ ++ U16 MinimumSectorAlignment; /*0x08 */ ++ U16 Reserved3; /*0x0A */ ++ U32 Reserved4; /*0x0C */ ++ MPI2_FLASH_LAYOUT Layout[MPI2_FLASH_NUMBER_OF_LAYOUTS]; /*0x10 */ ++} MPI2_FLASH_LAYOUT_DATA, *PTR_MPI2_FLASH_LAYOUT_DATA, ++ Mpi2FlashLayoutData_t, *pMpi2FlashLayoutData_t; ++ ++/*defines for the RegionType field */ ++#define MPI2_FLASH_REGION_UNUSED (0x00) ++#define MPI2_FLASH_REGION_FIRMWARE (0x01) ++#define MPI2_FLASH_REGION_BIOS (0x02) ++#define MPI2_FLASH_REGION_NVDATA (0x03) ++#define MPI2_FLASH_REGION_FIRMWARE_BACKUP (0x05) ++#define MPI2_FLASH_REGION_MFG_INFORMATION (0x06) ++#define MPI2_FLASH_REGION_CONFIG_1 (0x07) ++#define MPI2_FLASH_REGION_CONFIG_2 (0x08) ++#define MPI2_FLASH_REGION_MEGARAID (0x09) ++#define MPI2_FLASH_REGION_COMMON_BOOT_BLOCK (0x0A) ++#define MPI2_FLASH_REGION_INIT (MPI2_FLASH_REGION_COMMON_BOOT_BLOCK) ++#define MPI2_FLASH_REGION_CBB_BACKUP (0x0D) ++ ++/*ImageRevision */ ++#define MPI2_FLASH_LAYOUT_IMAGE_REVISION (0x00) ++ ++/*Supported Devices Extended Image Data */ ++ ++/* ++ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to ++ *one and check NumberOfDevices at runtime. ++ */ ++#ifndef MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES ++#define MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES (1) ++#endif ++ ++typedef struct _MPI2_SUPPORTED_DEVICE { ++ U16 DeviceID; /*0x00 */ ++ U16 VendorID; /*0x02 */ ++ U16 DeviceIDMask; /*0x04 */ ++ U16 Reserved1; /*0x06 */ ++ U8 LowPCIRev; /*0x08 */ ++ U8 HighPCIRev; /*0x09 */ ++ U16 Reserved2; /*0x0A */ ++ U32 Reserved3; /*0x0C */ ++} MPI2_SUPPORTED_DEVICE, *PTR_MPI2_SUPPORTED_DEVICE, ++ Mpi2SupportedDevice_t, *pMpi2SupportedDevice_t; ++ ++typedef struct _MPI2_SUPPORTED_DEVICES_DATA { ++ U8 ImageRevision; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 NumberOfDevices; /*0x02 */ ++ U8 Reserved2; /*0x03 */ ++ U32 Reserved3; /*0x04 */ ++ MPI2_SUPPORTED_DEVICE ++ SupportedDevice[MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES];/*0x08 */ ++} MPI2_SUPPORTED_DEVICES_DATA, *PTR_MPI2_SUPPORTED_DEVICES_DATA, ++ Mpi2SupportedDevicesData_t, *pMpi2SupportedDevicesData_t; ++ ++/*ImageRevision */ ++#define MPI2_SUPPORTED_DEVICES_IMAGE_REVISION (0x00) ++ ++/*Init Extended Image Data */ ++ ++typedef struct _MPI2_INIT_IMAGE_FOOTER { ++ U32 BootFlags; /*0x00 */ ++ U32 ImageSize; /*0x04 */ ++ U32 Signature0; /*0x08 */ ++ U32 Signature1; /*0x0C */ ++ U32 Signature2; /*0x10 */ ++ U32 ResetVector; /*0x14 */ ++} MPI2_INIT_IMAGE_FOOTER, *PTR_MPI2_INIT_IMAGE_FOOTER, ++ Mpi2InitImageFooter_t, *pMpi2InitImageFooter_t; ++ ++/*defines for the BootFlags field */ ++#define MPI2_INIT_IMAGE_BOOTFLAGS_OFFSET (0x00) ++ ++/*defines for the ImageSize field */ ++#define MPI2_INIT_IMAGE_IMAGESIZE_OFFSET (0x04) ++ ++/*defines for the Signature0 field */ ++#define MPI2_INIT_IMAGE_SIGNATURE0_OFFSET (0x08) ++#define MPI2_INIT_IMAGE_SIGNATURE0 (0x5AA55AEA) ++ ++/*defines for the Signature1 field */ ++#define MPI2_INIT_IMAGE_SIGNATURE1_OFFSET (0x0C) ++#define MPI2_INIT_IMAGE_SIGNATURE1 (0xA55AEAA5) ++ ++/*defines for the Signature2 field */ ++#define MPI2_INIT_IMAGE_SIGNATURE2_OFFSET (0x10) ++#define MPI2_INIT_IMAGE_SIGNATURE2 (0x5AEAA55A) ++ ++/*Signature fields as individual bytes */ ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_0 (0xEA) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_1 (0x5A) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_2 (0xA5) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_3 (0x5A) ++ ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_4 (0xA5) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_5 (0xEA) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_6 (0x5A) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_7 (0xA5) ++ ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_8 (0x5A) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_9 (0xA5) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_A (0xEA) ++#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_B (0x5A) ++ ++/*defines for the ResetVector field */ ++#define MPI2_INIT_IMAGE_RESETVECTOR_OFFSET (0x14) ++ ++ ++/* Encrypted Hash Extended Image Data */ ++ ++typedef struct _MPI25_ENCRYPTED_HASH_ENTRY { ++ U8 HashImageType; /* 0x00 */ ++ U8 HashAlgorithm; /* 0x01 */ ++ U8 EncryptionAlgorithm; /* 0x02 */ ++ U8 Reserved1; /* 0x03 */ ++ U32 Reserved2; /* 0x04 */ ++ U32 EncryptedHash[1]; /* 0x08 */ /* variable length */ ++} MPI25_ENCRYPTED_HASH_ENTRY, *PTR_MPI25_ENCRYPTED_HASH_ENTRY, ++Mpi25EncryptedHashEntry_t, *pMpi25EncryptedHashEntry_t; ++ ++/* values for HashImageType */ ++#define MPI25_HASH_IMAGE_TYPE_UNUSED (0x00) ++#define MPI25_HASH_IMAGE_TYPE_FIRMWARE (0x01) ++#define MPI25_HASH_IMAGE_TYPE_BIOS (0x02) ++ ++/* values for HashAlgorithm */ ++#define MPI25_HASH_ALGORITHM_UNUSED (0x00) ++#define MPI25_HASH_ALGORITHM_SHA256 (0x01) ++ ++/* values for EncryptionAlgorithm */ ++#define MPI25_ENCRYPTION_ALG_UNUSED (0x00) ++#define MPI25_ENCRYPTION_ALG_RSA256 (0x01) ++ ++typedef struct _MPI25_ENCRYPTED_HASH_DATA { ++ U8 ImageVersion; /* 0x00 */ ++ U8 NumHash; /* 0x01 */ ++ U16 Reserved1; /* 0x02 */ ++ U32 Reserved2; /* 0x04 */ ++ MPI25_ENCRYPTED_HASH_ENTRY EncryptedHashEntry[1]; /* 0x08 */ ++} MPI25_ENCRYPTED_HASH_DATA, *PTR_MPI25_ENCRYPTED_HASH_DATA, ++Mpi25EncryptedHashData_t, *pMpi25EncryptedHashData_t; ++ ++ ++/**************************************************************************** ++* PowerManagementControl message ++****************************************************************************/ ++ ++/*PowerManagementControl Request message */ ++typedef struct _MPI2_PWR_MGMT_CONTROL_REQUEST { ++ U8 Feature; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U8 Parameter1; /*0x0C */ ++ U8 Parameter2; /*0x0D */ ++ U8 Parameter3; /*0x0E */ ++ U8 Parameter4; /*0x0F */ ++ U32 Reserved5; /*0x10 */ ++ U32 Reserved6; /*0x14 */ ++} MPI2_PWR_MGMT_CONTROL_REQUEST, *PTR_MPI2_PWR_MGMT_CONTROL_REQUEST, ++ Mpi2PwrMgmtControlRequest_t, *pMpi2PwrMgmtControlRequest_t; ++ ++/*defines for the Feature field */ ++#define MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND (0x01) ++#define MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION (0x02) ++#define MPI2_PM_CONTROL_FEATURE_PCIE_LINK (0x03) /*obsolete */ ++#define MPI2_PM_CONTROL_FEATURE_IOC_SPEED (0x04) ++#define MPI2_PM_CONTROL_FEATURE_GLOBAL_PWR_MGMT_MODE (0x05) ++#define MPI2_PM_CONTROL_FEATURE_MIN_PRODUCT_SPECIFIC (0x80) ++#define MPI2_PM_CONTROL_FEATURE_MAX_PRODUCT_SPECIFIC (0xFF) ++ ++/*parameter usage for the MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND Feature */ ++/*Parameter1 contains a PHY number */ ++/*Parameter2 indicates power condition action using these defines */ ++#define MPI2_PM_CONTROL_PARAM2_PARTIAL (0x01) ++#define MPI2_PM_CONTROL_PARAM2_SLUMBER (0x02) ++#define MPI2_PM_CONTROL_PARAM2_EXIT_PWR_MGMT (0x03) ++/*Parameter3 and Parameter4 are reserved */ ++ ++/*parameter usage for the MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION ++ * Feature */ ++/*Parameter1 contains SAS port width modulation group number */ ++/*Parameter2 indicates IOC action using these defines */ ++#define MPI2_PM_CONTROL_PARAM2_REQUEST_OWNERSHIP (0x01) ++#define MPI2_PM_CONTROL_PARAM2_CHANGE_MODULATION (0x02) ++#define MPI2_PM_CONTROL_PARAM2_RELINQUISH_OWNERSHIP (0x03) ++/*Parameter3 indicates desired modulation level using these defines */ ++#define MPI2_PM_CONTROL_PARAM3_25_PERCENT (0x00) ++#define MPI2_PM_CONTROL_PARAM3_50_PERCENT (0x01) ++#define MPI2_PM_CONTROL_PARAM3_75_PERCENT (0x02) ++#define MPI2_PM_CONTROL_PARAM3_100_PERCENT (0x03) ++/*Parameter4 is reserved */ ++ ++/*this next set (_PCIE_LINK) is obsolete */ ++/*parameter usage for the MPI2_PM_CONTROL_FEATURE_PCIE_LINK Feature */ ++/*Parameter1 indicates desired PCIe link speed using these defines */ ++#define MPI2_PM_CONTROL_PARAM1_PCIE_2_5_GBPS (0x00) /*obsolete */ ++#define MPI2_PM_CONTROL_PARAM1_PCIE_5_0_GBPS (0x01) /*obsolete */ ++#define MPI2_PM_CONTROL_PARAM1_PCIE_8_0_GBPS (0x02) /*obsolete */ ++/*Parameter2 indicates desired PCIe link width using these defines */ ++#define MPI2_PM_CONTROL_PARAM2_WIDTH_X1 (0x01) /*obsolete */ ++#define MPI2_PM_CONTROL_PARAM2_WIDTH_X2 (0x02) /*obsolete */ ++#define MPI2_PM_CONTROL_PARAM2_WIDTH_X4 (0x04) /*obsolete */ ++#define MPI2_PM_CONTROL_PARAM2_WIDTH_X8 (0x08) /*obsolete */ ++/*Parameter3 and Parameter4 are reserved */ ++ ++/*parameter usage for the MPI2_PM_CONTROL_FEATURE_IOC_SPEED Feature */ ++/*Parameter1 indicates desired IOC hardware clock speed using these defines */ ++#define MPI2_PM_CONTROL_PARAM1_FULL_IOC_SPEED (0x01) ++#define MPI2_PM_CONTROL_PARAM1_HALF_IOC_SPEED (0x02) ++#define MPI2_PM_CONTROL_PARAM1_QUARTER_IOC_SPEED (0x04) ++#define MPI2_PM_CONTROL_PARAM1_EIGHTH_IOC_SPEED (0x08) ++/*Parameter2, Parameter3, and Parameter4 are reserved */ ++ ++/*parameter usage for the MPI2_PM_CONTROL_FEATURE_GLOBAL_PWR_MGMT_MODE Feature*/ ++/*Parameter1 indicates host action regarding global power management mode */ ++#define MPI2_PM_CONTROL_PARAM1_TAKE_CONTROL (0x01) ++#define MPI2_PM_CONTROL_PARAM1_CHANGE_GLOBAL_MODE (0x02) ++#define MPI2_PM_CONTROL_PARAM1_RELEASE_CONTROL (0x03) ++/*Parameter2 indicates the requested global power management mode */ ++#define MPI2_PM_CONTROL_PARAM2_FULL_PWR_PERF (0x01) ++#define MPI2_PM_CONTROL_PARAM2_REDUCED_PWR_PERF (0x08) ++#define MPI2_PM_CONTROL_PARAM2_STANDBY (0x40) ++/*Parameter3 and Parameter4 are reserved */ ++ ++/*PowerManagementControl Reply message */ ++typedef struct _MPI2_PWR_MGMT_CONTROL_REPLY { ++ U8 Feature; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_PWR_MGMT_CONTROL_REPLY, *PTR_MPI2_PWR_MGMT_CONTROL_REPLY, ++ Mpi2PwrMgmtControlReply_t, *pMpi2PwrMgmtControlReply_t; ++ ++/**************************************************************************** ++* IO Unit Control messages (MPI v2.6 and later only.) ++****************************************************************************/ ++ ++/* IO Unit Control Request Message */ ++typedef struct _MPI26_IOUNIT_CONTROL_REQUEST { ++ U8 Operation; /* 0x00 */ ++ U8 Reserved1; /* 0x01 */ ++ U8 ChainOffset; /* 0x02 */ ++ U8 Function; /* 0x03 */ ++ U16 DevHandle; /* 0x04 */ ++ U8 IOCParameter; /* 0x06 */ ++ U8 MsgFlags; /* 0x07 */ ++ U8 VP_ID; /* 0x08 */ ++ U8 VF_ID; /* 0x09 */ ++ U16 Reserved3; /* 0x0A */ ++ U16 Reserved4; /* 0x0C */ ++ U8 PhyNum; /* 0x0E */ ++ U8 PrimFlags; /* 0x0F */ ++ U32 Primitive; /* 0x10 */ ++ U8 LookupMethod; /* 0x14 */ ++ U8 Reserved5; /* 0x15 */ ++ U16 SlotNumber; /* 0x16 */ ++ U64 LookupAddress; /* 0x18 */ ++ U32 IOCParameterValue; /* 0x20 */ ++ U32 Reserved7; /* 0x24 */ ++ U32 Reserved8; /* 0x28 */ ++} MPI26_IOUNIT_CONTROL_REQUEST, ++ *PTR_MPI26_IOUNIT_CONTROL_REQUEST, ++ Mpi26IoUnitControlRequest_t, ++ *pMpi26IoUnitControlRequest_t; ++ ++/* values for the Operation field */ ++#define MPI26_CTRL_OP_CLEAR_ALL_PERSISTENT (0x02) ++#define MPI26_CTRL_OP_SAS_PHY_LINK_RESET (0x06) ++#define MPI26_CTRL_OP_SAS_PHY_HARD_RESET (0x07) ++#define MPI26_CTRL_OP_PHY_CLEAR_ERROR_LOG (0x08) ++#define MPI26_CTRL_OP_LINK_CLEAR_ERROR_LOG (0x09) ++#define MPI26_CTRL_OP_SAS_SEND_PRIMITIVE (0x0A) ++#define MPI26_CTRL_OP_FORCE_FULL_DISCOVERY (0x0B) ++#define MPI26_CTRL_OP_REMOVE_DEVICE (0x0D) ++#define MPI26_CTRL_OP_LOOKUP_MAPPING (0x0E) ++#define MPI26_CTRL_OP_SET_IOC_PARAMETER (0x0F) ++#define MPI26_CTRL_OP_ENABLE_FP_DEVICE (0x10) ++#define MPI26_CTRL_OP_DISABLE_FP_DEVICE (0x11) ++#define MPI26_CTRL_OP_ENABLE_FP_ALL (0x12) ++#define MPI26_CTRL_OP_DISABLE_FP_ALL (0x13) ++#define MPI26_CTRL_OP_DEV_ENABLE_NCQ (0x14) ++#define MPI26_CTRL_OP_DEV_DISABLE_NCQ (0x15) ++#define MPI26_CTRL_OP_SHUTDOWN (0x16) ++#define MPI26_CTRL_OP_DEV_ENABLE_PERSIST_CONNECTION (0x17) ++#define MPI26_CTRL_OP_DEV_DISABLE_PERSIST_CONNECTION (0x18) ++#define MPI26_CTRL_OP_DEV_CLOSE_PERSIST_CONNECTION (0x19) ++#define MPI26_CTRL_OP_PRODUCT_SPECIFIC_MIN (0x80) ++ ++/* values for the PrimFlags field */ ++#define MPI26_CTRL_PRIMFLAGS_SINGLE (0x08) ++#define MPI26_CTRL_PRIMFLAGS_TRIPLE (0x02) ++#define MPI26_CTRL_PRIMFLAGS_REDUNDANT (0x01) ++ ++/* values for the LookupMethod field */ ++#define MPI26_CTRL_LOOKUP_METHOD_WWID_ADDRESS (0x01) ++#define MPI26_CTRL_LOOKUP_METHOD_ENCLOSURE_SLOT (0x02) ++#define MPI26_CTRL_LOOKUP_METHOD_SAS_DEVICE_NAME (0x03) ++ ++ ++/* IO Unit Control Reply Message */ ++typedef struct _MPI26_IOUNIT_CONTROL_REPLY { ++ U8 Operation; /* 0x00 */ ++ U8 Reserved1; /* 0x01 */ ++ U8 MsgLength; /* 0x02 */ ++ U8 Function; /* 0x03 */ ++ U16 DevHandle; /* 0x04 */ ++ U8 IOCParameter; /* 0x06 */ ++ U8 MsgFlags; /* 0x07 */ ++ U8 VP_ID; /* 0x08 */ ++ U8 VF_ID; /* 0x09 */ ++ U16 Reserved3; /* 0x0A */ ++ U16 Reserved4; /* 0x0C */ ++ U16 IOCStatus; /* 0x0E */ ++ U32 IOCLogInfo; /* 0x10 */ ++} MPI26_IOUNIT_CONTROL_REPLY, ++ *PTR_MPI26_IOUNIT_CONTROL_REPLY, ++ Mpi26IoUnitControlReply_t, ++ *pMpi26IoUnitControlReply_t; ++ ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_raid.h b/drivers/scsi/mpt2sas/mpi/mpi2_raid.h +new file mode 100644 +index 0000000..1c0eeee +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2_raid.h +@@ -0,0 +1,355 @@ ++/* ++ * Copyright 2000-2014 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2_raid.h ++ * Title: MPI Integrated RAID messages and structures ++ * Creation Date: April 26, 2007 ++ * ++ * mpi2_raid.h Version: 02.00.11 ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 08-31-07 02.00.01 Modifications to RAID Action request and reply, ++ * including the Actions and ActionData. ++ * 02-29-08 02.00.02 Added MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD. ++ * 05-21-08 02.00.03 Added MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS so that ++ * the PhysDisk array in MPI2_RAID_VOLUME_CREATION_STRUCT ++ * can be sized by the build environment. ++ * 07-30-09 02.00.04 Added proper define for the Use Default Settings bit of ++ * VolumeCreationFlags and marked the old one as obsolete. ++ * 05-12-10 02.00.05 Added MPI2_RAID_VOL_FLAGS_OP_MDC define. ++ * 08-24-10 02.00.06 Added MPI2_RAID_ACTION_COMPATIBILITY_CHECK along with ++ * related structures and defines. ++ * Added product-specific range to RAID Action values. ++ * 11-18-11 02.00.07 Incorporating additions for MPI v2.5. ++ * 02-06-12 02.00.08 Added MPI2_RAID_ACTION_PHYSDISK_HIDDEN. ++ * 07-26-12 02.00.09 Added ElapsedSeconds field to MPI2_RAID_VOL_INDICATOR. ++ * Added MPI2_RAID_VOL_FLAGS_ELAPSED_SECONDS_VALID define. ++ * 04-17-13 02.00.10 Added MPI25_RAID_ACTION_ADATA_ALLOW_PI. ++ * 11-18-14 02.00.11 Updated copyright information. ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_RAID_H ++#define MPI2_RAID_H ++ ++/***************************************************************************** ++* ++* Integrated RAID Messages ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* RAID Action messages ++****************************************************************************/ ++ ++/* ActionDataWord defines for use with MPI2_RAID_ACTION_CREATE_VOLUME action */ ++#define MPI25_RAID_ACTION_ADATA_ALLOW_PI (0x80000000) ++ ++/*ActionDataWord defines for use with MPI2_RAID_ACTION_DELETE_VOLUME action */ ++#define MPI2_RAID_ACTION_ADATA_KEEP_LBA0 (0x00000000) ++#define MPI2_RAID_ACTION_ADATA_ZERO_LBA0 (0x00000001) ++ ++/*use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for ++ *MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */ ++ ++/*ActionDataWord defines for use with ++ *MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES action */ ++#define MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD (0x00000001) ++ ++/*ActionDataWord for MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE Action */ ++typedef struct _MPI2_RAID_ACTION_RATE_DATA { ++ U8 RateToChange; /*0x00 */ ++ U8 RateOrMode; /*0x01 */ ++ U16 DataScrubDuration; /*0x02 */ ++} MPI2_RAID_ACTION_RATE_DATA, *PTR_MPI2_RAID_ACTION_RATE_DATA, ++ Mpi2RaidActionRateData_t, *pMpi2RaidActionRateData_t; ++ ++#define MPI2_RAID_ACTION_SET_RATE_RESYNC (0x00) ++#define MPI2_RAID_ACTION_SET_RATE_DATA_SCRUB (0x01) ++#define MPI2_RAID_ACTION_SET_RATE_POWERSAVE_MODE (0x02) ++ ++/*ActionDataWord for MPI2_RAID_ACTION_START_RAID_FUNCTION Action */ ++typedef struct _MPI2_RAID_ACTION_START_RAID_FUNCTION { ++ U8 RAIDFunction; /*0x00 */ ++ U8 Flags; /*0x01 */ ++ U16 Reserved1; /*0x02 */ ++} MPI2_RAID_ACTION_START_RAID_FUNCTION, ++ *PTR_MPI2_RAID_ACTION_START_RAID_FUNCTION, ++ Mpi2RaidActionStartRaidFunction_t, ++ *pMpi2RaidActionStartRaidFunction_t; ++ ++/*defines for the RAIDFunction field */ ++#define MPI2_RAID_ACTION_START_BACKGROUND_INIT (0x00) ++#define MPI2_RAID_ACTION_START_ONLINE_CAP_EXPANSION (0x01) ++#define MPI2_RAID_ACTION_START_CONSISTENCY_CHECK (0x02) ++ ++/*defines for the Flags field */ ++#define MPI2_RAID_ACTION_START_NEW (0x00) ++#define MPI2_RAID_ACTION_START_RESUME (0x01) ++ ++/*ActionDataWord for MPI2_RAID_ACTION_STOP_RAID_FUNCTION Action */ ++typedef struct _MPI2_RAID_ACTION_STOP_RAID_FUNCTION { ++ U8 RAIDFunction; /*0x00 */ ++ U8 Flags; /*0x01 */ ++ U16 Reserved1; /*0x02 */ ++} MPI2_RAID_ACTION_STOP_RAID_FUNCTION, ++ *PTR_MPI2_RAID_ACTION_STOP_RAID_FUNCTION, ++ Mpi2RaidActionStopRaidFunction_t, ++ *pMpi2RaidActionStopRaidFunction_t; ++ ++/*defines for the RAIDFunction field */ ++#define MPI2_RAID_ACTION_STOP_BACKGROUND_INIT (0x00) ++#define MPI2_RAID_ACTION_STOP_ONLINE_CAP_EXPANSION (0x01) ++#define MPI2_RAID_ACTION_STOP_CONSISTENCY_CHECK (0x02) ++ ++/*defines for the Flags field */ ++#define MPI2_RAID_ACTION_STOP_ABORT (0x00) ++#define MPI2_RAID_ACTION_STOP_PAUSE (0x01) ++ ++/*ActionDataWord for MPI2_RAID_ACTION_CREATE_HOT_SPARE Action */ ++typedef struct _MPI2_RAID_ACTION_HOT_SPARE { ++ U8 HotSparePool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 DevHandle; /*0x02 */ ++} MPI2_RAID_ACTION_HOT_SPARE, *PTR_MPI2_RAID_ACTION_HOT_SPARE, ++ Mpi2RaidActionHotSpare_t, *pMpi2RaidActionHotSpare_t; ++ ++/*ActionDataWord for MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE Action */ ++typedef struct _MPI2_RAID_ACTION_FW_UPDATE_MODE { ++ U8 Flags; /*0x00 */ ++ U8 DeviceFirmwareUpdateModeTimeout; /*0x01 */ ++ U16 Reserved1; /*0x02 */ ++} MPI2_RAID_ACTION_FW_UPDATE_MODE, ++ *PTR_MPI2_RAID_ACTION_FW_UPDATE_MODE, ++ Mpi2RaidActionFwUpdateMode_t, ++ *pMpi2RaidActionFwUpdateMode_t; ++ ++/*ActionDataWord defines for use with ++ *MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE action */ ++#define MPI2_RAID_ACTION_ADATA_DISABLE_FW_UPDATE (0x00) ++#define MPI2_RAID_ACTION_ADATA_ENABLE_FW_UPDATE (0x01) ++ ++typedef union _MPI2_RAID_ACTION_DATA { ++ U32 Word; ++ MPI2_RAID_ACTION_RATE_DATA Rates; ++ MPI2_RAID_ACTION_START_RAID_FUNCTION StartRaidFunction; ++ MPI2_RAID_ACTION_STOP_RAID_FUNCTION StopRaidFunction; ++ MPI2_RAID_ACTION_HOT_SPARE HotSpare; ++ MPI2_RAID_ACTION_FW_UPDATE_MODE FwUpdateMode; ++} MPI2_RAID_ACTION_DATA, *PTR_MPI2_RAID_ACTION_DATA, ++ Mpi2RaidActionData_t, *pMpi2RaidActionData_t; ++ ++/*RAID Action Request Message */ ++typedef struct _MPI2_RAID_ACTION_REQUEST { ++ U8 Action; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 VolDevHandle; /*0x04 */ ++ U8 PhysDiskNum; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved2; /*0x0A */ ++ U32 Reserved3; /*0x0C */ ++ MPI2_RAID_ACTION_DATA ActionDataWord; /*0x10 */ ++ MPI2_SGE_SIMPLE_UNION ActionDataSGE; /*0x14 */ ++} MPI2_RAID_ACTION_REQUEST, *PTR_MPI2_RAID_ACTION_REQUEST, ++ Mpi2RaidActionRequest_t, *pMpi2RaidActionRequest_t; ++ ++/*RAID Action request Action values */ ++ ++#define MPI2_RAID_ACTION_INDICATOR_STRUCT (0x01) ++#define MPI2_RAID_ACTION_CREATE_VOLUME (0x02) ++#define MPI2_RAID_ACTION_DELETE_VOLUME (0x03) ++#define MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES (0x04) ++#define MPI2_RAID_ACTION_ENABLE_ALL_VOLUMES (0x05) ++#define MPI2_RAID_ACTION_PHYSDISK_OFFLINE (0x0A) ++#define MPI2_RAID_ACTION_PHYSDISK_ONLINE (0x0B) ++#define MPI2_RAID_ACTION_FAIL_PHYSDISK (0x0F) ++#define MPI2_RAID_ACTION_ACTIVATE_VOLUME (0x11) ++#define MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE (0x15) ++#define MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE (0x17) ++#define MPI2_RAID_ACTION_SET_VOLUME_NAME (0x18) ++#define MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE (0x19) ++#define MPI2_RAID_ACTION_ENABLE_FAILED_VOLUME (0x1C) ++#define MPI2_RAID_ACTION_CREATE_HOT_SPARE (0x1D) ++#define MPI2_RAID_ACTION_DELETE_HOT_SPARE (0x1E) ++#define MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED (0x20) ++#define MPI2_RAID_ACTION_START_RAID_FUNCTION (0x21) ++#define MPI2_RAID_ACTION_STOP_RAID_FUNCTION (0x22) ++#define MPI2_RAID_ACTION_COMPATIBILITY_CHECK (0x23) ++#define MPI2_RAID_ACTION_PHYSDISK_HIDDEN (0x24) ++#define MPI2_RAID_ACTION_MIN_PRODUCT_SPECIFIC (0x80) ++#define MPI2_RAID_ACTION_MAX_PRODUCT_SPECIFIC (0xFF) ++ ++/*RAID Volume Creation Structure */ ++ ++/* ++ *The following define can be customized for the targeted product. ++ */ ++#ifndef MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS ++#define MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS (1) ++#endif ++ ++typedef struct _MPI2_RAID_VOLUME_PHYSDISK { ++ U8 RAIDSetNum; /*0x00 */ ++ U8 PhysDiskMap; /*0x01 */ ++ U16 PhysDiskDevHandle; /*0x02 */ ++} MPI2_RAID_VOLUME_PHYSDISK, *PTR_MPI2_RAID_VOLUME_PHYSDISK, ++ Mpi2RaidVolumePhysDisk_t, *pMpi2RaidVolumePhysDisk_t; ++ ++/*defines for the PhysDiskMap field */ ++#define MPI2_RAIDACTION_PHYSDISK_PRIMARY (0x01) ++#define MPI2_RAIDACTION_PHYSDISK_SECONDARY (0x02) ++ ++typedef struct _MPI2_RAID_VOLUME_CREATION_STRUCT { ++ U8 NumPhysDisks; /*0x00 */ ++ U8 VolumeType; /*0x01 */ ++ U16 Reserved1; /*0x02 */ ++ U32 VolumeCreationFlags; /*0x04 */ ++ U32 VolumeSettings; /*0x08 */ ++ U8 Reserved2; /*0x0C */ ++ U8 ResyncRate; /*0x0D */ ++ U16 DataScrubDuration; /*0x0E */ ++ U64 VolumeMaxLBA; /*0x10 */ ++ U32 StripeSize; /*0x18 */ ++ U8 Name[16]; /*0x1C */ ++ MPI2_RAID_VOLUME_PHYSDISK ++ PhysDisk[MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS]; /*0x2C */ ++} MPI2_RAID_VOLUME_CREATION_STRUCT, ++ *PTR_MPI2_RAID_VOLUME_CREATION_STRUCT, ++ Mpi2RaidVolumeCreationStruct_t, ++ *pMpi2RaidVolumeCreationStruct_t; ++ ++/*use MPI2_RAID_VOL_TYPE_ defines from mpi2_cnfg.h for VolumeType */ ++ ++/*defines for the VolumeCreationFlags field */ ++#define MPI2_RAID_VOL_CREATION_DEFAULT_SETTINGS (0x80000000) ++#define MPI2_RAID_VOL_CREATION_BACKGROUND_INIT (0x00000004) ++#define MPI2_RAID_VOL_CREATION_LOW_LEVEL_INIT (0x00000002) ++#define MPI2_RAID_VOL_CREATION_MIGRATE_DATA (0x00000001) ++/*The following is an obsolete define. ++ *It must be shifted left 24 bits in order to set the proper bit. ++ */ ++#define MPI2_RAID_VOL_CREATION_USE_DEFAULT_SETTINGS (0x80) ++ ++/*RAID Online Capacity Expansion Structure */ ++ ++typedef struct _MPI2_RAID_ONLINE_CAPACITY_EXPANSION { ++ U32 Flags; /*0x00 */ ++ U16 DevHandle0; /*0x04 */ ++ U16 Reserved1; /*0x06 */ ++ U16 DevHandle1; /*0x08 */ ++ U16 Reserved2; /*0x0A */ ++} MPI2_RAID_ONLINE_CAPACITY_EXPANSION, ++ *PTR_MPI2_RAID_ONLINE_CAPACITY_EXPANSION, ++ Mpi2RaidOnlineCapacityExpansion_t, ++ *pMpi2RaidOnlineCapacityExpansion_t; ++ ++/*RAID Compatibility Input Structure */ ++ ++typedef struct _MPI2_RAID_COMPATIBILITY_INPUT_STRUCT { ++ U16 SourceDevHandle; /*0x00 */ ++ U16 CandidateDevHandle; /*0x02 */ ++ U32 Flags; /*0x04 */ ++ U32 Reserved1; /*0x08 */ ++ U32 Reserved2; /*0x0C */ ++} MPI2_RAID_COMPATIBILITY_INPUT_STRUCT, ++ *PTR_MPI2_RAID_COMPATIBILITY_INPUT_STRUCT, ++ Mpi2RaidCompatibilityInputStruct_t, ++ *pMpi2RaidCompatibilityInputStruct_t; ++ ++/*defines for RAID Compatibility Structure Flags field */ ++#define MPI2_RAID_COMPAT_SOURCE_IS_VOLUME_FLAG (0x00000002) ++#define MPI2_RAID_COMPAT_REPORT_SOURCE_INFO_FLAG (0x00000001) ++ ++/*RAID Volume Indicator Structure */ ++ ++typedef struct _MPI2_RAID_VOL_INDICATOR { ++ U64 TotalBlocks; /*0x00 */ ++ U64 BlocksRemaining; /*0x08 */ ++ U32 Flags; /*0x10 */ ++ U32 ElapsedSeconds; /* 0x14 */ ++} MPI2_RAID_VOL_INDICATOR, *PTR_MPI2_RAID_VOL_INDICATOR, ++ Mpi2RaidVolIndicator_t, *pMpi2RaidVolIndicator_t; ++ ++/*defines for RAID Volume Indicator Flags field */ ++#define MPI2_RAID_VOL_FLAGS_ELAPSED_SECONDS_VALID (0x80000000) ++#define MPI2_RAID_VOL_FLAGS_OP_MASK (0x0000000F) ++#define MPI2_RAID_VOL_FLAGS_OP_BACKGROUND_INIT (0x00000000) ++#define MPI2_RAID_VOL_FLAGS_OP_ONLINE_CAP_EXPANSION (0x00000001) ++#define MPI2_RAID_VOL_FLAGS_OP_CONSISTENCY_CHECK (0x00000002) ++#define MPI2_RAID_VOL_FLAGS_OP_RESYNC (0x00000003) ++#define MPI2_RAID_VOL_FLAGS_OP_MDC (0x00000004) ++ ++/*RAID Compatibility Result Structure */ ++ ++typedef struct _MPI2_RAID_COMPATIBILITY_RESULT_STRUCT { ++ U8 State; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U16 Reserved2; /*0x02 */ ++ U32 GenericAttributes; /*0x04 */ ++ U32 OEMSpecificAttributes; /*0x08 */ ++ U32 Reserved3; /*0x0C */ ++ U32 Reserved4; /*0x10 */ ++} MPI2_RAID_COMPATIBILITY_RESULT_STRUCT, ++ *PTR_MPI2_RAID_COMPATIBILITY_RESULT_STRUCT, ++ Mpi2RaidCompatibilityResultStruct_t, ++ *pMpi2RaidCompatibilityResultStruct_t; ++ ++/*defines for RAID Compatibility Result Structure State field */ ++#define MPI2_RAID_COMPAT_STATE_COMPATIBLE (0x00) ++#define MPI2_RAID_COMPAT_STATE_NOT_COMPATIBLE (0x01) ++ ++/*defines for RAID Compatibility Result Structure GenericAttributes field */ ++#define MPI2_RAID_COMPAT_GENATTRIB_4K_SECTOR (0x00000010) ++ ++#define MPI2_RAID_COMPAT_GENATTRIB_MEDIA_MASK (0x0000000C) ++#define MPI2_RAID_COMPAT_GENATTRIB_SOLID_STATE_DRIVE (0x00000008) ++#define MPI2_RAID_COMPAT_GENATTRIB_HARD_DISK_DRIVE (0x00000004) ++ ++#define MPI2_RAID_COMPAT_GENATTRIB_PROTOCOL_MASK (0x00000003) ++#define MPI2_RAID_COMPAT_GENATTRIB_SAS_PROTOCOL (0x00000002) ++#define MPI2_RAID_COMPAT_GENATTRIB_SATA_PROTOCOL (0x00000001) ++ ++/*RAID Action Reply ActionData union */ ++typedef union _MPI2_RAID_ACTION_REPLY_DATA { ++ U32 Word[6]; ++ MPI2_RAID_VOL_INDICATOR RaidVolumeIndicator; ++ U16 VolDevHandle; ++ U8 VolumeState; ++ U8 PhysDiskNum; ++ MPI2_RAID_COMPATIBILITY_RESULT_STRUCT RaidCompatibilityResult; ++} MPI2_RAID_ACTION_REPLY_DATA, *PTR_MPI2_RAID_ACTION_REPLY_DATA, ++ Mpi2RaidActionReplyData_t, *pMpi2RaidActionReplyData_t; ++ ++/*use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for ++ *MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */ ++ ++/*RAID Action Reply Message */ ++typedef struct _MPI2_RAID_ACTION_REPLY { ++ U8 Action; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 VolDevHandle; /*0x04 */ ++ U8 PhysDiskNum; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved2; /*0x0A */ ++ U16 Reserved3; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ MPI2_RAID_ACTION_REPLY_DATA ActionData; /*0x14 */ ++} MPI2_RAID_ACTION_REPLY, *PTR_MPI2_RAID_ACTION_REPLY, ++ Mpi2RaidActionReply_t, *pMpi2RaidActionReply_t; ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_sas.h b/drivers/scsi/mpt2sas/mpi/mpi2_sas.h +new file mode 100644 +index 0000000..c10c2c0 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2_sas.h +@@ -0,0 +1,303 @@ ++/* ++ * Copyright 2000-2015 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2_sas.h ++ * Title: MPI Serial Attached SCSI structures and definitions ++ * Creation Date: February 9, 2007 ++ * ++ * mpi2_sas.h Version: 02.00.10 ++ * ++ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 ++ * prefix are for use only on MPI v2.5 products, and must not be used ++ * with MPI v2.0 products. Unless otherwise noted, names beginning with ++ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products. ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 06-26-07 02.00.01 Added Clear All Persistent Operation to SAS IO Unit ++ * Control Request. ++ * 10-02-08 02.00.02 Added Set IOC Parameter Operation to SAS IO Unit Control ++ * Request. ++ * 10-28-09 02.00.03 Changed the type of SGL in MPI2_SATA_PASSTHROUGH_REQUEST ++ * to MPI2_SGE_IO_UNION since it supports chained SGLs. ++ * 05-12-10 02.00.04 Modified some comments. ++ * 08-11-10 02.00.05 Added NCQ operations to SAS IO Unit Control. ++ * 11-18-11 02.00.06 Incorporating additions for MPI v2.5. ++ * 07-10-12 02.00.07 Added MPI2_SATA_PT_SGE_UNION for use in the SATA ++ * Passthrough Request message. ++ * 08-19-13 02.00.08 Made MPI2_SAS_OP_TRANSMIT_PORT_SELECT_SIGNAL obsolete ++ * for anything newer than MPI v2.0. ++ * 11-18-14 02.00.09 Updated copyright information. ++ * 03-16-15 02.00.10 Updated for MPI v2.6. ++ * Added MPI2_SATA_PT_REQ_PT_FLAGS_FPDMA. ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_SAS_H ++#define MPI2_SAS_H ++ ++/* ++ *Values for SASStatus. ++ */ ++#define MPI2_SASSTATUS_SUCCESS (0x00) ++#define MPI2_SASSTATUS_UNKNOWN_ERROR (0x01) ++#define MPI2_SASSTATUS_INVALID_FRAME (0x02) ++#define MPI2_SASSTATUS_UTC_BAD_DEST (0x03) ++#define MPI2_SASSTATUS_UTC_BREAK_RECEIVED (0x04) ++#define MPI2_SASSTATUS_UTC_CONNECT_RATE_NOT_SUPPORTED (0x05) ++#define MPI2_SASSTATUS_UTC_PORT_LAYER_REQUEST (0x06) ++#define MPI2_SASSTATUS_UTC_PROTOCOL_NOT_SUPPORTED (0x07) ++#define MPI2_SASSTATUS_UTC_STP_RESOURCES_BUSY (0x08) ++#define MPI2_SASSTATUS_UTC_WRONG_DESTINATION (0x09) ++#define MPI2_SASSTATUS_SHORT_INFORMATION_UNIT (0x0A) ++#define MPI2_SASSTATUS_LONG_INFORMATION_UNIT (0x0B) ++#define MPI2_SASSTATUS_XFER_RDY_INCORRECT_WRITE_DATA (0x0C) ++#define MPI2_SASSTATUS_XFER_RDY_REQUEST_OFFSET_ERROR (0x0D) ++#define MPI2_SASSTATUS_XFER_RDY_NOT_EXPECTED (0x0E) ++#define MPI2_SASSTATUS_DATA_INCORRECT_DATA_LENGTH (0x0F) ++#define MPI2_SASSTATUS_DATA_TOO_MUCH_READ_DATA (0x10) ++#define MPI2_SASSTATUS_DATA_OFFSET_ERROR (0x11) ++#define MPI2_SASSTATUS_SDSF_NAK_RECEIVED (0x12) ++#define MPI2_SASSTATUS_SDSF_CONNECTION_FAILED (0x13) ++#define MPI2_SASSTATUS_INITIATOR_RESPONSE_TIMEOUT (0x14) ++ ++/* ++ *Values for the SAS DeviceInfo field used in SAS Device Status Change Event ++ *data and SAS Configuration pages. ++ */ ++#define MPI2_SAS_DEVICE_INFO_SEP (0x00004000) ++#define MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE (0x00002000) ++#define MPI2_SAS_DEVICE_INFO_LSI_DEVICE (0x00001000) ++#define MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH (0x00000800) ++#define MPI2_SAS_DEVICE_INFO_SSP_TARGET (0x00000400) ++#define MPI2_SAS_DEVICE_INFO_STP_TARGET (0x00000200) ++#define MPI2_SAS_DEVICE_INFO_SMP_TARGET (0x00000100) ++#define MPI2_SAS_DEVICE_INFO_SATA_DEVICE (0x00000080) ++#define MPI2_SAS_DEVICE_INFO_SSP_INITIATOR (0x00000040) ++#define MPI2_SAS_DEVICE_INFO_STP_INITIATOR (0x00000020) ++#define MPI2_SAS_DEVICE_INFO_SMP_INITIATOR (0x00000010) ++#define MPI2_SAS_DEVICE_INFO_SATA_HOST (0x00000008) ++ ++#define MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE (0x00000007) ++#define MPI2_SAS_DEVICE_INFO_NO_DEVICE (0x00000000) ++#define MPI2_SAS_DEVICE_INFO_END_DEVICE (0x00000001) ++#define MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER (0x00000002) ++#define MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER (0x00000003) ++ ++/***************************************************************************** ++* ++* SAS Messages ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* SMP Passthrough messages ++****************************************************************************/ ++ ++/*SMP Passthrough Request Message */ ++typedef struct _MPI2_SMP_PASSTHROUGH_REQUEST { ++ U8 PassthroughFlags; /*0x00 */ ++ U8 PhysicalPort; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 RequestDataLength; /*0x04 */ ++ U8 SGLFlags; /*0x06*//*MPI v2.0 only. Reserved on MPI v2.5*/ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U32 Reserved2; /*0x0C */ ++ U64 SASAddress; /*0x10 */ ++ U32 Reserved3; /*0x18 */ ++ U32 Reserved4; /*0x1C */ ++ MPI2_SIMPLE_SGE_UNION SGL;/*0x20 */ ++} MPI2_SMP_PASSTHROUGH_REQUEST, *PTR_MPI2_SMP_PASSTHROUGH_REQUEST, ++ Mpi2SmpPassthroughRequest_t, *pMpi2SmpPassthroughRequest_t; ++ ++/*values for PassthroughFlags field */ ++#define MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE (0x80) ++ ++/*MPI v2.0: use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ ++ ++/*SMP Passthrough Reply Message */ ++typedef struct _MPI2_SMP_PASSTHROUGH_REPLY { ++ U8 PassthroughFlags; /*0x00 */ ++ U8 PhysicalPort; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 ResponseDataLength; /*0x04 */ ++ U8 SGLFlags; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U8 Reserved2; /*0x0C */ ++ U8 SASStatus; /*0x0D */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U32 Reserved3; /*0x14 */ ++ U8 ResponseData[4]; /*0x18 */ ++} MPI2_SMP_PASSTHROUGH_REPLY, *PTR_MPI2_SMP_PASSTHROUGH_REPLY, ++ Mpi2SmpPassthroughReply_t, *pMpi2SmpPassthroughReply_t; ++ ++/*values for PassthroughFlags field */ ++#define MPI2_SMP_PT_REPLY_PT_FLAGS_IMMEDIATE (0x80) ++ ++/*values for SASStatus field are at the top of this file */ ++ ++/**************************************************************************** ++* SATA Passthrough messages ++****************************************************************************/ ++ ++typedef union _MPI2_SATA_PT_SGE_UNION { ++ MPI2_SGE_SIMPLE_UNION MpiSimple; /*MPI v2.0 only */ ++ MPI2_SGE_CHAIN_UNION MpiChain; /*MPI v2.0 only */ ++ MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple; ++ MPI2_IEEE_SGE_CHAIN_UNION IeeeChain; /*MPI v2.0 only */ ++ MPI25_IEEE_SGE_CHAIN64 IeeeChain64; /*MPI v2.5 only */ ++} MPI2_SATA_PT_SGE_UNION, *PTR_MPI2_SATA_PT_SGE_UNION, ++ Mpi2SataPTSGEUnion_t, *pMpi2SataPTSGEUnion_t; ++ ++/*SATA Passthrough Request Message */ ++typedef struct _MPI2_SATA_PASSTHROUGH_REQUEST { ++ U16 DevHandle; /*0x00 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 PassthroughFlags; /*0x04 */ ++ U8 SGLFlags; /*0x06*//*MPI v2.0 only. Reserved on MPI v2.5*/ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U32 Reserved2; /*0x0C */ ++ U32 Reserved3; /*0x10 */ ++ U32 Reserved4; /*0x14 */ ++ U32 DataLength; /*0x18 */ ++ U8 CommandFIS[20]; /*0x1C */ ++ MPI2_SATA_PT_SGE_UNION SGL;/*0x30*//*MPI v2.5: IEEE 64 elements only*/ ++} MPI2_SATA_PASSTHROUGH_REQUEST, *PTR_MPI2_SATA_PASSTHROUGH_REQUEST, ++ Mpi2SataPassthroughRequest_t, ++ *pMpi2SataPassthroughRequest_t; ++ ++/*values for PassthroughFlags field */ ++#define MPI2_SATA_PT_REQ_PT_FLAGS_EXECUTE_DIAG (0x0100) ++#define MPI2_SATA_PT_REQ_PT_FLAGS_FPDMA (0x0040) ++#define MPI2_SATA_PT_REQ_PT_FLAGS_DMA (0x0020) ++#define MPI2_SATA_PT_REQ_PT_FLAGS_PIO (0x0010) ++#define MPI2_SATA_PT_REQ_PT_FLAGS_UNSPECIFIED_VU (0x0004) ++#define MPI2_SATA_PT_REQ_PT_FLAGS_WRITE (0x0002) ++#define MPI2_SATA_PT_REQ_PT_FLAGS_READ (0x0001) ++ ++/*MPI v2.0: use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ ++ ++/*SATA Passthrough Reply Message */ ++typedef struct _MPI2_SATA_PASSTHROUGH_REPLY { ++ U16 DevHandle; /*0x00 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 PassthroughFlags; /*0x04 */ ++ U8 SGLFlags; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved1; /*0x0A */ ++ U8 Reserved2; /*0x0C */ ++ U8 SASStatus; /*0x0D */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U8 StatusFIS[20]; /*0x14 */ ++ U32 StatusControlRegisters; /*0x28 */ ++ U32 TransferCount; /*0x2C */ ++} MPI2_SATA_PASSTHROUGH_REPLY, *PTR_MPI2_SATA_PASSTHROUGH_REPLY, ++ Mpi2SataPassthroughReply_t, *pMpi2SataPassthroughReply_t; ++ ++/*values for SASStatus field are at the top of this file */ ++ ++/**************************************************************************** ++* SAS IO Unit Control messages ++* (MPI v2.5 and earlier only. ++* Replaced by IO Unit Control messages in MPI v2.6 and later.) ++****************************************************************************/ ++ ++/*SAS IO Unit Control Request Message */ ++typedef struct _MPI2_SAS_IOUNIT_CONTROL_REQUEST { ++ U8 Operation; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 DevHandle; /*0x04 */ ++ U8 IOCParameter; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U16 Reserved4; /*0x0C */ ++ U8 PhyNum; /*0x0E */ ++ U8 PrimFlags; /*0x0F */ ++ U32 Primitive; /*0x10 */ ++ U8 LookupMethod; /*0x14 */ ++ U8 Reserved5; /*0x15 */ ++ U16 SlotNumber; /*0x16 */ ++ U64 LookupAddress; /*0x18 */ ++ U32 IOCParameterValue; /*0x20 */ ++ U32 Reserved7; /*0x24 */ ++ U32 Reserved8; /*0x28 */ ++} MPI2_SAS_IOUNIT_CONTROL_REQUEST, ++ *PTR_MPI2_SAS_IOUNIT_CONTROL_REQUEST, ++ Mpi2SasIoUnitControlRequest_t, ++ *pMpi2SasIoUnitControlRequest_t; ++ ++/*values for the Operation field */ ++#define MPI2_SAS_OP_CLEAR_ALL_PERSISTENT (0x02) ++#define MPI2_SAS_OP_PHY_LINK_RESET (0x06) ++#define MPI2_SAS_OP_PHY_HARD_RESET (0x07) ++#define MPI2_SAS_OP_PHY_CLEAR_ERROR_LOG (0x08) ++#define MPI2_SAS_OP_SEND_PRIMITIVE (0x0A) ++#define MPI2_SAS_OP_FORCE_FULL_DISCOVERY (0x0B) ++#define MPI2_SAS_OP_TRANSMIT_PORT_SELECT_SIGNAL (0x0C) /* MPI v2.0 only */ ++#define MPI2_SAS_OP_REMOVE_DEVICE (0x0D) ++#define MPI2_SAS_OP_LOOKUP_MAPPING (0x0E) ++#define MPI2_SAS_OP_SET_IOC_PARAMETER (0x0F) ++#define MPI25_SAS_OP_ENABLE_FP_DEVICE (0x10) ++#define MPI25_SAS_OP_DISABLE_FP_DEVICE (0x11) ++#define MPI25_SAS_OP_ENABLE_FP_ALL (0x12) ++#define MPI25_SAS_OP_DISABLE_FP_ALL (0x13) ++#define MPI2_SAS_OP_DEV_ENABLE_NCQ (0x14) ++#define MPI2_SAS_OP_DEV_DISABLE_NCQ (0x15) ++#define MPI2_SAS_OP_PRODUCT_SPECIFIC_MIN (0x80) ++ ++/*values for the PrimFlags field */ ++#define MPI2_SAS_PRIMFLAGS_SINGLE (0x08) ++#define MPI2_SAS_PRIMFLAGS_TRIPLE (0x02) ++#define MPI2_SAS_PRIMFLAGS_REDUNDANT (0x01) ++ ++/*values for the LookupMethod field */ ++#define MPI2_SAS_LOOKUP_METHOD_SAS_ADDRESS (0x01) ++#define MPI2_SAS_LOOKUP_METHOD_SAS_ENCLOSURE_SLOT (0x02) ++#define MPI2_SAS_LOOKUP_METHOD_SAS_DEVICE_NAME (0x03) ++ ++/*SAS IO Unit Control Reply Message */ ++typedef struct _MPI2_SAS_IOUNIT_CONTROL_REPLY { ++ U8 Operation; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 DevHandle; /*0x04 */ ++ U8 IOCParameter; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved3; /*0x0A */ ++ U16 Reserved4; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_SAS_IOUNIT_CONTROL_REPLY, ++ *PTR_MPI2_SAS_IOUNIT_CONTROL_REPLY, ++ Mpi2SasIoUnitControlReply_t, *pMpi2SasIoUnitControlReply_t; ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h +new file mode 100644 +index 0000000..5f9289a +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h +@@ -0,0 +1,483 @@ ++/* ++ * Copyright 2000-2014 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2_tool.h ++ * Title: MPI diagnostic tool structures and definitions ++ * Creation Date: March 26, 2007 ++ * ++ * mpi2_tool.h Version: 02.00.13 ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 12-18-07 02.00.01 Added Diagnostic Buffer Post and Diagnostic Release ++ * structures and defines. ++ * 02-29-08 02.00.02 Modified various names to make them 32-character unique. ++ * 05-06-09 02.00.03 Added ISTWI Read Write Tool and Diagnostic CLI Tool. ++ * 07-30-09 02.00.04 Added ExtendedType field to DiagnosticBufferPost request ++ * and reply messages. ++ * Added MPI2_DIAG_BUF_TYPE_EXTENDED. ++ * Incremented MPI2_DIAG_BUF_TYPE_COUNT. ++ * 05-12-10 02.00.05 Added Diagnostic Data Upload tool. ++ * 08-11-10 02.00.06 Added defines that were missing for Diagnostic Buffer ++ * Post Request. ++ * 05-25-11 02.00.07 Added Flags field and related defines to ++ * MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST. ++ * 11-18-11 02.00.08 Incorporating additions for MPI v2.5. ++ * 07-10-12 02.00.09 Add MPI v2.5 Toolbox Diagnostic CLI Tool Request ++ * message. ++ * 07-26-12 02.00.10 Modified MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST so that ++ * it uses MPI Chain SGE as well as MPI Simple SGE. ++ * 08-19-13 02.00.11 Added MPI2_TOOLBOX_TEXT_DISPLAY_TOOL and related info. ++ * 01-08-14 02.00.12 Added MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC. ++ * 11-18-14 02.00.13 Updated copyright information. ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_TOOL_H ++#define MPI2_TOOL_H ++ ++/***************************************************************************** ++* ++* Toolbox Messages ++* ++*****************************************************************************/ ++ ++/*defines for the Tools */ ++#define MPI2_TOOLBOX_CLEAN_TOOL (0x00) ++#define MPI2_TOOLBOX_MEMORY_MOVE_TOOL (0x01) ++#define MPI2_TOOLBOX_DIAG_DATA_UPLOAD_TOOL (0x02) ++#define MPI2_TOOLBOX_ISTWI_READ_WRITE_TOOL (0x03) ++#define MPI2_TOOLBOX_BEACON_TOOL (0x05) ++#define MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL (0x06) ++#define MPI2_TOOLBOX_TEXT_DISPLAY_TOOL (0x07) ++ ++/**************************************************************************** ++* Toolbox reply ++****************************************************************************/ ++ ++typedef struct _MPI2_TOOLBOX_REPLY { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_TOOLBOX_REPLY, *PTR_MPI2_TOOLBOX_REPLY, ++ Mpi2ToolboxReply_t, *pMpi2ToolboxReply_t; ++ ++/**************************************************************************** ++* Toolbox Clean Tool request ++****************************************************************************/ ++ ++typedef struct _MPI2_TOOLBOX_CLEAN_REQUEST { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 Flags; /*0x0C */ ++} MPI2_TOOLBOX_CLEAN_REQUEST, *PTR_MPI2_TOOLBOX_CLEAN_REQUEST, ++ Mpi2ToolboxCleanRequest_t, *pMpi2ToolboxCleanRequest_t; ++ ++/*values for the Flags field */ ++#define MPI2_TOOLBOX_CLEAN_BOOT_SERVICES (0x80000000) ++#define MPI2_TOOLBOX_CLEAN_PERSIST_MANUFACT_PAGES (0x40000000) ++#define MPI2_TOOLBOX_CLEAN_OTHER_PERSIST_PAGES (0x20000000) ++#define MPI2_TOOLBOX_CLEAN_FW_CURRENT (0x10000000) ++#define MPI2_TOOLBOX_CLEAN_FW_BACKUP (0x08000000) ++#define MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC (0x04000000) ++#define MPI2_TOOLBOX_CLEAN_MEGARAID (0x02000000) ++#define MPI2_TOOLBOX_CLEAN_INITIALIZATION (0x01000000) ++#define MPI2_TOOLBOX_CLEAN_FLASH (0x00000004) ++#define MPI2_TOOLBOX_CLEAN_SEEPROM (0x00000002) ++#define MPI2_TOOLBOX_CLEAN_NVSRAM (0x00000001) ++ ++/**************************************************************************** ++* Toolbox Memory Move request ++****************************************************************************/ ++ ++typedef struct _MPI2_TOOLBOX_MEM_MOVE_REQUEST { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ MPI2_SGE_SIMPLE_UNION SGL; /*0x0C */ ++} MPI2_TOOLBOX_MEM_MOVE_REQUEST, *PTR_MPI2_TOOLBOX_MEM_MOVE_REQUEST, ++ Mpi2ToolboxMemMoveRequest_t, *pMpi2ToolboxMemMoveRequest_t; ++ ++/**************************************************************************** ++* Toolbox Diagnostic Data Upload request ++****************************************************************************/ ++ ++typedef struct _MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U8 SGLFlags; /*0x0C */ ++ U8 Reserved5; /*0x0D */ ++ U16 Reserved6; /*0x0E */ ++ U32 Flags; /*0x10 */ ++ U32 DataLength; /*0x14 */ ++ MPI2_SGE_SIMPLE_UNION SGL; /*0x18 */ ++} MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST, ++ *PTR_MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST, ++ Mpi2ToolboxDiagDataUploadRequest_t, ++ *pMpi2ToolboxDiagDataUploadRequest_t; ++ ++/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ ++ ++typedef struct _MPI2_DIAG_DATA_UPLOAD_HEADER { ++ U32 DiagDataLength; /*00h */ ++ U8 FormatCode; /*04h */ ++ U8 Reserved1; /*05h */ ++ U16 Reserved2; /*06h */ ++} MPI2_DIAG_DATA_UPLOAD_HEADER, *PTR_MPI2_DIAG_DATA_UPLOAD_HEADER, ++ Mpi2DiagDataUploadHeader_t, *pMpi2DiagDataUploadHeader_t; ++ ++/**************************************************************************** ++* Toolbox ISTWI Read Write Tool ++****************************************************************************/ ++ ++/*Toolbox ISTWI Read Write Tool request message */ ++typedef struct _MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 Reserved5; /*0x0C */ ++ U32 Reserved6; /*0x10 */ ++ U8 DevIndex; /*0x14 */ ++ U8 Action; /*0x15 */ ++ U8 SGLFlags; /*0x16 */ ++ U8 Flags; /*0x17 */ ++ U16 TxDataLength; /*0x18 */ ++ U16 RxDataLength; /*0x1A */ ++ U32 Reserved8; /*0x1C */ ++ U32 Reserved9; /*0x20 */ ++ U32 Reserved10; /*0x24 */ ++ U32 Reserved11; /*0x28 */ ++ U32 Reserved12; /*0x2C */ ++ MPI2_SGE_SIMPLE_UNION SGL; /*0x30 */ ++} MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST, ++ *PTR_MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST, ++ Mpi2ToolboxIstwiReadWriteRequest_t, ++ *pMpi2ToolboxIstwiReadWriteRequest_t; ++ ++/*values for the Action field */ ++#define MPI2_TOOL_ISTWI_ACTION_READ_DATA (0x01) ++#define MPI2_TOOL_ISTWI_ACTION_WRITE_DATA (0x02) ++#define MPI2_TOOL_ISTWI_ACTION_SEQUENCE (0x03) ++#define MPI2_TOOL_ISTWI_ACTION_RESERVE_BUS (0x10) ++#define MPI2_TOOL_ISTWI_ACTION_RELEASE_BUS (0x11) ++#define MPI2_TOOL_ISTWI_ACTION_RESET (0x12) ++ ++/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ ++ ++/*values for the Flags field */ ++#define MPI2_TOOL_ISTWI_FLAG_AUTO_RESERVE_RELEASE (0x80) ++#define MPI2_TOOL_ISTWI_FLAG_PAGE_ADDR_MASK (0x07) ++ ++/*Toolbox ISTWI Read Write Tool reply message */ ++typedef struct _MPI2_TOOLBOX_ISTWI_REPLY { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U8 DevIndex; /*0x14 */ ++ U8 Action; /*0x15 */ ++ U8 IstwiStatus; /*0x16 */ ++ U8 Reserved6; /*0x17 */ ++ U16 TxDataCount; /*0x18 */ ++ U16 RxDataCount; /*0x1A */ ++} MPI2_TOOLBOX_ISTWI_REPLY, *PTR_MPI2_TOOLBOX_ISTWI_REPLY, ++ Mpi2ToolboxIstwiReply_t, *pMpi2ToolboxIstwiReply_t; ++ ++/**************************************************************************** ++* Toolbox Beacon Tool request ++****************************************************************************/ ++ ++typedef struct _MPI2_TOOLBOX_BEACON_REQUEST { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U8 Reserved5; /*0x0C */ ++ U8 PhysicalPort; /*0x0D */ ++ U8 Reserved6; /*0x0E */ ++ U8 Flags; /*0x0F */ ++} MPI2_TOOLBOX_BEACON_REQUEST, *PTR_MPI2_TOOLBOX_BEACON_REQUEST, ++ Mpi2ToolboxBeaconRequest_t, *pMpi2ToolboxBeaconRequest_t; ++ ++/*values for the Flags field */ ++#define MPI2_TOOLBOX_FLAGS_BEACONMODE_OFF (0x00) ++#define MPI2_TOOLBOX_FLAGS_BEACONMODE_ON (0x01) ++ ++/**************************************************************************** ++* Toolbox Diagnostic CLI Tool ++****************************************************************************/ ++ ++#define MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH (0x5C) ++ ++/*MPI v2.0 Toolbox Diagnostic CLI Tool request message */ ++typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U8 SGLFlags; /*0x0C */ ++ U8 Reserved5; /*0x0D */ ++ U16 Reserved6; /*0x0E */ ++ U32 DataLength; /*0x10 */ ++ U8 DiagnosticCliCommand[MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH];/*0x14 */ ++ MPI2_MPI_SGE_IO_UNION SGL; /*0x70 */ ++} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, ++ *PTR_MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, ++ Mpi2ToolboxDiagnosticCliRequest_t, ++ *pMpi2ToolboxDiagnosticCliRequest_t; ++ ++/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ ++ ++/*MPI v2.5 Toolbox Diagnostic CLI Tool request message */ ++typedef struct _MPI25_TOOLBOX_DIAGNOSTIC_CLI_REQUEST { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U32 Reserved5; /*0x0C */ ++ U32 DataLength; /*0x10 */ ++ U8 DiagnosticCliCommand[MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH];/*0x14 */ ++ MPI25_SGE_IO_UNION SGL; /* 0x70 */ ++} MPI25_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, ++ *PTR_MPI25_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, ++ Mpi25ToolboxDiagnosticCliRequest_t, ++ *pMpi25ToolboxDiagnosticCliRequest_t; ++ ++/*Toolbox Diagnostic CLI Tool reply message */ ++typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY { ++ U8 Tool; /*0x00 */ ++ U8 Reserved1; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U32 ReturnedDataLength; /*0x14 */ ++} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY, ++ *PTR_MPI2_TOOLBOX_DIAG_CLI_REPLY, ++ Mpi2ToolboxDiagnosticCliReply_t, ++ *pMpi2ToolboxDiagnosticCliReply_t; ++ ++ ++/**************************************************************************** ++* Toolbox Console Text Display Tool ++****************************************************************************/ ++ ++/* Toolbox Console Text Display Tool request message */ ++typedef struct _MPI2_TOOLBOX_TEXT_DISPLAY_REQUEST { ++ U8 Tool; /* 0x00 */ ++ U8 Reserved1; /* 0x01 */ ++ U8 ChainOffset; /* 0x02 */ ++ U8 Function; /* 0x03 */ ++ U16 Reserved2; /* 0x04 */ ++ U8 Reserved3; /* 0x06 */ ++ U8 MsgFlags; /* 0x07 */ ++ U8 VP_ID; /* 0x08 */ ++ U8 VF_ID; /* 0x09 */ ++ U16 Reserved4; /* 0x0A */ ++ U8 Console; /* 0x0C */ ++ U8 Flags; /* 0x0D */ ++ U16 Reserved6; /* 0x0E */ ++ U8 TextToDisplay[4]; /* 0x10 */ ++} MPI2_TOOLBOX_TEXT_DISPLAY_REQUEST, ++*PTR_MPI2_TOOLBOX_TEXT_DISPLAY_REQUEST, ++Mpi2ToolboxTextDisplayRequest_t, ++*pMpi2ToolboxTextDisplayRequest_t; ++ ++/* defines for the Console field */ ++#define MPI2_TOOLBOX_CONSOLE_TYPE_MASK (0xF0) ++#define MPI2_TOOLBOX_CONSOLE_TYPE_DEFAULT (0x00) ++#define MPI2_TOOLBOX_CONSOLE_TYPE_UART (0x10) ++#define MPI2_TOOLBOX_CONSOLE_TYPE_ETHERNET (0x20) ++ ++#define MPI2_TOOLBOX_CONSOLE_NUMBER_MASK (0x0F) ++ ++/* defines for the Flags field */ ++#define MPI2_TOOLBOX_CONSOLE_FLAG_TIMESTAMP (0x01) ++ ++ ++ ++/***************************************************************************** ++* ++* Diagnostic Buffer Messages ++* ++*****************************************************************************/ ++ ++/**************************************************************************** ++* Diagnostic Buffer Post request ++****************************************************************************/ ++ ++typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST { ++ U8 ExtendedType; /*0x00 */ ++ U8 BufferType; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U64 BufferAddress; /*0x0C */ ++ U32 BufferLength; /*0x14 */ ++ U32 Reserved5; /*0x18 */ ++ U32 Reserved6; /*0x1C */ ++ U32 Flags; /*0x20 */ ++ U32 ProductSpecific[23]; /*0x24 */ ++} MPI2_DIAG_BUFFER_POST_REQUEST, *PTR_MPI2_DIAG_BUFFER_POST_REQUEST, ++ Mpi2DiagBufferPostRequest_t, *pMpi2DiagBufferPostRequest_t; ++ ++/*values for the ExtendedType field */ ++#define MPI2_DIAG_EXTENDED_TYPE_UTILIZATION (0x02) ++ ++/*values for the BufferType field */ ++#define MPI2_DIAG_BUF_TYPE_TRACE (0x00) ++#define MPI2_DIAG_BUF_TYPE_SNAPSHOT (0x01) ++#define MPI2_DIAG_BUF_TYPE_EXTENDED (0x02) ++/*count of the number of buffer types */ ++#define MPI2_DIAG_BUF_TYPE_COUNT (0x03) ++ ++/*values for the Flags field */ ++#define MPI2_DIAG_BUF_FLAG_RELEASE_ON_FULL (0x00000002) ++#define MPI2_DIAG_BUF_FLAG_IMMEDIATE_RELEASE (0x00000001) ++ ++/**************************************************************************** ++* Diagnostic Buffer Post reply ++****************************************************************************/ ++ ++typedef struct _MPI2_DIAG_BUFFER_POST_REPLY { ++ U8 ExtendedType; /*0x00 */ ++ U8 BufferType; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++ U32 TransferLength; /*0x14 */ ++} MPI2_DIAG_BUFFER_POST_REPLY, *PTR_MPI2_DIAG_BUFFER_POST_REPLY, ++ Mpi2DiagBufferPostReply_t, *pMpi2DiagBufferPostReply_t; ++ ++/**************************************************************************** ++* Diagnostic Release request ++****************************************************************************/ ++ ++typedef struct _MPI2_DIAG_RELEASE_REQUEST { ++ U8 Reserved1; /*0x00 */ ++ U8 BufferType; /*0x01 */ ++ U8 ChainOffset; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++} MPI2_DIAG_RELEASE_REQUEST, *PTR_MPI2_DIAG_RELEASE_REQUEST, ++ Mpi2DiagReleaseRequest_t, *pMpi2DiagReleaseRequest_t; ++ ++/**************************************************************************** ++* Diagnostic Buffer Post reply ++****************************************************************************/ ++ ++typedef struct _MPI2_DIAG_RELEASE_REPLY { ++ U8 Reserved1; /*0x00 */ ++ U8 BufferType; /*0x01 */ ++ U8 MsgLength; /*0x02 */ ++ U8 Function; /*0x03 */ ++ U16 Reserved2; /*0x04 */ ++ U8 Reserved3; /*0x06 */ ++ U8 MsgFlags; /*0x07 */ ++ U8 VP_ID; /*0x08 */ ++ U8 VF_ID; /*0x09 */ ++ U16 Reserved4; /*0x0A */ ++ U16 Reserved5; /*0x0C */ ++ U16 IOCStatus; /*0x0E */ ++ U32 IOCLogInfo; /*0x10 */ ++} MPI2_DIAG_RELEASE_REPLY, *PTR_MPI2_DIAG_RELEASE_REPLY, ++ Mpi2DiagReleaseReply_t, *pMpi2DiagReleaseReply_t; ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_type.h b/drivers/scsi/mpt2sas/mpi/mpi2_type.h +new file mode 100644 +index 0000000..92a81ab +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpi/mpi2_type.h +@@ -0,0 +1,57 @@ ++/* ++ * Copyright 2000-2014 Avago Technologies. All rights reserved. ++ * ++ * ++ * Name: mpi2_type.h ++ * Title: MPI basic type definitions ++ * Creation Date: August 16, 2006 ++ * ++ * mpi2_type.h Version: 02.00.01 ++ * ++ * Version History ++ * --------------- ++ * ++ * Date Version Description ++ * -------- -------- ------------------------------------------------------ ++ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. ++ * 11-18-14 02.00.01 Updated copyright information. ++ * -------------------------------------------------------------------------- ++ */ ++ ++#ifndef MPI2_TYPE_H ++#define MPI2_TYPE_H ++ ++/******************************************************************************* ++ * Define * if it hasn't already been defined. By default ++ * * is defined to be a near pointer. MPI2_POINTER can be defined as ++ * a far pointer by defining * as "far *" before this header file is ++ * included. ++ */ ++ ++/* the basic types may have already been included by mpi_type.h */ ++#ifndef MPI_TYPE_H ++/***************************************************************************** ++* ++* Basic Types ++* ++*****************************************************************************/ ++ ++typedef u8 U8; ++typedef __le16 U16; ++typedef __le32 U32; ++typedef __le64 U64 __attribute__ ((aligned(4))); ++ ++/***************************************************************************** ++* ++* Pointer Types ++* ++*****************************************************************************/ ++ ++typedef U8 *PU8; ++typedef U16 *PU16; ++typedef U32 *PU32; ++typedef U64 *PU64; ++ ++#endif ++ ++#endif +diff --git a/drivers/scsi/mpt2sas/mpt3sas_base.c b/drivers/scsi/mpt2sas/mpt3sas_base.c +new file mode 100644 +index 0000000..224bf9d +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_base.c +@@ -0,0 +1,5713 @@ ++/* ++ * This is the Fusion MPT base driver providing common API layer interface ++ * for access to MPT (Message Passing Technology) firmware. ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.c ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#include "mpt3sas_base.h" ++ ++static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS]; ++ ++ ++#define FAULT_POLLING_INTERVAL 1000 /* in milliseconds */ ++ ++ /* maximum controller queue depth */ ++#define MAX_HBA_QUEUE_DEPTH 30000 ++#define MAX_CHAIN_DEPTH 100000 ++static int max_queue_depth = -1; ++module_param(max_queue_depth, int, 0); ++MODULE_PARM_DESC(max_queue_depth, " max controller queue depth "); ++ ++static int max_sgl_entries = -1; ++module_param(max_sgl_entries, int, 0); ++MODULE_PARM_DESC(max_sgl_entries, " max sg entries "); ++ ++static int msix_disable = -1; ++module_param(msix_disable, int, 0); ++MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)"); ++ ++static int smp_affinity_enable = 1; ++module_param(smp_affinity_enable, int, S_IRUGO); ++MODULE_PARM_DESC(smp_affinity_enable, "SMP affinity feature enable/disbale Default: enable(1)"); ++ ++static int max_msix_vectors = -1; ++module_param(max_msix_vectors, int, 0); ++MODULE_PARM_DESC(max_msix_vectors, ++ " max msix vectors"); ++ ++static int mpt2sas_fwfault_debug; ++MODULE_PARM_DESC(mpt2sas_fwfault_debug, ++ " enable detection of firmware fault and halt firmware - (default=0)"); ++ ++static int ++_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag); ++ ++/** ++ * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug. ++ * ++ */ ++static int ++_scsih_set_fwfault_debug(const char *val, struct kernel_param *kp) ++{ ++ int ret = param_set_int(val, kp); ++ struct MPT3SAS_ADAPTER *ioc; ++ ++ if (ret) ++ return ret; ++ ++ /* global ioc spinlock to protect controller list on list operations */ ++ pr_info("setting fwfault_debug(%d)\n", mpt2sas_fwfault_debug); ++ spin_lock(&gioc_lock_mpt2sas); ++ list_for_each_entry(ioc, &mpt2sas_ioc_list, list) ++ ioc->fwfault_debug = mpt2sas_fwfault_debug; ++ spin_unlock(&gioc_lock_mpt2sas); ++ return 0; ++} ++module_param_call(mpt2sas_fwfault_debug, _scsih_set_fwfault_debug, ++ param_get_int, &mpt2sas_fwfault_debug, 0644); ++ ++/** ++ * mpt2sas_remove_dead_ioc_func - kthread context to remove dead ioc ++ * @arg: input argument, used to derive ioc ++ * ++ * Return 0 if controller is removed from pci subsystem. ++ * Return -1 for other case. ++ */ ++static int mpt2sas_remove_dead_ioc_func(void *arg) ++{ ++ struct MPT3SAS_ADAPTER *ioc = (struct MPT3SAS_ADAPTER *)arg; ++ struct pci_dev *pdev; ++ ++ if ((ioc == NULL)) ++ return -1; ++ ++ pdev = ioc->pdev; ++ if ((pdev == NULL)) ++ return -1; ++ pci_stop_and_remove_bus_device_locked(pdev); ++ return 0; ++} ++ ++/** ++ * _base_fault_reset_work - workq handling ioc fault conditions ++ * @work: input argument, used to derive ioc ++ * Context: sleep. ++ * ++ * Return nothing. ++ */ ++static void ++_base_fault_reset_work(struct work_struct *work) ++{ ++ struct MPT3SAS_ADAPTER *ioc = ++ container_of(work, struct MPT3SAS_ADAPTER, fault_reset_work.work); ++ unsigned long flags; ++ u32 doorbell; ++ int rc; ++ struct task_struct *p; ++ ++ ++ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ++ if (ioc->shost_recovery || ioc->pci_error_recovery) ++ goto rearm_timer; ++ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); ++ ++ doorbell = mpt2sas_base_get_iocstate(ioc, 0); ++ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_MASK) { ++ pr_err(MPT3SAS_FMT "SAS host is non-operational !!!!\n", ++ ioc->name); ++ ++ /* It may be possible that EEH recovery can resolve some of ++ * pci bus failure issues rather removing the dead ioc function ++ * by considering controller is in a non-operational state. So ++ * here priority is given to the EEH recovery. If it doesn't ++ * not resolve this issue, mpt3sas driver will consider this ++ * controller to non-operational state and remove the dead ioc ++ * function. ++ */ ++ if (ioc->non_operational_loop++ < 5) { ++ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, ++ flags); ++ goto rearm_timer; ++ } ++ ++ /* ++ * Call _scsih_flush_pending_cmds callback so that we flush all ++ * pending commands back to OS. This call is required to aovid ++ * deadlock at block layer. Dead IOC will fail to do diag reset, ++ * and this call is safe since dead ioc will never return any ++ * command back from HW. ++ */ ++ ioc->schedule_dead_ioc_flush_running_cmds(ioc); ++ /* ++ * Set remove_host flag early since kernel thread will ++ * take some time to execute. ++ */ ++ ioc->remove_host = 1; ++ /*Remove the Dead Host */ ++ p = kthread_run(mpt2sas_remove_dead_ioc_func, ioc, ++ "%s_dead_ioc_%d", ioc->driver_name, ioc->id); ++ if (IS_ERR(p)) ++ pr_err(MPT3SAS_FMT ++ "%s: Running mpt2sas_dead_ioc thread failed !!!!\n", ++ ioc->name, __func__); ++ else ++ pr_err(MPT3SAS_FMT ++ "%s: Running mpt2sas_dead_ioc thread success !!!!\n", ++ ioc->name, __func__); ++ return; /* don't rearm timer */ ++ } ++ ++ ioc->non_operational_loop = 0; ++ ++ if ((doorbell & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL) { ++ rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ pr_warn(MPT3SAS_FMT "%s: hard reset: %s\n", ioc->name, ++ __func__, (rc == 0) ? "success" : "failed"); ++ doorbell = mpt2sas_base_get_iocstate(ioc, 0); ++ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) ++ mpt2sas_base_fault_info(ioc, doorbell & ++ MPI2_DOORBELL_DATA_MASK); ++ if (rc && (doorbell & MPI2_IOC_STATE_MASK) != ++ MPI2_IOC_STATE_OPERATIONAL) ++ return; /* don't rearm timer */ ++ } ++ ++ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ++ rearm_timer: ++ if (ioc->fault_reset_work_q) ++ queue_delayed_work(ioc->fault_reset_work_q, ++ &ioc->fault_reset_work, ++ msecs_to_jiffies(FAULT_POLLING_INTERVAL)); ++ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); ++} ++ ++/** ++ * mpt2sas_base_start_watchdog - start the fault_reset_work_q ++ * @ioc: per adapter object ++ * Context: sleep. ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_start_watchdog(struct MPT3SAS_ADAPTER *ioc) ++{ ++ unsigned long flags; ++ ++ if (ioc->fault_reset_work_q) ++ return; ++ ++ /* initialize fault polling */ ++ ++ INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work); ++ snprintf(ioc->fault_reset_work_q_name, ++ sizeof(ioc->fault_reset_work_q_name), "poll_%s%d_status", ++ ioc->driver_name, ioc->id); ++ ioc->fault_reset_work_q = ++ create_singlethread_workqueue(ioc->fault_reset_work_q_name); ++ if (!ioc->fault_reset_work_q) { ++ pr_err(MPT3SAS_FMT "%s: failed (line=%d)\n", ++ ioc->name, __func__, __LINE__); ++ return; ++ } ++ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ++ if (ioc->fault_reset_work_q) ++ queue_delayed_work(ioc->fault_reset_work_q, ++ &ioc->fault_reset_work, ++ msecs_to_jiffies(FAULT_POLLING_INTERVAL)); ++ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); ++} ++ ++/** ++ * mpt2sas_base_stop_watchdog - stop the fault_reset_work_q ++ * @ioc: per adapter object ++ * Context: sleep. ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_stop_watchdog(struct MPT3SAS_ADAPTER *ioc) ++{ ++ unsigned long flags; ++ struct workqueue_struct *wq; ++ ++ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ++ wq = ioc->fault_reset_work_q; ++ ioc->fault_reset_work_q = NULL; ++ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); ++ if (wq) { ++ if (!cancel_delayed_work_sync(&ioc->fault_reset_work)) ++ flush_workqueue(wq); ++ destroy_workqueue(wq); ++ } ++} ++ ++/** ++ * mpt2sas_base_fault_info - verbose translation of firmware FAULT code ++ * @ioc: per adapter object ++ * @fault_code: fault code ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_fault_info(struct MPT3SAS_ADAPTER *ioc , u16 fault_code) ++{ ++ pr_err(MPT3SAS_FMT "fault_state(0x%04x)!\n", ++ ioc->name, fault_code); ++} ++ ++/** ++ * mpt2sas_halt_firmware - halt's mpt controller firmware ++ * @ioc: per adapter object ++ * ++ * For debugging timeout related issues. Writing 0xCOFFEE00 ++ * to the doorbell register will halt controller firmware. With ++ * the purpose to stop both driver and firmware, the enduser can ++ * obtain a ring buffer from controller UART. ++ */ ++void ++mpt2sas_halt_firmware(struct MPT3SAS_ADAPTER *ioc) ++{ ++ u32 doorbell; ++ ++ if (!ioc->fwfault_debug) ++ return; ++ ++ dump_stack(); ++ ++ doorbell = readl(&ioc->chip->Doorbell); ++ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) ++ mpt2sas_base_fault_info(ioc , doorbell); ++ else { ++ writel(0xC0FFEE00, &ioc->chip->Doorbell); ++ pr_err(MPT3SAS_FMT "Firmware is halted due to command timeout\n", ++ ioc->name); ++ } ++ ++ if (ioc->fwfault_debug == 2) ++ for (;;) ++ ; ++ else ++ panic("panic in %s\n", __func__); ++} ++ ++/** ++ * _base_sas_ioc_info - verbose translation of the ioc status ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @request_hdr: request mf ++ * ++ * Return nothing. ++ */ ++static void ++_base_sas_ioc_info(struct MPT3SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply, ++ MPI2RequestHeader_t *request_hdr) ++{ ++ u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ char *desc = NULL; ++ u16 frame_sz; ++ char *func_str = NULL; ++ ++ /* SCSI_IO, RAID_PASS are handled from _scsih_scsi_ioc_info */ ++ if (request_hdr->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || ++ request_hdr->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH || ++ request_hdr->Function == MPI2_FUNCTION_EVENT_NOTIFICATION) ++ return; ++ ++ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) ++ return; ++ ++ switch (ioc_status) { ++ ++/**************************************************************************** ++* Common IOCStatus values for all replies ++****************************************************************************/ ++ ++ case MPI2_IOCSTATUS_INVALID_FUNCTION: ++ desc = "invalid function"; ++ break; ++ case MPI2_IOCSTATUS_BUSY: ++ desc = "busy"; ++ break; ++ case MPI2_IOCSTATUS_INVALID_SGL: ++ desc = "invalid sgl"; ++ break; ++ case MPI2_IOCSTATUS_INTERNAL_ERROR: ++ desc = "internal error"; ++ break; ++ case MPI2_IOCSTATUS_INVALID_VPID: ++ desc = "invalid vpid"; ++ break; ++ case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES: ++ desc = "insufficient resources"; ++ break; ++ case MPI2_IOCSTATUS_INSUFFICIENT_POWER: ++ desc = "insufficient power"; ++ break; ++ case MPI2_IOCSTATUS_INVALID_FIELD: ++ desc = "invalid field"; ++ break; ++ case MPI2_IOCSTATUS_INVALID_STATE: ++ desc = "invalid state"; ++ break; ++ case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED: ++ desc = "op state not supported"; ++ break; ++ ++/**************************************************************************** ++* Config IOCStatus values ++****************************************************************************/ ++ ++ case MPI2_IOCSTATUS_CONFIG_INVALID_ACTION: ++ desc = "config invalid action"; ++ break; ++ case MPI2_IOCSTATUS_CONFIG_INVALID_TYPE: ++ desc = "config invalid type"; ++ break; ++ case MPI2_IOCSTATUS_CONFIG_INVALID_PAGE: ++ desc = "config invalid page"; ++ break; ++ case MPI2_IOCSTATUS_CONFIG_INVALID_DATA: ++ desc = "config invalid data"; ++ break; ++ case MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS: ++ desc = "config no defaults"; ++ break; ++ case MPI2_IOCSTATUS_CONFIG_CANT_COMMIT: ++ desc = "config cant commit"; ++ break; ++ ++/**************************************************************************** ++* SCSI IO Reply ++****************************************************************************/ ++ ++ case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: ++ case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE: ++ case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: ++ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: ++ case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: ++ case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: ++ case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: ++ case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: ++ case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: ++ case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: ++ case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: ++ case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: ++ break; ++ ++/**************************************************************************** ++* For use by SCSI Initiator and SCSI Target end-to-end data protection ++****************************************************************************/ ++ ++ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: ++ desc = "eedp guard error"; ++ break; ++ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR: ++ desc = "eedp ref tag error"; ++ break; ++ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR: ++ desc = "eedp app tag error"; ++ break; ++ ++/**************************************************************************** ++* SCSI Target values ++****************************************************************************/ ++ ++ case MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX: ++ desc = "target invalid io index"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_ABORTED: ++ desc = "target aborted"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE: ++ desc = "target no conn retryable"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_NO_CONNECTION: ++ desc = "target no connection"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH: ++ desc = "target xfer count mismatch"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR: ++ desc = "target data offset error"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA: ++ desc = "target too much write data"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT: ++ desc = "target iu too short"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT: ++ desc = "target ack nak timeout"; ++ break; ++ case MPI2_IOCSTATUS_TARGET_NAK_RECEIVED: ++ desc = "target nak received"; ++ break; ++ ++/**************************************************************************** ++* Serial Attached SCSI values ++****************************************************************************/ ++ ++ case MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED: ++ desc = "smp request failed"; ++ break; ++ case MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN: ++ desc = "smp data overrun"; ++ break; ++ ++/**************************************************************************** ++* Diagnostic Buffer Post / Diagnostic Release values ++****************************************************************************/ ++ ++ case MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED: ++ desc = "diagnostic released"; ++ break; ++ default: ++ break; ++ } ++ ++ if (!desc) ++ return; ++ ++ switch (request_hdr->Function) { ++ case MPI2_FUNCTION_CONFIG: ++ frame_sz = sizeof(Mpi2ConfigRequest_t) + ioc->sge_size; ++ func_str = "config_page"; ++ break; ++ case MPI2_FUNCTION_SCSI_TASK_MGMT: ++ frame_sz = sizeof(Mpi2SCSITaskManagementRequest_t); ++ func_str = "task_mgmt"; ++ break; ++ case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: ++ frame_sz = sizeof(Mpi2SasIoUnitControlRequest_t); ++ func_str = "sas_iounit_ctl"; ++ break; ++ case MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR: ++ frame_sz = sizeof(Mpi2SepRequest_t); ++ func_str = "enclosure"; ++ break; ++ case MPI2_FUNCTION_IOC_INIT: ++ frame_sz = sizeof(Mpi2IOCInitRequest_t); ++ func_str = "ioc_init"; ++ break; ++ case MPI2_FUNCTION_PORT_ENABLE: ++ frame_sz = sizeof(Mpi2PortEnableRequest_t); ++ func_str = "port_enable"; ++ break; ++ case MPI2_FUNCTION_SMP_PASSTHROUGH: ++ frame_sz = sizeof(Mpi2SmpPassthroughRequest_t) + ioc->sge_size; ++ func_str = "smp_passthru"; ++ break; ++ default: ++ frame_sz = 32; ++ func_str = "unknown"; ++ break; ++ } ++ ++ pr_warn(MPT3SAS_FMT "ioc_status: %s(0x%04x), request(0x%p),(%s)\n", ++ ioc->name, desc, ioc_status, request_hdr, func_str); ++ ++ _debug_dump_mf(request_hdr, frame_sz/4); ++} ++ ++/** ++ * _base_display_event_data - verbose translation of firmware asyn events ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * ++ * Return nothing. ++ */ ++static void ++_base_display_event_data(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventNotificationReply_t *mpi_reply) ++{ ++ char *desc = NULL; ++ u16 event; ++ ++ if (!(ioc->logging_level & MPT_DEBUG_EVENTS)) ++ return; ++ ++ event = le16_to_cpu(mpi_reply->Event); ++ ++ switch (event) { ++ case MPI2_EVENT_LOG_DATA: ++ desc = "Log Data"; ++ break; ++ case MPI2_EVENT_STATE_CHANGE: ++ desc = "Status Change"; ++ break; ++ case MPI2_EVENT_HARD_RESET_RECEIVED: ++ desc = "Hard Reset Received"; ++ break; ++ case MPI2_EVENT_EVENT_CHANGE: ++ desc = "Event Change"; ++ break; ++ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: ++ desc = "Device Status Change"; ++ break; ++ case MPI2_EVENT_IR_OPERATION_STATUS: ++ if (!ioc->hide_ir_msg) ++ desc = "IR Operation Status"; ++ break; ++ case MPI2_EVENT_SAS_DISCOVERY: ++ { ++ Mpi2EventDataSasDiscovery_t *event_data = ++ (Mpi2EventDataSasDiscovery_t *)mpi_reply->EventData; ++ pr_info(MPT3SAS_FMT "Discovery: (%s)", ioc->name, ++ (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ? ++ "start" : "stop"); ++ if (event_data->DiscoveryStatus) ++ pr_info("discovery_status(0x%08x)", ++ le32_to_cpu(event_data->DiscoveryStatus)); ++ pr_info("\n"); ++ return; ++ } ++ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: ++ desc = "SAS Broadcast Primitive"; ++ break; ++ case MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE: ++ desc = "SAS Init Device Status Change"; ++ break; ++ case MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW: ++ desc = "SAS Init Table Overflow"; ++ break; ++ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: ++ desc = "SAS Topology Change List"; ++ break; ++ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: ++ desc = "SAS Enclosure Device Status Change"; ++ break; ++ case MPI2_EVENT_IR_VOLUME: ++ if (!ioc->hide_ir_msg) ++ desc = "IR Volume"; ++ break; ++ case MPI2_EVENT_IR_PHYSICAL_DISK: ++ if (!ioc->hide_ir_msg) ++ desc = "IR Physical Disk"; ++ break; ++ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: ++ if (!ioc->hide_ir_msg) ++ desc = "IR Configuration Change List"; ++ break; ++ case MPI2_EVENT_LOG_ENTRY_ADDED: ++ if (!ioc->hide_ir_msg) ++ desc = "Log Entry Added"; ++ break; ++ case MPI2_EVENT_TEMP_THRESHOLD: ++ desc = "Temperature Threshold"; ++ break; ++ case MPI2_EVENT_ACTIVE_CABLE_EXCEPTION: ++ desc = "Active cable exception"; ++ break; ++ } ++ ++ if (!desc) ++ return; ++ ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, desc); ++} ++ ++/** ++ * _base_sas_log_info - verbose translation of firmware log info ++ * @ioc: per adapter object ++ * @log_info: log info ++ * ++ * Return nothing. ++ */ ++static void ++_base_sas_log_info(struct MPT3SAS_ADAPTER *ioc , u32 log_info) ++{ ++ union loginfo_type { ++ u32 loginfo; ++ struct { ++ u32 subcode:16; ++ u32 code:8; ++ u32 originator:4; ++ u32 bus_type:4; ++ } dw; ++ }; ++ union loginfo_type sas_loginfo; ++ char *originator_str = NULL; ++ ++ sas_loginfo.loginfo = log_info; ++ if (sas_loginfo.dw.bus_type != 3 /*SAS*/) ++ return; ++ ++ /* each nexus loss loginfo */ ++ if (log_info == 0x31170000) ++ return; ++ ++ /* eat the loginfos associated with task aborts */ ++ if (ioc->ignore_loginfos && (log_info == 0x30050000 || log_info == ++ 0x31140000 || log_info == 0x31130000)) ++ return; ++ ++ switch (sas_loginfo.dw.originator) { ++ case 0: ++ originator_str = "IOP"; ++ break; ++ case 1: ++ originator_str = "PL"; ++ break; ++ case 2: ++ if (!ioc->hide_ir_msg) ++ originator_str = "IR"; ++ else ++ originator_str = "WarpDrive"; ++ break; ++ } ++ ++ pr_warn(MPT3SAS_FMT ++ "log_info(0x%08x): originator(%s), code(0x%02x), sub_code(0x%04x)\n", ++ ioc->name, log_info, ++ originator_str, sas_loginfo.dw.code, ++ sas_loginfo.dw.subcode); ++} ++ ++/** ++ * _base_display_reply_info - ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * ++ * Return nothing. ++ */ ++static void ++_base_display_reply_info(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ u16 ioc_status; ++ u32 loginfo = 0; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (unlikely(!mpi_reply)) { ++ pr_err(MPT3SAS_FMT "mpi_reply not valid at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus); ++ ++ if ((ioc_status & MPI2_IOCSTATUS_MASK) && ++ (ioc->logging_level & MPT_DEBUG_REPLY)) { ++ _base_sas_ioc_info(ioc , mpi_reply, ++ mpt2sas_base_get_msg_frame(ioc, smid)); ++ } ++ ++ if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) { ++ loginfo = le32_to_cpu(mpi_reply->IOCLogInfo); ++ _base_sas_log_info(ioc, loginfo); ++ } ++ ++ if (ioc_status || loginfo) { ++ ioc_status &= MPI2_IOCSTATUS_MASK; ++ mpt2sas_trigger_mpi(ioc, ioc_status, loginfo); ++ } ++} ++ ++/** ++ * mpt2sas_base_done - base internal command completion routine ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_base_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (mpi_reply && mpi_reply->Function == MPI2_FUNCTION_EVENT_ACK) ++ return mpt2sas_check_for_pending_internal_cmds(ioc, smid); ++ ++ if (ioc->base_cmds.status == MPT3_CMD_NOT_USED) ++ return 1; ++ ++ ioc->base_cmds.status |= MPT3_CMD_COMPLETE; ++ if (mpi_reply) { ++ ioc->base_cmds.status |= MPT3_CMD_REPLY_VALID; ++ memcpy(ioc->base_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); ++ } ++ ioc->base_cmds.status &= ~MPT3_CMD_PENDING; ++ ++ complete(&ioc->base_cmds.done); ++ return 1; ++} ++ ++/** ++ * _base_async_event - main callback handler for firmware asyn events ++ * @ioc: per adapter object ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_base_async_event(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, u32 reply) ++{ ++ Mpi2EventNotificationReply_t *mpi_reply; ++ Mpi2EventAckRequest_t *ack_request; ++ u16 smid; ++ struct _event_ack_list *delayed_event_ack; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (!mpi_reply) ++ return 1; ++ if (mpi_reply->Function != MPI2_FUNCTION_EVENT_NOTIFICATION) ++ return 1; ++ ++ _base_display_event_data(ioc, mpi_reply); ++ ++ if (!(mpi_reply->AckRequired & MPI2_EVENT_NOTIFICATION_ACK_REQUIRED)) ++ goto out; ++ smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); ++ if (!smid) { ++ delayed_event_ack = kzalloc(sizeof(*delayed_event_ack), ++ GFP_ATOMIC); ++ if (!delayed_event_ack) ++ goto out; ++ INIT_LIST_HEAD(&delayed_event_ack->list); ++ delayed_event_ack->Event = mpi_reply->Event; ++ delayed_event_ack->EventContext = mpi_reply->EventContext; ++ list_add_tail(&delayed_event_ack->list, ++ &ioc->delayed_event_ack_list); ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "DELAYED: EVENT ACK: event (0x%04x)\n", ++ ioc->name, le16_to_cpu(mpi_reply->Event))); ++ goto out; ++ } ++ ++ ack_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ memset(ack_request, 0, sizeof(Mpi2EventAckRequest_t)); ++ ack_request->Function = MPI2_FUNCTION_EVENT_ACK; ++ ack_request->Event = mpi_reply->Event; ++ ack_request->EventContext = mpi_reply->EventContext; ++ ack_request->VF_ID = 0; /* TODO */ ++ ack_request->VP_ID = 0; ++ mpt2sas_base_put_smid_default(ioc, smid); ++ ++ out: ++ ++ /* scsih callback handler */ ++ mpt2sas_scsih_event_callback(ioc, msix_index, reply); ++ ++ /* ctl callback handler */ ++ mpt2sas_ctl_event_callback(ioc, msix_index, reply); ++ ++ return 1; ++} ++ ++/** ++ * _base_get_cb_idx - obtain the callback index ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Return callback index. ++ */ ++static u8 ++_base_get_cb_idx(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ int i; ++ u8 cb_idx; ++ ++ if (smid < ioc->hi_priority_smid) { ++ i = smid - 1; ++ cb_idx = ioc->scsi_lookup[i].cb_idx; ++ } else if (smid < ioc->internal_smid) { ++ i = smid - ioc->hi_priority_smid; ++ cb_idx = ioc->hpr_lookup[i].cb_idx; ++ } else if (smid <= ioc->hba_queue_depth) { ++ i = smid - ioc->internal_smid; ++ cb_idx = ioc->internal_lookup[i].cb_idx; ++ } else ++ cb_idx = 0xFF; ++ return cb_idx; ++} ++ ++/** ++ * _base_mask_interrupts - disable interrupts ++ * @ioc: per adapter object ++ * ++ * Disabling ResetIRQ, Reply and Doorbell Interrupts ++ * ++ * Return nothing. ++ */ ++static void ++_base_mask_interrupts(struct MPT3SAS_ADAPTER *ioc) ++{ ++ u32 him_register; ++ ++ ioc->mask_interrupts = 1; ++ him_register = readl(&ioc->chip->HostInterruptMask); ++ him_register |= MPI2_HIM_DIM + MPI2_HIM_RIM + MPI2_HIM_RESET_IRQ_MASK; ++ writel(him_register, &ioc->chip->HostInterruptMask); ++ readl(&ioc->chip->HostInterruptMask); ++} ++ ++/** ++ * _base_unmask_interrupts - enable interrupts ++ * @ioc: per adapter object ++ * ++ * Enabling only Reply Interrupts ++ * ++ * Return nothing. ++ */ ++static void ++_base_unmask_interrupts(struct MPT3SAS_ADAPTER *ioc) ++{ ++ u32 him_register; ++ ++ him_register = readl(&ioc->chip->HostInterruptMask); ++ him_register &= ~MPI2_HIM_RIM; ++ writel(him_register, &ioc->chip->HostInterruptMask); ++ ioc->mask_interrupts = 0; ++} ++ ++union reply_descriptor { ++ u64 word; ++ struct { ++ u32 low; ++ u32 high; ++ } u; ++}; ++ ++/** ++ * _base_interrupt - MPT adapter (IOC) specific interrupt handler. ++ * @irq: irq number (not used) ++ * @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure ++ * @r: pt_regs pointer (not used) ++ * ++ * Return IRQ_HANDLE if processed, else IRQ_NONE. ++ */ ++static irqreturn_t ++_base_interrupt(int irq, void *bus_id) ++{ ++ struct adapter_reply_queue *reply_q = bus_id; ++ union reply_descriptor rd; ++ u32 completed_cmds; ++ u8 request_desript_type; ++ u16 smid; ++ u8 cb_idx; ++ u32 reply; ++ u8 msix_index = reply_q->msix_index; ++ struct MPT3SAS_ADAPTER *ioc = reply_q->ioc; ++ Mpi2ReplyDescriptorsUnion_t *rpf; ++ u8 rc; ++ ++ if (ioc->mask_interrupts) ++ return IRQ_NONE; ++ ++ if (!atomic_add_unless(&reply_q->busy, 1, 1)) ++ return IRQ_NONE; ++ ++ rpf = &reply_q->reply_post_free[reply_q->reply_post_host_index]; ++ request_desript_type = rpf->Default.ReplyFlags ++ & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; ++ if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) { ++ atomic_dec(&reply_q->busy); ++ return IRQ_NONE; ++ } ++ ++ completed_cmds = 0; ++ cb_idx = 0xFF; ++ do { ++ rd.word = le64_to_cpu(rpf->Words); ++ if (rd.u.low == UINT_MAX || rd.u.high == UINT_MAX) ++ goto out; ++ reply = 0; ++ smid = le16_to_cpu(rpf->Default.DescriptorTypeDependent1); ++ if (request_desript_type == ++ MPI25_RPY_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO_SUCCESS || ++ request_desript_type == ++ MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS) { ++ cb_idx = _base_get_cb_idx(ioc, smid); ++ if ((likely(cb_idx < MPT_MAX_CALLBACKS)) && ++ (likely(mpt_callbacks[cb_idx] != NULL))) { ++ rc = mpt_callbacks[cb_idx](ioc, smid, ++ msix_index, 0); ++ if (rc) ++ mpt2sas_base_free_smid(ioc, smid); ++ } ++ } else if (request_desript_type == ++ MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) { ++ reply = le32_to_cpu( ++ rpf->AddressReply.ReplyFrameAddress); ++ if (reply > ioc->reply_dma_max_address || ++ reply < ioc->reply_dma_min_address) ++ reply = 0; ++ if (smid) { ++ cb_idx = _base_get_cb_idx(ioc, smid); ++ if ((likely(cb_idx < MPT_MAX_CALLBACKS)) && ++ (likely(mpt_callbacks[cb_idx] != NULL))) { ++ rc = mpt_callbacks[cb_idx](ioc, smid, ++ msix_index, reply); ++ if (reply) ++ _base_display_reply_info(ioc, ++ smid, msix_index, reply); ++ if (rc) ++ mpt2sas_base_free_smid(ioc, ++ smid); ++ } ++ } else { ++ _base_async_event(ioc, msix_index, reply); ++ } ++ ++ /* reply free queue handling */ ++ if (reply) { ++ ioc->reply_free_host_index = ++ (ioc->reply_free_host_index == ++ (ioc->reply_free_queue_depth - 1)) ? ++ 0 : ioc->reply_free_host_index + 1; ++ ioc->reply_free[ioc->reply_free_host_index] = ++ cpu_to_le32(reply); ++ wmb(); ++ writel(ioc->reply_free_host_index, ++ &ioc->chip->ReplyFreeHostIndex); ++ } ++ } ++ ++ rpf->Words = cpu_to_le64(ULLONG_MAX); ++ reply_q->reply_post_host_index = ++ (reply_q->reply_post_host_index == ++ (ioc->reply_post_queue_depth - 1)) ? 0 : ++ reply_q->reply_post_host_index + 1; ++ request_desript_type = ++ reply_q->reply_post_free[reply_q->reply_post_host_index]. ++ Default.ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; ++ completed_cmds++; ++ if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) ++ goto out; ++ if (!reply_q->reply_post_host_index) ++ rpf = reply_q->reply_post_free; ++ else ++ rpf++; ++ } while (1); ++ ++ out: ++ ++ if (!completed_cmds) { ++ atomic_dec(&reply_q->busy); ++ return IRQ_NONE; ++ } ++ ++ wmb(); ++ if (ioc->is_warpdrive) { ++ writel(reply_q->reply_post_host_index, ++ ioc->reply_post_host_index[msix_index]); ++ atomic_dec(&reply_q->busy); ++ return IRQ_HANDLED; ++ } ++ ++ /* Update Reply Post Host Index. ++ * For those HBA's which support combined reply queue feature ++ * 1. Get the correct Supplemental Reply Post Host Index Register. ++ * i.e. (msix_index / 8)th entry from Supplemental Reply Post Host ++ * Index Register address bank i.e replyPostRegisterIndex[], ++ * 2. Then update this register with new reply host index value ++ * in ReplyPostIndex field and the MSIxIndex field with ++ * msix_index value reduced to a value between 0 and 7, ++ * using a modulo 8 operation. Since each Supplemental Reply Post ++ * Host Index Register supports 8 MSI-X vectors. ++ * ++ * For other HBA's just update the Reply Post Host Index register with ++ * new reply host index value in ReplyPostIndex Field and msix_index ++ * value in MSIxIndex field. ++ */ ++ if (ioc->msix96_vector) ++ writel(reply_q->reply_post_host_index | ((msix_index & 7) << ++ MPI2_RPHI_MSIX_INDEX_SHIFT), ++ ioc->replyPostRegisterIndex[msix_index/8]); ++ else ++ writel(reply_q->reply_post_host_index | (msix_index << ++ MPI2_RPHI_MSIX_INDEX_SHIFT), ++ &ioc->chip->ReplyPostHostIndex); ++ atomic_dec(&reply_q->busy); ++ return IRQ_HANDLED; ++} ++ ++/** ++ * _base_is_controller_msix_enabled - is controller support muli-reply queues ++ * @ioc: per adapter object ++ * ++ */ ++static inline int ++_base_is_controller_msix_enabled(struct MPT3SAS_ADAPTER *ioc) ++{ ++ return (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable; ++} ++ ++/** ++ * mpt2sas_base_sync_reply_irqs - flush pending MSIX interrupts ++ * @ioc: per adapter object ++ * Context: non ISR conext ++ * ++ * Called when a Task Management request has completed. ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_sync_reply_irqs(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct adapter_reply_queue *reply_q; ++ ++ /* If MSIX capability is turned off ++ * then multi-queues are not enabled ++ */ ++ if (!_base_is_controller_msix_enabled(ioc)) ++ return; ++ ++ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { ++ if (ioc->shost_recovery || ioc->remove_host || ++ ioc->pci_error_recovery) ++ return; ++ /* TMs are on msix_index == 0 */ ++ if (reply_q->msix_index == 0) ++ continue; ++ synchronize_irq(reply_q->vector); ++ } ++} ++ ++/** ++ * mpt2sas_base_release_callback_handler - clear interrupt callback handler ++ * @cb_idx: callback index ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_release_callback_handler(u8 cb_idx) ++{ ++ mpt_callbacks[cb_idx] = NULL; ++} ++ ++/** ++ * mpt2sas_base_register_callback_handler - obtain index for the interrupt callback handler ++ * @cb_func: callback function ++ * ++ * Returns cb_func. ++ */ ++u8 ++mpt2sas_base_register_callback_handler(MPT_CALLBACK cb_func) ++{ ++ u8 cb_idx; ++ ++ for (cb_idx = MPT_MAX_CALLBACKS-1; cb_idx; cb_idx--) ++ if (mpt_callbacks[cb_idx] == NULL) ++ break; ++ ++ mpt_callbacks[cb_idx] = cb_func; ++ return cb_idx; ++} ++ ++/** ++ * mpt2sas_base_initialize_callback_handler - initialize the interrupt callback handler ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_initialize_callback_handler(void) ++{ ++ u8 cb_idx; ++ ++ for (cb_idx = 0; cb_idx < MPT_MAX_CALLBACKS; cb_idx++) ++ mpt2sas_base_release_callback_handler(cb_idx); ++} ++ ++ ++/** ++ * _base_build_zero_len_sge - build zero length sg entry ++ * @ioc: per adapter object ++ * @paddr: virtual address for SGE ++ * ++ * Create a zero length scatter gather entry to insure the IOCs hardware has ++ * something to use if the target device goes brain dead and tries ++ * to send data even when none is asked for. ++ * ++ * Return nothing. ++ */ ++static void ++_base_build_zero_len_sge(struct MPT3SAS_ADAPTER *ioc, void *paddr) ++{ ++ u32 flags_length = (u32)((MPI2_SGE_FLAGS_LAST_ELEMENT | ++ MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST | ++ MPI2_SGE_FLAGS_SIMPLE_ELEMENT) << ++ MPI2_SGE_FLAGS_SHIFT); ++ ioc->base_add_sg_single(paddr, flags_length, -1); ++} ++ ++/** ++ * _base_add_sg_single_32 - Place a simple 32 bit SGE at address pAddr. ++ * @paddr: virtual address for SGE ++ * @flags_length: SGE flags and data transfer length ++ * @dma_addr: Physical address ++ * ++ * Return nothing. ++ */ ++static void ++_base_add_sg_single_32(void *paddr, u32 flags_length, dma_addr_t dma_addr) ++{ ++ Mpi2SGESimple32_t *sgel = paddr; ++ ++ flags_length |= (MPI2_SGE_FLAGS_32_BIT_ADDRESSING | ++ MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT; ++ sgel->FlagsLength = cpu_to_le32(flags_length); ++ sgel->Address = cpu_to_le32(dma_addr); ++} ++ ++ ++/** ++ * _base_add_sg_single_64 - Place a simple 64 bit SGE at address pAddr. ++ * @paddr: virtual address for SGE ++ * @flags_length: SGE flags and data transfer length ++ * @dma_addr: Physical address ++ * ++ * Return nothing. ++ */ ++static void ++_base_add_sg_single_64(void *paddr, u32 flags_length, dma_addr_t dma_addr) ++{ ++ Mpi2SGESimple64_t *sgel = paddr; ++ ++ flags_length |= (MPI2_SGE_FLAGS_64_BIT_ADDRESSING | ++ MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT; ++ sgel->FlagsLength = cpu_to_le32(flags_length); ++ sgel->Address = cpu_to_le64(dma_addr); ++} ++ ++/** ++ * _base_get_chain_buffer_tracker - obtain chain tracker ++ * @ioc: per adapter object ++ * @smid: smid associated to an IO request ++ * ++ * Returns chain tracker(from ioc->free_chain_list) ++ */ ++static struct chain_tracker * ++_base_get_chain_buffer_tracker(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ struct chain_tracker *chain_req; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ if (list_empty(&ioc->free_chain_list)) { ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "chain buffers not available\n", ioc->name)); ++ return NULL; ++ } ++ chain_req = list_entry(ioc->free_chain_list.next, ++ struct chain_tracker, tracker_list); ++ list_del_init(&chain_req->tracker_list); ++ list_add_tail(&chain_req->tracker_list, ++ &ioc->scsi_lookup[smid - 1].chain_list); ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return chain_req; ++} ++ ++ ++/** ++ * _base_build_sg - build generic sg ++ * @ioc: per adapter object ++ * @psge: virtual address for SGE ++ * @data_out_dma: physical address for WRITES ++ * @data_out_sz: data xfer size for WRITES ++ * @data_in_dma: physical address for READS ++ * @data_in_sz: data xfer size for READS ++ * ++ * Return nothing. ++ */ ++static void ++_base_build_sg(struct MPT3SAS_ADAPTER *ioc, void *psge, ++ dma_addr_t data_out_dma, size_t data_out_sz, dma_addr_t data_in_dma, ++ size_t data_in_sz) ++{ ++ u32 sgl_flags; ++ ++ if (!data_out_sz && !data_in_sz) { ++ _base_build_zero_len_sge(ioc, psge); ++ return; ++ } ++ ++ if (data_out_sz && data_in_sz) { ++ /* WRITE sgel first */ ++ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC); ++ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; ++ ioc->base_add_sg_single(psge, sgl_flags | ++ data_out_sz, data_out_dma); ++ ++ /* incr sgel */ ++ psge += ioc->sge_size; ++ ++ /* READ sgel last */ ++ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | ++ MPI2_SGE_FLAGS_END_OF_LIST); ++ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; ++ ioc->base_add_sg_single(psge, sgl_flags | ++ data_in_sz, data_in_dma); ++ } else if (data_out_sz) /* WRITE */ { ++ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | ++ MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC); ++ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; ++ ioc->base_add_sg_single(psge, sgl_flags | ++ data_out_sz, data_out_dma); ++ } else if (data_in_sz) /* READ */ { ++ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | ++ MPI2_SGE_FLAGS_END_OF_LIST); ++ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; ++ ioc->base_add_sg_single(psge, sgl_flags | ++ data_in_sz, data_in_dma); ++ } ++} ++ ++/* IEEE format sgls */ ++ ++/** ++ * _base_add_sg_single_ieee - add sg element for IEEE format ++ * @paddr: virtual address for SGE ++ * @flags: SGE flags ++ * @chain_offset: number of 128 byte elements from start of segment ++ * @length: data transfer length ++ * @dma_addr: Physical address ++ * ++ * Return nothing. ++ */ ++static void ++_base_add_sg_single_ieee(void *paddr, u8 flags, u8 chain_offset, u32 length, ++ dma_addr_t dma_addr) ++{ ++ Mpi25IeeeSgeChain64_t *sgel = paddr; ++ ++ sgel->Flags = flags; ++ sgel->NextChainOffset = chain_offset; ++ sgel->Length = cpu_to_le32(length); ++ sgel->Address = cpu_to_le64(dma_addr); ++} ++ ++/** ++ * _base_build_zero_len_sge_ieee - build zero length sg entry for IEEE format ++ * @ioc: per adapter object ++ * @paddr: virtual address for SGE ++ * ++ * Create a zero length scatter gather entry to insure the IOCs hardware has ++ * something to use if the target device goes brain dead and tries ++ * to send data even when none is asked for. ++ * ++ * Return nothing. ++ */ ++static void ++_base_build_zero_len_sge_ieee(struct MPT3SAS_ADAPTER *ioc, void *paddr) ++{ ++ u8 sgl_flags = (MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR | ++ MPI25_IEEE_SGE_FLAGS_END_OF_LIST); ++ ++ _base_add_sg_single_ieee(paddr, sgl_flags, 0, 0, -1); ++} ++ ++/** ++ * _base_build_sg_scmd - main sg creation routine ++ * @ioc: per adapter object ++ * @scmd: scsi command ++ * @smid: system request message index ++ * Context: none. ++ * ++ * The main routine that builds scatter gather table from a given ++ * scsi request sent via the .queuecommand main handler. ++ * ++ * Returns 0 success, anything else error ++ */ ++static int ++_base_build_sg_scmd(struct MPT3SAS_ADAPTER *ioc, ++ struct scsi_cmnd *scmd, u16 smid) ++{ ++ Mpi2SCSIIORequest_t *mpi_request; ++ dma_addr_t chain_dma; ++ struct scatterlist *sg_scmd; ++ void *sg_local, *chain; ++ u32 chain_offset; ++ u32 chain_length; ++ u32 chain_flags; ++ int sges_left; ++ u32 sges_in_segment; ++ u32 sgl_flags; ++ u32 sgl_flags_last_element; ++ u32 sgl_flags_end_buffer; ++ struct chain_tracker *chain_req; ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ++ /* init scatter gather flags */ ++ sgl_flags = MPI2_SGE_FLAGS_SIMPLE_ELEMENT; ++ if (scmd->sc_data_direction == DMA_TO_DEVICE) ++ sgl_flags |= MPI2_SGE_FLAGS_HOST_TO_IOC; ++ sgl_flags_last_element = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT) ++ << MPI2_SGE_FLAGS_SHIFT; ++ sgl_flags_end_buffer = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT | ++ MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST) ++ << MPI2_SGE_FLAGS_SHIFT; ++ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; ++ ++ sg_scmd = scsi_sglist(scmd); ++ sges_left = scsi_dma_map(scmd); ++ if (sges_left < 0) { ++ sdev_printk(KERN_ERR, scmd->device, ++ "pci_map_sg failed: request for %d bytes!\n", ++ scsi_bufflen(scmd)); ++ return -ENOMEM; ++ } ++ ++ sg_local = &mpi_request->SGL; ++ sges_in_segment = ioc->max_sges_in_main_message; ++ if (sges_left <= sges_in_segment) ++ goto fill_in_last_segment; ++ ++ mpi_request->ChainOffset = (offsetof(Mpi2SCSIIORequest_t, SGL) + ++ (sges_in_segment * ioc->sge_size))/4; ++ ++ /* fill in main message segment when there is a chain following */ ++ while (sges_in_segment) { ++ if (sges_in_segment == 1) ++ ioc->base_add_sg_single(sg_local, ++ sgl_flags_last_element | sg_dma_len(sg_scmd), ++ sg_dma_address(sg_scmd)); ++ else ++ ioc->base_add_sg_single(sg_local, sgl_flags | ++ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); ++ sg_scmd = sg_next(sg_scmd); ++ sg_local += ioc->sge_size; ++ sges_left--; ++ sges_in_segment--; ++ } ++ ++ /* initializing the chain flags and pointers */ ++ chain_flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT << MPI2_SGE_FLAGS_SHIFT; ++ chain_req = _base_get_chain_buffer_tracker(ioc, smid); ++ if (!chain_req) ++ return -1; ++ chain = chain_req->chain_buffer; ++ chain_dma = chain_req->chain_buffer_dma; ++ do { ++ sges_in_segment = (sges_left <= ++ ioc->max_sges_in_chain_message) ? sges_left : ++ ioc->max_sges_in_chain_message; ++ chain_offset = (sges_left == sges_in_segment) ? ++ 0 : (sges_in_segment * ioc->sge_size)/4; ++ chain_length = sges_in_segment * ioc->sge_size; ++ if (chain_offset) { ++ chain_offset = chain_offset << ++ MPI2_SGE_CHAIN_OFFSET_SHIFT; ++ chain_length += ioc->sge_size; ++ } ++ ioc->base_add_sg_single(sg_local, chain_flags | chain_offset | ++ chain_length, chain_dma); ++ sg_local = chain; ++ if (!chain_offset) ++ goto fill_in_last_segment; ++ ++ /* fill in chain segments */ ++ while (sges_in_segment) { ++ if (sges_in_segment == 1) ++ ioc->base_add_sg_single(sg_local, ++ sgl_flags_last_element | ++ sg_dma_len(sg_scmd), ++ sg_dma_address(sg_scmd)); ++ else ++ ioc->base_add_sg_single(sg_local, sgl_flags | ++ sg_dma_len(sg_scmd), ++ sg_dma_address(sg_scmd)); ++ sg_scmd = sg_next(sg_scmd); ++ sg_local += ioc->sge_size; ++ sges_left--; ++ sges_in_segment--; ++ } ++ ++ chain_req = _base_get_chain_buffer_tracker(ioc, smid); ++ if (!chain_req) ++ return -1; ++ chain = chain_req->chain_buffer; ++ chain_dma = chain_req->chain_buffer_dma; ++ } while (1); ++ ++ ++ fill_in_last_segment: ++ ++ /* fill the last segment */ ++ while (sges_left) { ++ if (sges_left == 1) ++ ioc->base_add_sg_single(sg_local, sgl_flags_end_buffer | ++ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); ++ else ++ ioc->base_add_sg_single(sg_local, sgl_flags | ++ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); ++ sg_scmd = sg_next(sg_scmd); ++ sg_local += ioc->sge_size; ++ sges_left--; ++ } ++ ++ return 0; ++} ++ ++/** ++ * _base_build_sg_scmd_ieee - main sg creation routine for IEEE format ++ * @ioc: per adapter object ++ * @scmd: scsi command ++ * @smid: system request message index ++ * Context: none. ++ * ++ * The main routine that builds scatter gather table from a given ++ * scsi request sent via the .queuecommand main handler. ++ * ++ * Returns 0 success, anything else error ++ */ ++static int ++_base_build_sg_scmd_ieee(struct MPT3SAS_ADAPTER *ioc, ++ struct scsi_cmnd *scmd, u16 smid) ++{ ++ Mpi2SCSIIORequest_t *mpi_request; ++ dma_addr_t chain_dma; ++ struct scatterlist *sg_scmd; ++ void *sg_local, *chain; ++ u32 chain_offset; ++ u32 chain_length; ++ int sges_left; ++ u32 sges_in_segment; ++ u8 simple_sgl_flags; ++ u8 simple_sgl_flags_last; ++ u8 chain_sgl_flags; ++ struct chain_tracker *chain_req; ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ++ /* init scatter gather flags */ ++ simple_sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; ++ simple_sgl_flags_last = simple_sgl_flags | ++ MPI25_IEEE_SGE_FLAGS_END_OF_LIST; ++ chain_sgl_flags = MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT | ++ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; ++ ++ sg_scmd = scsi_sglist(scmd); ++ sges_left = scsi_dma_map(scmd); ++ if (sges_left < 0) { ++ sdev_printk(KERN_ERR, scmd->device, ++ "pci_map_sg failed: request for %d bytes!\n", ++ scsi_bufflen(scmd)); ++ return -ENOMEM; ++ } ++ ++ sg_local = &mpi_request->SGL; ++ sges_in_segment = (ioc->request_sz - ++ offsetof(Mpi2SCSIIORequest_t, SGL))/ioc->sge_size_ieee; ++ if (sges_left <= sges_in_segment) ++ goto fill_in_last_segment; ++ ++ mpi_request->ChainOffset = (sges_in_segment - 1 /* chain element */) + ++ (offsetof(Mpi2SCSIIORequest_t, SGL)/ioc->sge_size_ieee); ++ ++ /* fill in main message segment when there is a chain following */ ++ while (sges_in_segment > 1) { ++ _base_add_sg_single_ieee(sg_local, simple_sgl_flags, 0, ++ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); ++ sg_scmd = sg_next(sg_scmd); ++ sg_local += ioc->sge_size_ieee; ++ sges_left--; ++ sges_in_segment--; ++ } ++ ++ /* initializing the pointers */ ++ chain_req = _base_get_chain_buffer_tracker(ioc, smid); ++ if (!chain_req) ++ return -1; ++ chain = chain_req->chain_buffer; ++ chain_dma = chain_req->chain_buffer_dma; ++ do { ++ sges_in_segment = (sges_left <= ++ ioc->max_sges_in_chain_message) ? sges_left : ++ ioc->max_sges_in_chain_message; ++ chain_offset = (sges_left == sges_in_segment) ? ++ 0 : sges_in_segment; ++ chain_length = sges_in_segment * ioc->sge_size_ieee; ++ if (chain_offset) ++ chain_length += ioc->sge_size_ieee; ++ _base_add_sg_single_ieee(sg_local, chain_sgl_flags, ++ chain_offset, chain_length, chain_dma); ++ ++ sg_local = chain; ++ if (!chain_offset) ++ goto fill_in_last_segment; ++ ++ /* fill in chain segments */ ++ while (sges_in_segment) { ++ _base_add_sg_single_ieee(sg_local, simple_sgl_flags, 0, ++ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); ++ sg_scmd = sg_next(sg_scmd); ++ sg_local += ioc->sge_size_ieee; ++ sges_left--; ++ sges_in_segment--; ++ } ++ ++ chain_req = _base_get_chain_buffer_tracker(ioc, smid); ++ if (!chain_req) ++ return -1; ++ chain = chain_req->chain_buffer; ++ chain_dma = chain_req->chain_buffer_dma; ++ } while (1); ++ ++ ++ fill_in_last_segment: ++ ++ /* fill the last segment */ ++ while (sges_left > 0) { ++ if (sges_left == 1) ++ _base_add_sg_single_ieee(sg_local, ++ simple_sgl_flags_last, 0, sg_dma_len(sg_scmd), ++ sg_dma_address(sg_scmd)); ++ else ++ _base_add_sg_single_ieee(sg_local, simple_sgl_flags, 0, ++ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); ++ sg_scmd = sg_next(sg_scmd); ++ sg_local += ioc->sge_size_ieee; ++ sges_left--; ++ } ++ ++ return 0; ++} ++ ++/** ++ * _base_build_sg_ieee - build generic sg for IEEE format ++ * @ioc: per adapter object ++ * @psge: virtual address for SGE ++ * @data_out_dma: physical address for WRITES ++ * @data_out_sz: data xfer size for WRITES ++ * @data_in_dma: physical address for READS ++ * @data_in_sz: data xfer size for READS ++ * ++ * Return nothing. ++ */ ++static void ++_base_build_sg_ieee(struct MPT3SAS_ADAPTER *ioc, void *psge, ++ dma_addr_t data_out_dma, size_t data_out_sz, dma_addr_t data_in_dma, ++ size_t data_in_sz) ++{ ++ u8 sgl_flags; ++ ++ if (!data_out_sz && !data_in_sz) { ++ _base_build_zero_len_sge_ieee(ioc, psge); ++ return; ++ } ++ ++ if (data_out_sz && data_in_sz) { ++ /* WRITE sgel first */ ++ sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; ++ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_out_sz, ++ data_out_dma); ++ ++ /* incr sgel */ ++ psge += ioc->sge_size_ieee; ++ ++ /* READ sgel last */ ++ sgl_flags |= MPI25_IEEE_SGE_FLAGS_END_OF_LIST; ++ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_in_sz, ++ data_in_dma); ++ } else if (data_out_sz) /* WRITE */ { ++ sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI25_IEEE_SGE_FLAGS_END_OF_LIST | ++ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; ++ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_out_sz, ++ data_out_dma); ++ } else if (data_in_sz) /* READ */ { ++ sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI25_IEEE_SGE_FLAGS_END_OF_LIST | ++ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; ++ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_in_sz, ++ data_in_dma); ++ } ++} ++ ++#define convert_to_kb(x) ((x) << (PAGE_SHIFT - 10)) ++ ++/** ++ * _base_config_dma_addressing - set dma addressing ++ * @ioc: per adapter object ++ * @pdev: PCI device struct ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_config_dma_addressing(struct MPT3SAS_ADAPTER *ioc, struct pci_dev *pdev) ++{ ++ struct sysinfo s; ++ u64 consistent_dma_mask; ++ ++ if (ioc->dma_mask) ++ consistent_dma_mask = DMA_BIT_MASK(64); ++ else ++ consistent_dma_mask = DMA_BIT_MASK(32); ++ ++ if (sizeof(dma_addr_t) > 4) { ++ const uint64_t required_mask = ++ dma_get_required_mask(&pdev->dev); ++ if ((required_mask > DMA_BIT_MASK(32)) && ++ !pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) && ++ !pci_set_consistent_dma_mask(pdev, consistent_dma_mask)) { ++ ioc->base_add_sg_single = &_base_add_sg_single_64; ++ ioc->sge_size = sizeof(Mpi2SGESimple64_t); ++ ioc->dma_mask = 64; ++ goto out; ++ } ++ } ++ ++ if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) ++ && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { ++ ioc->base_add_sg_single = &_base_add_sg_single_32; ++ ioc->sge_size = sizeof(Mpi2SGESimple32_t); ++ ioc->dma_mask = 32; ++ } else ++ return -ENODEV; ++ ++ out: ++ si_meminfo(&s); ++ pr_info(MPT3SAS_FMT ++ "%d BIT PCI BUS DMA ADDRESSING SUPPORTED, total mem (%ld kB)\n", ++ ioc->name, ioc->dma_mask, convert_to_kb(s.totalram)); ++ ++ return 0; ++} ++ ++static int ++_base_change_consistent_dma_mask(struct MPT3SAS_ADAPTER *ioc, ++ struct pci_dev *pdev) ++{ ++ if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) { ++ if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++/** ++ * _base_check_enable_msix - checks MSIX capabable. ++ * @ioc: per adapter object ++ * ++ * Check to see if card is capable of MSIX, and set number ++ * of available msix vectors ++ */ ++static int ++_base_check_enable_msix(struct MPT3SAS_ADAPTER *ioc) ++{ ++ int base; ++ u16 message_control; ++ ++ /* Check whether controller SAS2008 B0 controller, ++ * if it is SAS2008 B0 controller use IO-APIC instead of MSIX ++ */ ++ if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 && ++ ioc->pdev->revision == SAS2_PCI_DEVICE_B0_REVISION) { ++ return -EINVAL; ++ } ++ ++ base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX); ++ if (!base) { ++ dfailprintk(ioc, pr_info(MPT3SAS_FMT "msix not supported\n", ++ ioc->name)); ++ return -EINVAL; ++ } ++ ++ /* get msix vector count */ ++ /* NUMA_IO not supported for older controllers */ ++ if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2004 || ++ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 || ++ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_1 || ++ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_2 || ++ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_3 || ++ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2116_1 || ++ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2116_2) ++ ioc->msix_vector_count = 1; ++ else { ++ pci_read_config_word(ioc->pdev, base + 2, &message_control); ++ ioc->msix_vector_count = (message_control & 0x3FF) + 1; ++ } ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "msix is supported, vector_count(%d)\n", ++ ioc->name, ioc->msix_vector_count)); ++ return 0; ++} ++ ++/** ++ * _base_free_irq - free irq ++ * @ioc: per adapter object ++ * ++ * Freeing respective reply_queue from the list. ++ */ ++static void ++_base_free_irq(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct adapter_reply_queue *reply_q, *next; ++ ++ if (list_empty(&ioc->reply_queue_list)) ++ return; ++ ++ list_for_each_entry_safe(reply_q, next, &ioc->reply_queue_list, list) { ++ list_del(&reply_q->list); ++ if (smp_affinity_enable) { ++ irq_set_affinity_hint(reply_q->vector, NULL); ++ free_cpumask_var(reply_q->affinity_hint); ++ } ++ free_irq(reply_q->vector, reply_q); ++ kfree(reply_q); ++ } ++} ++ ++/** ++ * _base_request_irq - request irq ++ * @ioc: per adapter object ++ * @index: msix index into vector table ++ * @vector: irq vector ++ * ++ * Inserting respective reply_queue into the list. ++ */ ++static int ++_base_request_irq(struct MPT3SAS_ADAPTER *ioc, u8 index, u32 vector) ++{ ++ struct adapter_reply_queue *reply_q; ++ int r; ++ ++ reply_q = kzalloc(sizeof(struct adapter_reply_queue), GFP_KERNEL); ++ if (!reply_q) { ++ pr_err(MPT3SAS_FMT "unable to allocate memory %d!\n", ++ ioc->name, (int)sizeof(struct adapter_reply_queue)); ++ return -ENOMEM; ++ } ++ reply_q->ioc = ioc; ++ reply_q->msix_index = index; ++ reply_q->vector = vector; ++ ++ if (smp_affinity_enable) { ++ if (!zalloc_cpumask_var(&reply_q->affinity_hint, GFP_KERNEL)) { ++ kfree(reply_q); ++ return -ENOMEM; ++ } ++ } ++ ++ atomic_set(&reply_q->busy, 0); ++ if (ioc->msix_enable) ++ snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d-msix%d", ++ ioc->driver_name, ioc->id, index); ++ else ++ snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d", ++ ioc->driver_name, ioc->id); ++ r = request_irq(vector, _base_interrupt, IRQF_SHARED, reply_q->name, ++ reply_q); ++ if (r) { ++ pr_err(MPT3SAS_FMT "unable to allocate interrupt %d!\n", ++ reply_q->name, vector); ++ free_cpumask_var(reply_q->affinity_hint); ++ kfree(reply_q); ++ return -EBUSY; ++ } ++ ++ INIT_LIST_HEAD(&reply_q->list); ++ list_add_tail(&reply_q->list, &ioc->reply_queue_list); ++ return 0; ++} ++ ++/** ++ * _base_assign_reply_queues - assigning msix index for each cpu ++ * @ioc: per adapter object ++ * ++ * The enduser would need to set the affinity via /proc/irq/#/smp_affinity ++ * ++ * It would nice if we could call irq_set_affinity, however it is not ++ * an exported symbol ++ */ ++static void ++_base_assign_reply_queues(struct MPT3SAS_ADAPTER *ioc) ++{ ++ unsigned int cpu, nr_cpus, nr_msix, index = 0; ++ struct adapter_reply_queue *reply_q; ++ ++ if (!_base_is_controller_msix_enabled(ioc)) ++ return; ++ ++ memset(ioc->cpu_msix_table, 0, ioc->cpu_msix_table_sz); ++ ++ nr_cpus = num_online_cpus(); ++ nr_msix = ioc->reply_queue_count = min(ioc->reply_queue_count, ++ ioc->facts.MaxMSIxVectors); ++ if (!nr_msix) ++ return; ++ ++ cpu = cpumask_first(cpu_online_mask); ++ ++ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { ++ ++ unsigned int i, group = nr_cpus / nr_msix; ++ ++ if (cpu >= nr_cpus) ++ break; ++ ++ if (index < nr_cpus % nr_msix) ++ group++; ++ ++ for (i = 0 ; i < group ; i++) { ++ ioc->cpu_msix_table[cpu] = index; ++ if (smp_affinity_enable) ++ cpumask_or(reply_q->affinity_hint, ++ reply_q->affinity_hint, get_cpu_mask(cpu)); ++ cpu = cpumask_next(cpu, cpu_online_mask); ++ } ++ if (smp_affinity_enable) ++ if (irq_set_affinity_hint(reply_q->vector, ++ reply_q->affinity_hint)) ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "Err setting affinity hint to irq vector %d\n", ++ ioc->name, reply_q->vector)); ++ index++; ++ } ++} ++ ++/** ++ * _base_disable_msix - disables msix ++ * @ioc: per adapter object ++ * ++ */ ++static void ++_base_disable_msix(struct MPT3SAS_ADAPTER *ioc) ++{ ++ if (!ioc->msix_enable) ++ return; ++ pci_disable_msix(ioc->pdev); ++ ioc->msix_enable = 0; ++} ++ ++/** ++ * _base_enable_msix - enables msix, failback to io_apic ++ * @ioc: per adapter object ++ * ++ */ ++static int ++_base_enable_msix(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct msix_entry *entries, *a; ++ int r; ++ int i; ++ u8 try_msix = 0; ++ ++ if (msix_disable == -1 || msix_disable == 0) ++ try_msix = 1; ++ ++ if (!try_msix) ++ goto try_ioapic; ++ ++ if (_base_check_enable_msix(ioc) != 0) ++ goto try_ioapic; ++ ++ ioc->reply_queue_count = min_t(int, ioc->cpu_count, ++ ioc->msix_vector_count); ++ ++ printk(MPT3SAS_FMT "MSI-X vectors supported: %d, no of cores" ++ ": %d, max_msix_vectors: %d\n", ioc->name, ioc->msix_vector_count, ++ ioc->cpu_count, max_msix_vectors); ++ ++ if (!ioc->rdpq_array_enable && max_msix_vectors == -1) ++ max_msix_vectors = 8; ++ ++ if (max_msix_vectors > 0) { ++ ioc->reply_queue_count = min_t(int, max_msix_vectors, ++ ioc->reply_queue_count); ++ ioc->msix_vector_count = ioc->reply_queue_count; ++ } else if (max_msix_vectors == 0) ++ goto try_ioapic; ++ ++ if (ioc->msix_vector_count < ioc->cpu_count) ++ smp_affinity_enable = 0; ++ ++ entries = kcalloc(ioc->reply_queue_count, sizeof(struct msix_entry), ++ GFP_KERNEL); ++ if (!entries) { ++ dfailprintk(ioc, pr_info(MPT3SAS_FMT ++ "kcalloc failed @ at %s:%d/%s() !!!\n", ++ ioc->name, __FILE__, __LINE__, __func__)); ++ goto try_ioapic; ++ } ++ ++ for (i = 0, a = entries; i < ioc->reply_queue_count; i++, a++) ++ a->entry = i; ++ ++ r = pci_enable_msix_exact(ioc->pdev, entries, ioc->reply_queue_count); ++ if (r) { ++ dfailprintk(ioc, pr_info(MPT3SAS_FMT ++ "pci_enable_msix_exact failed (r=%d) !!!\n", ++ ioc->name, r)); ++ kfree(entries); ++ goto try_ioapic; ++ } ++ ++ ioc->msix_enable = 1; ++ for (i = 0, a = entries; i < ioc->reply_queue_count; i++, a++) { ++ r = _base_request_irq(ioc, i, a->vector); ++ if (r) { ++ _base_free_irq(ioc); ++ _base_disable_msix(ioc); ++ kfree(entries); ++ goto try_ioapic; ++ } ++ } ++ ++ kfree(entries); ++ return 0; ++ ++/* failback to io_apic interrupt routing */ ++ try_ioapic: ++ ++ ioc->reply_queue_count = 1; ++ r = _base_request_irq(ioc, 0, ioc->pdev->irq); ++ ++ return r; ++} ++ ++/** ++ * mpt2sas_base_unmap_resources - free controller resources ++ * @ioc: per adapter object ++ */ ++void ++mpt2sas_base_unmap_resources(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct pci_dev *pdev = ioc->pdev; ++ ++ dexitprintk(ioc, printk(MPT3SAS_FMT "%s\n", ++ ioc->name, __func__)); ++ ++ _base_free_irq(ioc); ++ _base_disable_msix(ioc); ++ ++ if (ioc->msix96_vector) { ++ kfree(ioc->replyPostRegisterIndex); ++ ioc->replyPostRegisterIndex = NULL; ++ } ++ ++ if (ioc->chip_phys) { ++ iounmap(ioc->chip); ++ ioc->chip_phys = 0; ++ } ++ ++ if (pci_is_enabled(pdev)) { ++ pci_release_selected_regions(ioc->pdev, ioc->bars); ++ pci_disable_pcie_error_reporting(pdev); ++ pci_disable_device(pdev); ++ } ++} ++ ++/** ++ * mpt2sas_base_map_resources - map in controller resources (io/irq/memap) ++ * @ioc: per adapter object ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct pci_dev *pdev = ioc->pdev; ++ u32 memap_sz; ++ u32 pio_sz; ++ int i, r = 0; ++ u64 pio_chip = 0; ++ u64 chip_phys = 0; ++ struct adapter_reply_queue *reply_q; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ++ ioc->name, __func__)); ++ ++ ioc->bars = pci_select_bars(pdev, IORESOURCE_MEM); ++ if (pci_enable_device_mem(pdev)) { ++ pr_warn(MPT3SAS_FMT "pci_enable_device_mem: failed\n", ++ ioc->name); ++ ioc->bars = 0; ++ return -ENODEV; ++ } ++ ++ ++ if (pci_request_selected_regions(pdev, ioc->bars, ++ ioc->driver_name)) { ++ pr_warn(MPT3SAS_FMT "pci_request_selected_regions: failed\n", ++ ioc->name); ++ ioc->bars = 0; ++ r = -ENODEV; ++ goto out_fail; ++ } ++ ++/* AER (Advanced Error Reporting) hooks */ ++ pci_enable_pcie_error_reporting(pdev); ++ ++ pci_set_master(pdev); ++ ++ ++ if (_base_config_dma_addressing(ioc, pdev) != 0) { ++ pr_warn(MPT3SAS_FMT "no suitable DMA mask for %s\n", ++ ioc->name, pci_name(pdev)); ++ r = -ENODEV; ++ goto out_fail; ++ } ++ ++ for (i = 0, memap_sz = 0, pio_sz = 0; (i < DEVICE_COUNT_RESOURCE) && ++ (!memap_sz || !pio_sz); i++) { ++ if (pci_resource_flags(pdev, i) & IORESOURCE_IO) { ++ if (pio_sz) ++ continue; ++ pio_chip = (u64)pci_resource_start(pdev, i); ++ pio_sz = pci_resource_len(pdev, i); ++ } else if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) { ++ if (memap_sz) ++ continue; ++ ioc->chip_phys = pci_resource_start(pdev, i); ++ chip_phys = (u64)ioc->chip_phys; ++ memap_sz = pci_resource_len(pdev, i); ++ ioc->chip = ioremap(ioc->chip_phys, memap_sz); ++ } ++ } ++ ++ if (ioc->chip == NULL) { ++ pr_err(MPT3SAS_FMT "unable to map adapter memory! " ++ " or resource not found\n", ioc->name); ++ r = -EINVAL; ++ goto out_fail; ++ } ++ ++ _base_mask_interrupts(ioc); ++ ++ r = _base_get_ioc_facts(ioc, CAN_SLEEP); ++ if (r) ++ goto out_fail; ++ ++ if (!ioc->rdpq_array_enable_assigned) { ++ ioc->rdpq_array_enable = ioc->rdpq_array_capable; ++ ioc->rdpq_array_enable_assigned = 1; ++ } ++ ++ r = _base_enable_msix(ioc); ++ if (r) ++ goto out_fail; ++ ++ /* Use the Combined reply queue feature only for SAS3 C0 & higher ++ * revision HBAs and also only when reply queue count is greater than 8 ++ */ ++ if (ioc->msix96_vector && ioc->reply_queue_count > 8) { ++ /* Determine the Supplemental Reply Post Host Index Registers ++ * Addresse. Supplemental Reply Post Host Index Registers ++ * starts at offset MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET and ++ * each register is at offset bytes of ++ * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET from previous one. ++ */ ++ ioc->replyPostRegisterIndex = kcalloc( ++ MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT, ++ sizeof(resource_size_t *), GFP_KERNEL); ++ if (!ioc->replyPostRegisterIndex) { ++ dfailprintk(ioc, printk(MPT3SAS_FMT ++ "allocation for reply Post Register Index failed!!!\n", ++ ioc->name)); ++ r = -ENOMEM; ++ goto out_fail; ++ } ++ ++ for (i = 0; i < MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT; i++) { ++ ioc->replyPostRegisterIndex[i] = (resource_size_t *) ++ ((u8 *)&ioc->chip->Doorbell + ++ MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET + ++ (i * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET)); ++ } ++ } else ++ ioc->msix96_vector = 0; ++ ++ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) ++ pr_info(MPT3SAS_FMT "%s: IRQ %d\n", ++ reply_q->name, ((ioc->msix_enable) ? "PCI-MSI-X enabled" : ++ "IO-APIC enabled"), reply_q->vector); ++ ++ pr_info(MPT3SAS_FMT "iomem(0x%016llx), mapped(0x%p), size(%d)\n", ++ ioc->name, (unsigned long long)chip_phys, ioc->chip, memap_sz); ++ pr_info(MPT3SAS_FMT "ioport(0x%016llx), size(%d)\n", ++ ioc->name, (unsigned long long)pio_chip, pio_sz); ++ ++ /* Save PCI configuration state for recovery from PCI AER/EEH errors */ ++ pci_save_state(pdev); ++ return 0; ++ ++ out_fail: ++ mpt2sas_base_unmap_resources(ioc); ++ return r; ++} ++ ++/** ++ * mpt2sas_base_get_msg_frame - obtain request mf pointer ++ * @ioc: per adapter object ++ * @smid: system request message index(smid zero is invalid) ++ * ++ * Returns virt pointer to message frame. ++ */ ++void * ++mpt2sas_base_get_msg_frame(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ return (void *)(ioc->request + (smid * ioc->request_sz)); ++} ++ ++/** ++ * mpt2sas_base_get_sense_buffer - obtain a sense buffer virt addr ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Returns virt pointer to sense buffer. ++ */ ++void * ++mpt2sas_base_get_sense_buffer(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ return (void *)(ioc->sense + ((smid - 1) * SCSI_SENSE_BUFFERSIZE)); ++} ++ ++/** ++ * mpt2sas_base_get_sense_buffer_dma - obtain a sense buffer dma addr ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Returns phys pointer to the low 32bit address of the sense buffer. ++ */ ++__le32 ++mpt2sas_base_get_sense_buffer_dma(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ return cpu_to_le32(ioc->sense_dma + ((smid - 1) * ++ SCSI_SENSE_BUFFERSIZE)); ++} ++ ++/** ++ * mpt2sas_base_get_reply_virt_addr - obtain reply frames virt address ++ * @ioc: per adapter object ++ * @phys_addr: lower 32 physical addr of the reply ++ * ++ * Converts 32bit lower physical addr into a virt address. ++ */ ++void * ++mpt2sas_base_get_reply_virt_addr(struct MPT3SAS_ADAPTER *ioc, u32 phys_addr) ++{ ++ if (!phys_addr) ++ return NULL; ++ return ioc->reply + (phys_addr - (u32)ioc->reply_dma); ++} ++ ++static inline u8 ++_base_get_msix_index(struct MPT3SAS_ADAPTER *ioc) ++{ ++ return ioc->cpu_msix_table[raw_smp_processor_id()]; ++} ++ ++/** ++ * mpt2sas_base_get_smid - obtain a free smid from internal queue ++ * @ioc: per adapter object ++ * @cb_idx: callback index ++ * ++ * Returns smid (zero is invalid) ++ */ ++u16 ++mpt2sas_base_get_smid(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx) ++{ ++ unsigned long flags; ++ struct request_tracker *request; ++ u16 smid; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ if (list_empty(&ioc->internal_free_list)) { ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ pr_err(MPT3SAS_FMT "%s: smid not available\n", ++ ioc->name, __func__); ++ return 0; ++ } ++ ++ request = list_entry(ioc->internal_free_list.next, ++ struct request_tracker, tracker_list); ++ request->cb_idx = cb_idx; ++ smid = request->smid; ++ list_del(&request->tracker_list); ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return smid; ++} ++ ++/** ++ * mpt2sas_base_get_smid_scsiio - obtain a free smid from scsiio queue ++ * @ioc: per adapter object ++ * @cb_idx: callback index ++ * @scmd: pointer to scsi command object ++ * ++ * Returns smid (zero is invalid) ++ */ ++u16 ++mpt2sas_base_get_smid_scsiio(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx, ++ struct scsi_cmnd *scmd) ++{ ++ unsigned long flags; ++ struct scsiio_tracker *request; ++ u16 smid; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ if (list_empty(&ioc->free_list)) { ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ pr_err(MPT3SAS_FMT "%s: smid not available\n", ++ ioc->name, __func__); ++ return 0; ++ } ++ ++ request = list_entry(ioc->free_list.next, ++ struct scsiio_tracker, tracker_list); ++ request->scmd = scmd; ++ request->cb_idx = cb_idx; ++ smid = request->smid; ++ request->msix_io = _base_get_msix_index(ioc); ++ list_del(&request->tracker_list); ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return smid; ++} ++ ++/** ++ * mpt2sas_base_get_smid_hpr - obtain a free smid from hi-priority queue ++ * @ioc: per adapter object ++ * @cb_idx: callback index ++ * ++ * Returns smid (zero is invalid) ++ */ ++u16 ++mpt2sas_base_get_smid_hpr(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx) ++{ ++ unsigned long flags; ++ struct request_tracker *request; ++ u16 smid; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ if (list_empty(&ioc->hpr_free_list)) { ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return 0; ++ } ++ ++ request = list_entry(ioc->hpr_free_list.next, ++ struct request_tracker, tracker_list); ++ request->cb_idx = cb_idx; ++ smid = request->smid; ++ list_del(&request->tracker_list); ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return smid; ++} ++ ++/** ++ * mpt2sas_base_free_smid - put smid back on free_list ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_free_smid(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ unsigned long flags; ++ int i; ++ struct chain_tracker *chain_req, *next; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ if (smid < ioc->hi_priority_smid) { ++ /* scsiio queue */ ++ i = smid - 1; ++ if (!list_empty(&ioc->scsi_lookup[i].chain_list)) { ++ list_for_each_entry_safe(chain_req, next, ++ &ioc->scsi_lookup[i].chain_list, tracker_list) { ++ list_del_init(&chain_req->tracker_list); ++ list_add(&chain_req->tracker_list, ++ &ioc->free_chain_list); ++ } ++ } ++ ioc->scsi_lookup[i].cb_idx = 0xFF; ++ ioc->scsi_lookup[i].scmd = NULL; ++ ioc->scsi_lookup[i].direct_io = 0; ++ list_add(&ioc->scsi_lookup[i].tracker_list, &ioc->free_list); ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ ++ /* ++ * See _wait_for_commands_to_complete() call with regards ++ * to this code. ++ */ ++ if (ioc->shost_recovery && ioc->pending_io_count) { ++ if (ioc->pending_io_count == 1) ++ wake_up(&ioc->reset_wq); ++ ioc->pending_io_count--; ++ } ++ return; ++ } else if (smid < ioc->internal_smid) { ++ /* hi-priority */ ++ i = smid - ioc->hi_priority_smid; ++ ioc->hpr_lookup[i].cb_idx = 0xFF; ++ list_add(&ioc->hpr_lookup[i].tracker_list, &ioc->hpr_free_list); ++ } else if (smid <= ioc->hba_queue_depth) { ++ /* internal queue */ ++ i = smid - ioc->internal_smid; ++ ioc->internal_lookup[i].cb_idx = 0xFF; ++ list_add(&ioc->internal_lookup[i].tracker_list, ++ &ioc->internal_free_list); ++ } ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++} ++ ++/** ++ * _base_writeq - 64 bit write to MMIO ++ * @ioc: per adapter object ++ * @b: data payload ++ * @addr: address in MMIO space ++ * @writeq_lock: spin lock ++ * ++ * Glue for handling an atomic 64 bit word to MMIO. This special handling takes ++ * care of 32 bit environment where its not quarenteed to send the entire word ++ * in one transfer. ++ */ ++#if defined(writeq) && defined(CONFIG_64BIT) ++static inline void ++_base_writeq(__u64 b, volatile void __iomem *addr, spinlock_t *writeq_lock) ++{ ++ writeq(cpu_to_le64(b), addr); ++} ++#else ++static inline void ++_base_writeq(__u64 b, volatile void __iomem *addr, spinlock_t *writeq_lock) ++{ ++ unsigned long flags; ++ __u64 data_out = cpu_to_le64(b); ++ ++ spin_lock_irqsave(writeq_lock, flags); ++ writel((u32)(data_out), addr); ++ writel((u32)(data_out >> 32), (addr + 4)); ++ spin_unlock_irqrestore(writeq_lock, flags); ++} ++#endif ++ ++/** ++ * mpt2sas_base_put_smid_scsi_io - send SCSI_IO request to firmware ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @handle: device handle ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_put_smid_scsi_io(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 handle) ++{ ++ Mpi2RequestDescriptorUnion_t descriptor; ++ u64 *request = (u64 *)&descriptor; ++ ++ ++ descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; ++ descriptor.SCSIIO.MSIxIndex = _base_get_msix_index(ioc); ++ descriptor.SCSIIO.SMID = cpu_to_le16(smid); ++ descriptor.SCSIIO.DevHandle = cpu_to_le16(handle); ++ descriptor.SCSIIO.LMID = 0; ++ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, ++ &ioc->scsi_lookup_lock); ++} ++ ++/** ++ * mpt2sas_base_put_smid_fast_path - send fast path request to firmware ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @handle: device handle ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ u16 handle) ++{ ++ Mpi2RequestDescriptorUnion_t descriptor; ++ u64 *request = (u64 *)&descriptor; ++ ++ descriptor.SCSIIO.RequestFlags = ++ MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO; ++ descriptor.SCSIIO.MSIxIndex = _base_get_msix_index(ioc); ++ descriptor.SCSIIO.SMID = cpu_to_le16(smid); ++ descriptor.SCSIIO.DevHandle = cpu_to_le16(handle); ++ descriptor.SCSIIO.LMID = 0; ++ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, ++ &ioc->scsi_lookup_lock); ++} ++ ++/** ++ * mpt2sas_base_put_smid_hi_priority - send Task Managment request to firmware ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_task: msix_task will be same as msix of IO incase of task abort else 0. ++ * Return nothing. ++ */ ++void ++mpt2sas_base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ u16 msix_task) ++{ ++ Mpi2RequestDescriptorUnion_t descriptor; ++ u64 *request = (u64 *)&descriptor; ++ ++ descriptor.HighPriority.RequestFlags = ++ MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY; ++ descriptor.HighPriority.MSIxIndex = msix_task; ++ descriptor.HighPriority.SMID = cpu_to_le16(smid); ++ descriptor.HighPriority.LMID = 0; ++ descriptor.HighPriority.Reserved1 = 0; ++ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, ++ &ioc->scsi_lookup_lock); ++} ++ ++/** ++ * mpt2sas_base_put_smid_default - Default, primarily used for config pages ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ Mpi2RequestDescriptorUnion_t descriptor; ++ u64 *request = (u64 *)&descriptor; ++ ++ descriptor.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; ++ descriptor.Default.MSIxIndex = _base_get_msix_index(ioc); ++ descriptor.Default.SMID = cpu_to_le16(smid); ++ descriptor.Default.LMID = 0; ++ descriptor.Default.DescriptorTypeDependent = 0; ++ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, ++ &ioc->scsi_lookup_lock); ++} ++ ++/** ++ * _base_display_OEMs_branding - Display branding string ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++static void ++_base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc) ++{ ++ if (ioc->pdev->subsystem_vendor != PCI_VENDOR_ID_INTEL) ++ return; ++ ++ switch (ioc->pdev->subsystem_vendor) { ++ case PCI_VENDOR_ID_INTEL: ++ switch (ioc->pdev->device) { ++ case MPI2_MFGPAGE_DEVID_SAS2008: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT2SAS_INTEL_RMS2LL080_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS2LL080_BRANDING); ++ break; ++ case MPT2SAS_INTEL_RMS2LL040_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS2LL040_BRANDING); ++ break; ++ case MPT2SAS_INTEL_SSD910_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_SSD910_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Intel(R) Controller: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ case MPI2_MFGPAGE_DEVID_SAS2308_2: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT2SAS_INTEL_RS25GB008_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RS25GB008_BRANDING); ++ break; ++ case MPT2SAS_INTEL_RMS25JB080_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS25JB080_BRANDING); ++ break; ++ case MPT2SAS_INTEL_RMS25JB040_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS25JB040_BRANDING); ++ break; ++ case MPT2SAS_INTEL_RMS25KB080_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS25KB080_BRANDING); ++ break; ++ case MPT2SAS_INTEL_RMS25KB040_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS25KB040_BRANDING); ++ break; ++ case MPT2SAS_INTEL_RMS25LB040_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS25LB040_BRANDING); ++ break; ++ case MPT2SAS_INTEL_RMS25LB080_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_INTEL_RMS25LB080_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Intel(R) Controller: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ case MPI25_MFGPAGE_DEVID_SAS3008: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT3SAS_INTEL_RMS3JC080_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_INTEL_RMS3JC080_BRANDING); ++ break; ++ ++ case MPT3SAS_INTEL_RS3GC008_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_INTEL_RS3GC008_BRANDING); ++ break; ++ case MPT3SAS_INTEL_RS3FC044_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_INTEL_RS3FC044_BRANDING); ++ break; ++ case MPT3SAS_INTEL_RS3UC080_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_INTEL_RS3UC080_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Intel(R) Controller: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Intel(R) Controller: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ case PCI_VENDOR_ID_DELL: ++ switch (ioc->pdev->device) { ++ case MPI2_MFGPAGE_DEVID_SAS2008: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT2SAS_DELL_6GBPS_SAS_HBA_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_DELL_6GBPS_SAS_HBA_BRANDING); ++ break; ++ case MPT2SAS_DELL_PERC_H200_ADAPTER_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_DELL_PERC_H200_ADAPTER_BRANDING); ++ break; ++ case MPT2SAS_DELL_PERC_H200_INTEGRATED_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_DELL_PERC_H200_INTEGRATED_BRANDING); ++ break; ++ case MPT2SAS_DELL_PERC_H200_MODULAR_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_DELL_PERC_H200_MODULAR_BRANDING); ++ break; ++ case MPT2SAS_DELL_PERC_H200_EMBEDDED_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_DELL_PERC_H200_EMBEDDED_BRANDING); ++ break; ++ case MPT2SAS_DELL_PERC_H200_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_DELL_PERC_H200_BRANDING); ++ break; ++ case MPT2SAS_DELL_6GBPS_SAS_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_DELL_6GBPS_SAS_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Dell 6Gbps HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ case MPI25_MFGPAGE_DEVID_SAS3008: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT3SAS_DELL_12G_HBA_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_DELL_12G_HBA_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Dell 12Gbps HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Dell HBA: Subsystem ID: 0x%X\n", ioc->name, ++ ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ case PCI_VENDOR_ID_CISCO: ++ switch (ioc->pdev->device) { ++ case MPI25_MFGPAGE_DEVID_SAS3008: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT3SAS_CISCO_12G_8E_HBA_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_CISCO_12G_8E_HBA_BRANDING); ++ break; ++ case MPT3SAS_CISCO_12G_8I_HBA_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_CISCO_12G_8I_HBA_BRANDING); ++ break; ++ case MPT3SAS_CISCO_12G_AVILA_HBA_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Cisco 12Gbps SAS HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ case MPI25_MFGPAGE_DEVID_SAS3108_1: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT3SAS_CISCO_12G_AVILA_HBA_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING); ++ break; ++ case MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_BRANDING ++ ); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Cisco 12Gbps SAS HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "Cisco SAS HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ break; ++ case MPT2SAS_HP_3PAR_SSVID: ++ switch (ioc->pdev->device) { ++ case MPI2_MFGPAGE_DEVID_SAS2004: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "HP 6Gbps SAS HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ case MPI2_MFGPAGE_DEVID_SAS2308_2: ++ switch (ioc->pdev->subsystem_device) { ++ case MPT2SAS_HP_2_4_INTERNAL_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_HP_2_4_INTERNAL_BRANDING); ++ break; ++ case MPT2SAS_HP_2_4_EXTERNAL_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_HP_2_4_EXTERNAL_BRANDING); ++ break; ++ case MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING); ++ break; ++ case MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID: ++ pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING); ++ break; ++ default: ++ pr_info(MPT3SAS_FMT ++ "HP 6Gbps SAS HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ default: ++ pr_info(MPT3SAS_FMT ++ "HP SAS HBA: Subsystem ID: 0x%X\n", ++ ioc->name, ioc->pdev->subsystem_device); ++ break; ++ } ++ default: ++ break; ++ } ++} ++ ++/** ++ * _base_display_ioc_capabilities - Disply IOC's capabilities. ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++static void ++_base_display_ioc_capabilities(struct MPT3SAS_ADAPTER *ioc) ++{ ++ int i = 0; ++ char desc[16]; ++ u32 iounit_pg1_flags; ++ u32 bios_version; ++ ++ bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion); ++ strncpy(desc, ioc->manu_pg0.ChipName, 16); ++ pr_info(MPT3SAS_FMT "%s: FWVersion(%02d.%02d.%02d.%02d), "\ ++ "ChipRevision(0x%02x), BiosVersion(%02d.%02d.%02d.%02d)\n", ++ ioc->name, desc, ++ (ioc->facts.FWVersion.Word & 0xFF000000) >> 24, ++ (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16, ++ (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8, ++ ioc->facts.FWVersion.Word & 0x000000FF, ++ ioc->pdev->revision, ++ (bios_version & 0xFF000000) >> 24, ++ (bios_version & 0x00FF0000) >> 16, ++ (bios_version & 0x0000FF00) >> 8, ++ bios_version & 0x000000FF); ++ ++ _base_display_OEMs_branding(ioc); ++ ++ pr_info(MPT3SAS_FMT "Protocol=(", ioc->name); ++ ++ if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR) { ++ pr_info("Initiator"); ++ i++; ++ } ++ ++ if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET) { ++ pr_info("%sTarget", i ? "," : ""); ++ i++; ++ } ++ ++ i = 0; ++ pr_info("), "); ++ pr_info("Capabilities=("); ++ ++ if (!ioc->hide_ir_msg) { ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) { ++ pr_info("Raid"); ++ i++; ++ } ++ } ++ ++ if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR) { ++ pr_info("%sTLR", i ? "," : ""); ++ i++; ++ } ++ ++ if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_MULTICAST) { ++ pr_info("%sMulticast", i ? "," : ""); ++ i++; ++ } ++ ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET) { ++ pr_info("%sBIDI Target", i ? "," : ""); ++ i++; ++ } ++ ++ if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_EEDP) { ++ pr_info("%sEEDP", i ? "," : ""); ++ i++; ++ } ++ ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) { ++ pr_info("%sSnapshot Buffer", i ? "," : ""); ++ i++; ++ } ++ ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) { ++ pr_info("%sDiag Trace Buffer", i ? "," : ""); ++ i++; ++ } ++ ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) { ++ pr_info("%sDiag Extended Buffer", i ? "," : ""); ++ i++; ++ } ++ ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING) { ++ pr_info("%sTask Set Full", i ? "," : ""); ++ i++; ++ } ++ ++ iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags); ++ if (!(iounit_pg1_flags & MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE)) { ++ pr_info("%sNCQ", i ? "," : ""); ++ i++; ++ } ++ ++ pr_info(")\n"); ++} ++ ++/** ++ * mpt2sas_base_update_missing_delay - change the missing delay timers ++ * @ioc: per adapter object ++ * @device_missing_delay: amount of time till device is reported missing ++ * @io_missing_delay: interval IO is returned when there is a missing device ++ * ++ * Return nothing. ++ * ++ * Passed on the command line, this function will modify the device missing ++ * delay, as well as the io missing delay. This should be called at driver ++ * load time. ++ */ ++void ++mpt2sas_base_update_missing_delay(struct MPT3SAS_ADAPTER *ioc, ++ u16 device_missing_delay, u8 io_missing_delay) ++{ ++ u16 dmd, dmd_new, dmd_orignal; ++ u8 io_missing_delay_original; ++ u16 sz; ++ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; ++ Mpi2ConfigReply_t mpi_reply; ++ u8 num_phys = 0; ++ u16 ioc_status; ++ ++ mpt2sas_config_get_number_hba_phys(ioc, &num_phys); ++ if (!num_phys) ++ return; ++ ++ sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (num_phys * ++ sizeof(Mpi2SasIOUnit1PhyData_t)); ++ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); ++ if (!sas_iounit_pg1) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, ++ sas_iounit_pg1, sz))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ++ /* device missing delay */ ++ dmd = sas_iounit_pg1->ReportDeviceMissingDelay; ++ if (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16) ++ dmd = (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16; ++ else ++ dmd = dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK; ++ dmd_orignal = dmd; ++ if (device_missing_delay > 0x7F) { ++ dmd = (device_missing_delay > 0x7F0) ? 0x7F0 : ++ device_missing_delay; ++ dmd = dmd / 16; ++ dmd |= MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16; ++ } else ++ dmd = device_missing_delay; ++ sas_iounit_pg1->ReportDeviceMissingDelay = dmd; ++ ++ /* io missing delay */ ++ io_missing_delay_original = sas_iounit_pg1->IODeviceMissingDelay; ++ sas_iounit_pg1->IODeviceMissingDelay = io_missing_delay; ++ ++ if (!mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, ++ sz)) { ++ if (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16) ++ dmd_new = (dmd & ++ MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16; ++ else ++ dmd_new = ++ dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK; ++ pr_info(MPT3SAS_FMT "device_missing_delay: old(%d), new(%d)\n", ++ ioc->name, dmd_orignal, dmd_new); ++ pr_info(MPT3SAS_FMT "ioc_missing_delay: old(%d), new(%d)\n", ++ ioc->name, io_missing_delay_original, ++ io_missing_delay); ++ ioc->device_missing_delay = dmd_new; ++ ioc->io_missing_delay = io_missing_delay; ++ } ++ ++out: ++ kfree(sas_iounit_pg1); ++} ++/** ++ * _base_static_config_pages - static start of day config pages ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++static void ++_base_static_config_pages(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2ConfigReply_t mpi_reply; ++ u32 iounit_pg1_flags; ++ ++ mpt2sas_config_get_manufacturing_pg0(ioc, &mpi_reply, &ioc->manu_pg0); ++ if (ioc->ir_firmware) ++ mpt2sas_config_get_manufacturing_pg10(ioc, &mpi_reply, ++ &ioc->manu_pg10); ++ ++ /* ++ * Ensure correct T10 PI operation if vendor left EEDPTagMode ++ * flag unset in NVDATA. ++ */ ++ mpt2sas_config_get_manufacturing_pg11(ioc, &mpi_reply, &ioc->manu_pg11); ++ if (ioc->manu_pg11.EEDPTagMode == 0) { ++ pr_err("%s: overriding NVDATA EEDPTagMode setting\n", ++ ioc->name); ++ ioc->manu_pg11.EEDPTagMode &= ~0x3; ++ ioc->manu_pg11.EEDPTagMode |= 0x1; ++ mpt2sas_config_set_manufacturing_pg11(ioc, &mpi_reply, ++ &ioc->manu_pg11); ++ } ++ ++ mpt2sas_config_get_bios_pg2(ioc, &mpi_reply, &ioc->bios_pg2); ++ mpt2sas_config_get_bios_pg3(ioc, &mpi_reply, &ioc->bios_pg3); ++ mpt2sas_config_get_ioc_pg8(ioc, &mpi_reply, &ioc->ioc_pg8); ++ mpt2sas_config_get_iounit_pg0(ioc, &mpi_reply, &ioc->iounit_pg0); ++ mpt2sas_config_get_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1); ++ mpt2sas_config_get_iounit_pg8(ioc, &mpi_reply, &ioc->iounit_pg8); ++ _base_display_ioc_capabilities(ioc); ++ ++ /* ++ * Enable task_set_full handling in iounit_pg1 when the ++ * facts capabilities indicate that its supported. ++ */ ++ iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags); ++ if ((ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING)) ++ iounit_pg1_flags &= ++ ~MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING; ++ else ++ iounit_pg1_flags |= ++ MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING; ++ ioc->iounit_pg1.Flags = cpu_to_le32(iounit_pg1_flags); ++ mpt2sas_config_set_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1); ++ ++ if (ioc->iounit_pg8.NumSensors) ++ ioc->temp_sensors_count = ioc->iounit_pg8.NumSensors; ++} ++ ++/** ++ * _base_release_memory_pools - release memory ++ * @ioc: per adapter object ++ * ++ * Free memory allocated from _base_allocate_memory_pools. ++ * ++ * Return nothing. ++ */ ++static void ++_base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc) ++{ ++ int i = 0; ++ struct reply_post_struct *rps; ++ ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ if (ioc->request) { ++ pci_free_consistent(ioc->pdev, ioc->request_dma_sz, ++ ioc->request, ioc->request_dma); ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT ++ "request_pool(0x%p): free\n", ++ ioc->name, ioc->request)); ++ ioc->request = NULL; ++ } ++ ++ if (ioc->sense) { ++ pci_pool_free(ioc->sense_dma_pool, ioc->sense, ioc->sense_dma); ++ if (ioc->sense_dma_pool) ++ pci_pool_destroy(ioc->sense_dma_pool); ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT ++ "sense_pool(0x%p): free\n", ++ ioc->name, ioc->sense)); ++ ioc->sense = NULL; ++ } ++ ++ if (ioc->reply) { ++ pci_pool_free(ioc->reply_dma_pool, ioc->reply, ioc->reply_dma); ++ if (ioc->reply_dma_pool) ++ pci_pool_destroy(ioc->reply_dma_pool); ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT ++ "reply_pool(0x%p): free\n", ++ ioc->name, ioc->reply)); ++ ioc->reply = NULL; ++ } ++ ++ if (ioc->reply_free) { ++ pci_pool_free(ioc->reply_free_dma_pool, ioc->reply_free, ++ ioc->reply_free_dma); ++ if (ioc->reply_free_dma_pool) ++ pci_pool_destroy(ioc->reply_free_dma_pool); ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT ++ "reply_free_pool(0x%p): free\n", ++ ioc->name, ioc->reply_free)); ++ ioc->reply_free = NULL; ++ } ++ ++ if (ioc->reply_post) { ++ do { ++ rps = &ioc->reply_post[i]; ++ if (rps->reply_post_free) { ++ pci_pool_free( ++ ioc->reply_post_free_dma_pool, ++ rps->reply_post_free, ++ rps->reply_post_free_dma); ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT ++ "reply_post_free_pool(0x%p): free\n", ++ ioc->name, rps->reply_post_free)); ++ rps->reply_post_free = NULL; ++ } ++ } while (ioc->rdpq_array_enable && ++ (++i < ioc->reply_queue_count)); ++ ++ if (ioc->reply_post_free_dma_pool) ++ pci_pool_destroy(ioc->reply_post_free_dma_pool); ++ kfree(ioc->reply_post); ++ } ++ ++ if (ioc->config_page) { ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT ++ "config_page(0x%p): free\n", ioc->name, ++ ioc->config_page)); ++ pci_free_consistent(ioc->pdev, ioc->config_page_sz, ++ ioc->config_page, ioc->config_page_dma); ++ } ++ ++ if (ioc->scsi_lookup) { ++ free_pages((ulong)ioc->scsi_lookup, ioc->scsi_lookup_pages); ++ ioc->scsi_lookup = NULL; ++ } ++ kfree(ioc->hpr_lookup); ++ kfree(ioc->internal_lookup); ++ if (ioc->chain_lookup) { ++ for (i = 0; i < ioc->chain_depth; i++) { ++ if (ioc->chain_lookup[i].chain_buffer) ++ pci_pool_free(ioc->chain_dma_pool, ++ ioc->chain_lookup[i].chain_buffer, ++ ioc->chain_lookup[i].chain_buffer_dma); ++ } ++ if (ioc->chain_dma_pool) ++ pci_pool_destroy(ioc->chain_dma_pool); ++ free_pages((ulong)ioc->chain_lookup, ioc->chain_pages); ++ ioc->chain_lookup = NULL; ++ } ++} ++ ++/** ++ * _base_allocate_memory_pools - allocate start of day memory pools ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 success, anything else error ++ */ ++static int ++_base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ struct mpt2sas_facts *facts; ++ u16 max_sge_elements; ++ u16 chains_needed_per_io; ++ u32 sz, total_sz, reply_post_free_sz; ++ u32 retry_sz; ++ u16 max_request_credit; ++ unsigned short sg_tablesize; ++ u16 sge_size; ++ int i; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ ++ retry_sz = 0; ++ facts = &ioc->facts; ++ ++ /* command line tunables for max sgl entries */ ++ if (max_sgl_entries != -1) ++ sg_tablesize = max_sgl_entries; ++ else { ++ if (ioc->hba_mpi_version_belonged == MPI2_VERSION) ++ sg_tablesize = MPT2SAS_SG_DEPTH; ++ else ++ sg_tablesize = MPT3SAS_SG_DEPTH; ++ } ++ ++ if (sg_tablesize < MPT_MIN_PHYS_SEGMENTS) ++ sg_tablesize = MPT_MIN_PHYS_SEGMENTS; ++ else if (sg_tablesize > MPT_MAX_PHYS_SEGMENTS) { ++ sg_tablesize = min_t(unsigned short, sg_tablesize, ++ SCSI_MAX_SG_CHAIN_SEGMENTS); ++ pr_warn(MPT3SAS_FMT ++ "sg_tablesize(%u) is bigger than kernel" ++ " defined SCSI_MAX_SG_SEGMENTS(%u)\n", ioc->name, ++ sg_tablesize, MPT_MAX_PHYS_SEGMENTS); ++ } ++ ioc->shost->sg_tablesize = sg_tablesize; ++ ++ ioc->internal_depth = min_t(int, (facts->HighPriorityCredit + (5)), ++ (facts->RequestCredit / 4)); ++ if (ioc->internal_depth < INTERNAL_CMDS_COUNT) { ++ if (facts->RequestCredit <= (INTERNAL_CMDS_COUNT + ++ INTERNAL_SCSIIO_CMDS_COUNT)) { ++ pr_err(MPT3SAS_FMT "IOC doesn't have enough Request \ ++ Credits, it has just %d number of credits\n", ++ ioc->name, facts->RequestCredit); ++ return -ENOMEM; ++ } ++ ioc->internal_depth = 10; ++ } ++ ++ ioc->hi_priority_depth = ioc->internal_depth - (5); ++ /* command line tunables for max controller queue depth */ ++ if (max_queue_depth != -1 && max_queue_depth != 0) { ++ max_request_credit = min_t(u16, max_queue_depth + ++ ioc->internal_depth, facts->RequestCredit); ++ if (max_request_credit > MAX_HBA_QUEUE_DEPTH) ++ max_request_credit = MAX_HBA_QUEUE_DEPTH; ++ } else ++ max_request_credit = min_t(u16, facts->RequestCredit, ++ MAX_HBA_QUEUE_DEPTH); ++ ++ /* Firmware maintains additional facts->HighPriorityCredit number of ++ * credits for HiPriprity Request messages, so hba queue depth will be ++ * sum of max_request_credit and high priority queue depth. ++ */ ++ ioc->hba_queue_depth = max_request_credit + ioc->hi_priority_depth; ++ ++ /* request frame size */ ++ ioc->request_sz = facts->IOCRequestFrameSize * 4; ++ ++ /* reply frame size */ ++ ioc->reply_sz = facts->ReplyFrameSize * 4; ++ ++ /* chain segment size */ ++ if (ioc->hba_mpi_version_belonged != MPI2_VERSION) { ++ if (facts->IOCMaxChainSegmentSize) ++ ioc->chain_segment_sz = ++ facts->IOCMaxChainSegmentSize * ++ MAX_CHAIN_ELEMT_SZ; ++ else ++ /* set to 128 bytes size if IOCMaxChainSegmentSize is zero */ ++ ioc->chain_segment_sz = DEFAULT_NUM_FWCHAIN_ELEMTS * ++ MAX_CHAIN_ELEMT_SZ; ++ } else ++ ioc->chain_segment_sz = ioc->request_sz; ++ ++ /* calculate the max scatter element size */ ++ sge_size = max_t(u16, ioc->sge_size, ioc->sge_size_ieee); ++ ++ retry_allocation: ++ total_sz = 0; ++ /* calculate number of sg elements left over in the 1st frame */ ++ max_sge_elements = ioc->request_sz - ((sizeof(Mpi2SCSIIORequest_t) - ++ sizeof(Mpi2SGEIOUnion_t)) + sge_size); ++ ioc->max_sges_in_main_message = max_sge_elements/sge_size; ++ ++ /* now do the same for a chain buffer */ ++ max_sge_elements = ioc->chain_segment_sz - sge_size; ++ ioc->max_sges_in_chain_message = max_sge_elements/sge_size; ++ ++ /* ++ * MPT3SAS_SG_DEPTH = CONFIG_FUSION_MAX_SGE ++ */ ++ chains_needed_per_io = ((ioc->shost->sg_tablesize - ++ ioc->max_sges_in_main_message)/ioc->max_sges_in_chain_message) ++ + 1; ++ if (chains_needed_per_io > facts->MaxChainDepth) { ++ chains_needed_per_io = facts->MaxChainDepth; ++ ioc->shost->sg_tablesize = min_t(u16, ++ ioc->max_sges_in_main_message + (ioc->max_sges_in_chain_message ++ * chains_needed_per_io), ioc->shost->sg_tablesize); ++ } ++ ioc->chains_needed_per_io = chains_needed_per_io; ++ ++ /* reply free queue sizing - taking into account for 64 FW events */ ++ ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64; ++ ++ /* calculate reply descriptor post queue depth */ ++ ioc->reply_post_queue_depth = ioc->hba_queue_depth + ++ ioc->reply_free_queue_depth + 1 ; ++ /* align the reply post queue on the next 16 count boundary */ ++ if (ioc->reply_post_queue_depth % 16) ++ ioc->reply_post_queue_depth += 16 - ++ (ioc->reply_post_queue_depth % 16); ++ ++ if (ioc->reply_post_queue_depth > ++ facts->MaxReplyDescriptorPostQueueDepth) { ++ ioc->reply_post_queue_depth = ++ facts->MaxReplyDescriptorPostQueueDepth - ++ (facts->MaxReplyDescriptorPostQueueDepth % 16); ++ ioc->hba_queue_depth = ++ ((ioc->reply_post_queue_depth - 64) / 2) - 1; ++ ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64; ++ } ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "scatter gather: " \ ++ "sge_in_main_msg(%d), sge_per_chain(%d), sge_per_io(%d), " ++ "chains_per_io(%d)\n", ioc->name, ioc->max_sges_in_main_message, ++ ioc->max_sges_in_chain_message, ioc->shost->sg_tablesize, ++ ioc->chains_needed_per_io)); ++ ++ /* reply post queue, 16 byte align */ ++ reply_post_free_sz = ioc->reply_post_queue_depth * ++ sizeof(Mpi2DefaultReplyDescriptor_t); ++ ++ sz = reply_post_free_sz; ++ if (_base_is_controller_msix_enabled(ioc) && !ioc->rdpq_array_enable) ++ sz *= ioc->reply_queue_count; ++ ++ ioc->reply_post = kcalloc((ioc->rdpq_array_enable) ? ++ (ioc->reply_queue_count):1, ++ sizeof(struct reply_post_struct), GFP_KERNEL); ++ ++ if (!ioc->reply_post) { ++ pr_err(MPT3SAS_FMT "reply_post_free pool: kcalloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->reply_post_free_dma_pool = pci_pool_create("reply_post_free pool", ++ ioc->pdev, sz, 16, 0); ++ if (!ioc->reply_post_free_dma_pool) { ++ pr_err(MPT3SAS_FMT ++ "reply_post_free pool: pci_pool_create failed\n", ++ ioc->name); ++ goto out; ++ } ++ i = 0; ++ do { ++ ioc->reply_post[i].reply_post_free = ++ pci_pool_alloc(ioc->reply_post_free_dma_pool, ++ GFP_KERNEL, ++ &ioc->reply_post[i].reply_post_free_dma); ++ if (!ioc->reply_post[i].reply_post_free) { ++ pr_err(MPT3SAS_FMT ++ "reply_post_free pool: pci_pool_alloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ memset(ioc->reply_post[i].reply_post_free, 0, sz); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "reply post free pool (0x%p): depth(%d)," ++ "element_size(%d), pool_size(%d kB)\n", ioc->name, ++ ioc->reply_post[i].reply_post_free, ++ ioc->reply_post_queue_depth, 8, sz/1024)); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "reply_post_free_dma = (0x%llx)\n", ioc->name, ++ (unsigned long long) ++ ioc->reply_post[i].reply_post_free_dma)); ++ total_sz += sz; ++ } while (ioc->rdpq_array_enable && (++i < ioc->reply_queue_count)); ++ ++ if (ioc->dma_mask == 64) { ++ if (_base_change_consistent_dma_mask(ioc, ioc->pdev) != 0) { ++ pr_warn(MPT3SAS_FMT ++ "no suitable consistent DMA mask for %s\n", ++ ioc->name, pci_name(ioc->pdev)); ++ goto out; ++ } ++ } ++ ++ ioc->scsiio_depth = ioc->hba_queue_depth - ++ ioc->hi_priority_depth - ioc->internal_depth; ++ ++ /* set the scsi host can_queue depth ++ * with some internal commands that could be outstanding ++ */ ++ ioc->shost->can_queue = ioc->scsiio_depth - INTERNAL_SCSIIO_CMDS_COUNT; ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "scsi host: can_queue depth (%d)\n", ++ ioc->name, ioc->shost->can_queue)); ++ ++ ++ /* contiguous pool for request and chains, 16 byte align, one extra " ++ * "frame for smid=0 ++ */ ++ ioc->chain_depth = ioc->chains_needed_per_io * ioc->scsiio_depth; ++ sz = ((ioc->scsiio_depth + 1) * ioc->request_sz); ++ ++ /* hi-priority queue */ ++ sz += (ioc->hi_priority_depth * ioc->request_sz); ++ ++ /* internal queue */ ++ sz += (ioc->internal_depth * ioc->request_sz); ++ ++ ioc->request_dma_sz = sz; ++ ioc->request = pci_alloc_consistent(ioc->pdev, sz, &ioc->request_dma); ++ if (!ioc->request) { ++ pr_err(MPT3SAS_FMT "request pool: pci_alloc_consistent " \ ++ "failed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), " ++ "total(%d kB)\n", ioc->name, ioc->hba_queue_depth, ++ ioc->chains_needed_per_io, ioc->request_sz, sz/1024); ++ if (ioc->scsiio_depth < MPT3SAS_SAS_QUEUE_DEPTH) ++ goto out; ++ retry_sz = 64; ++ ioc->hba_queue_depth -= retry_sz; ++ _base_release_memory_pools(ioc); ++ goto retry_allocation; ++ } ++ ++ if (retry_sz) ++ pr_err(MPT3SAS_FMT "request pool: pci_alloc_consistent " \ ++ "succeed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), " ++ "total(%d kb)\n", ioc->name, ioc->hba_queue_depth, ++ ioc->chains_needed_per_io, ioc->request_sz, sz/1024); ++ ++ /* hi-priority queue */ ++ ioc->hi_priority = ioc->request + ((ioc->scsiio_depth + 1) * ++ ioc->request_sz); ++ ioc->hi_priority_dma = ioc->request_dma + ((ioc->scsiio_depth + 1) * ++ ioc->request_sz); ++ ++ /* internal queue */ ++ ioc->internal = ioc->hi_priority + (ioc->hi_priority_depth * ++ ioc->request_sz); ++ ioc->internal_dma = ioc->hi_priority_dma + (ioc->hi_priority_depth * ++ ioc->request_sz); ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "request pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB)\n", ++ ioc->name, ioc->request, ioc->hba_queue_depth, ioc->request_sz, ++ (ioc->hba_queue_depth * ioc->request_sz)/1024)); ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "request pool: dma(0x%llx)\n", ++ ioc->name, (unsigned long long) ioc->request_dma)); ++ total_sz += sz; ++ ++ sz = ioc->scsiio_depth * sizeof(struct scsiio_tracker); ++ ioc->scsi_lookup_pages = get_order(sz); ++ ioc->scsi_lookup = (struct scsiio_tracker *)__get_free_pages( ++ GFP_KERNEL, ioc->scsi_lookup_pages); ++ if (!ioc->scsi_lookup) { ++ pr_err(MPT3SAS_FMT "scsi_lookup: get_free_pages failed, sz(%d)\n", ++ ioc->name, (int)sz); ++ goto out; ++ } ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "scsiio(0x%p): depth(%d)\n", ++ ioc->name, ioc->request, ioc->scsiio_depth)); ++ ++ ioc->chain_depth = min_t(u32, ioc->chain_depth, MAX_CHAIN_DEPTH); ++ sz = ioc->chain_depth * sizeof(struct chain_tracker); ++ ioc->chain_pages = get_order(sz); ++ ioc->chain_lookup = (struct chain_tracker *)__get_free_pages( ++ GFP_KERNEL, ioc->chain_pages); ++ if (!ioc->chain_lookup) { ++ pr_err(MPT3SAS_FMT "chain_lookup: __get_free_pages failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->chain_dma_pool = pci_pool_create("chain pool", ioc->pdev, ++ ioc->chain_segment_sz, 16, 0); ++ if (!ioc->chain_dma_pool) { ++ pr_err(MPT3SAS_FMT "chain_dma_pool: pci_pool_create failed\n", ++ ioc->name); ++ goto out; ++ } ++ for (i = 0; i < ioc->chain_depth; i++) { ++ ioc->chain_lookup[i].chain_buffer = pci_pool_alloc( ++ ioc->chain_dma_pool , GFP_KERNEL, ++ &ioc->chain_lookup[i].chain_buffer_dma); ++ if (!ioc->chain_lookup[i].chain_buffer) { ++ ioc->chain_depth = i; ++ goto chain_done; ++ } ++ total_sz += ioc->chain_segment_sz; ++ } ++ chain_done: ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "chain pool depth(%d), frame_size(%d), pool_size(%d kB)\n", ++ ioc->name, ioc->chain_depth, ioc->chain_segment_sz, ++ ((ioc->chain_depth * ioc->chain_segment_sz))/1024)); ++ ++ /* initialize hi-priority queue smid's */ ++ ioc->hpr_lookup = kcalloc(ioc->hi_priority_depth, ++ sizeof(struct request_tracker), GFP_KERNEL); ++ if (!ioc->hpr_lookup) { ++ pr_err(MPT3SAS_FMT "hpr_lookup: kcalloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->hi_priority_smid = ioc->scsiio_depth + 1; ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "hi_priority(0x%p): depth(%d), start smid(%d)\n", ++ ioc->name, ioc->hi_priority, ++ ioc->hi_priority_depth, ioc->hi_priority_smid)); ++ ++ /* initialize internal queue smid's */ ++ ioc->internal_lookup = kcalloc(ioc->internal_depth, ++ sizeof(struct request_tracker), GFP_KERNEL); ++ if (!ioc->internal_lookup) { ++ pr_err(MPT3SAS_FMT "internal_lookup: kcalloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->internal_smid = ioc->hi_priority_smid + ioc->hi_priority_depth; ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "internal(0x%p): depth(%d), start smid(%d)\n", ++ ioc->name, ioc->internal, ++ ioc->internal_depth, ioc->internal_smid)); ++ ++ /* sense buffers, 4 byte align */ ++ sz = ioc->scsiio_depth * SCSI_SENSE_BUFFERSIZE; ++ ioc->sense_dma_pool = pci_pool_create("sense pool", ioc->pdev, sz, 4, ++ 0); ++ if (!ioc->sense_dma_pool) { ++ pr_err(MPT3SAS_FMT "sense pool: pci_pool_create failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->sense = pci_pool_alloc(ioc->sense_dma_pool , GFP_KERNEL, ++ &ioc->sense_dma); ++ if (!ioc->sense) { ++ pr_err(MPT3SAS_FMT "sense pool: pci_pool_alloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "sense pool(0x%p): depth(%d), element_size(%d), pool_size" ++ "(%d kB)\n", ioc->name, ioc->sense, ioc->scsiio_depth, ++ SCSI_SENSE_BUFFERSIZE, sz/1024)); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "sense_dma(0x%llx)\n", ++ ioc->name, (unsigned long long)ioc->sense_dma)); ++ total_sz += sz; ++ ++ /* reply pool, 4 byte align */ ++ sz = ioc->reply_free_queue_depth * ioc->reply_sz; ++ ioc->reply_dma_pool = pci_pool_create("reply pool", ioc->pdev, sz, 4, ++ 0); ++ if (!ioc->reply_dma_pool) { ++ pr_err(MPT3SAS_FMT "reply pool: pci_pool_create failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->reply = pci_pool_alloc(ioc->reply_dma_pool , GFP_KERNEL, ++ &ioc->reply_dma); ++ if (!ioc->reply) { ++ pr_err(MPT3SAS_FMT "reply pool: pci_pool_alloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->reply_dma_min_address = (u32)(ioc->reply_dma); ++ ioc->reply_dma_max_address = (u32)(ioc->reply_dma) + sz; ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "reply pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB)\n", ++ ioc->name, ioc->reply, ++ ioc->reply_free_queue_depth, ioc->reply_sz, sz/1024)); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "reply_dma(0x%llx)\n", ++ ioc->name, (unsigned long long)ioc->reply_dma)); ++ total_sz += sz; ++ ++ /* reply free queue, 16 byte align */ ++ sz = ioc->reply_free_queue_depth * 4; ++ ioc->reply_free_dma_pool = pci_pool_create("reply_free pool", ++ ioc->pdev, sz, 16, 0); ++ if (!ioc->reply_free_dma_pool) { ++ pr_err(MPT3SAS_FMT "reply_free pool: pci_pool_create failed\n", ++ ioc->name); ++ goto out; ++ } ++ ioc->reply_free = pci_pool_alloc(ioc->reply_free_dma_pool , GFP_KERNEL, ++ &ioc->reply_free_dma); ++ if (!ioc->reply_free) { ++ pr_err(MPT3SAS_FMT "reply_free pool: pci_pool_alloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ memset(ioc->reply_free, 0, sz); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "reply_free pool(0x%p): " \ ++ "depth(%d), element_size(%d), pool_size(%d kB)\n", ioc->name, ++ ioc->reply_free, ioc->reply_free_queue_depth, 4, sz/1024)); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "reply_free_dma (0x%llx)\n", ++ ioc->name, (unsigned long long)ioc->reply_free_dma)); ++ total_sz += sz; ++ ++ ioc->config_page_sz = 512; ++ ioc->config_page = pci_alloc_consistent(ioc->pdev, ++ ioc->config_page_sz, &ioc->config_page_dma); ++ if (!ioc->config_page) { ++ pr_err(MPT3SAS_FMT ++ "config page: pci_pool_alloc failed\n", ++ ioc->name); ++ goto out; ++ } ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "config page(0x%p): size(%d)\n", ++ ioc->name, ioc->config_page, ioc->config_page_sz)); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "config_page_dma(0x%llx)\n", ++ ioc->name, (unsigned long long)ioc->config_page_dma)); ++ total_sz += ioc->config_page_sz; ++ ++ pr_info(MPT3SAS_FMT "Allocated physical memory: size(%d kB)\n", ++ ioc->name, total_sz/1024); ++ pr_info(MPT3SAS_FMT ++ "Current Controller Queue Depth(%d),Max Controller Queue Depth(%d)\n", ++ ioc->name, ioc->shost->can_queue, facts->RequestCredit); ++ pr_info(MPT3SAS_FMT "Scatter Gather Elements per IO(%d)\n", ++ ioc->name, ioc->shost->sg_tablesize); ++ return 0; ++ ++ out: ++ return -ENOMEM; ++} ++ ++/** ++ * mpt2sas_base_get_iocstate - Get the current state of a MPT adapter. ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @cooked: Request raw or cooked IOC state ++ * ++ * Returns all IOC Doorbell register bits if cooked==0, else just the ++ * Doorbell bits in MPI_IOC_STATE_MASK. ++ */ ++u32 ++mpt2sas_base_get_iocstate(struct MPT3SAS_ADAPTER *ioc, int cooked) ++{ ++ u32 s, sc; ++ ++ s = readl(&ioc->chip->Doorbell); ++ sc = s & MPI2_IOC_STATE_MASK; ++ return cooked ? sc : s; ++} ++ ++/** ++ * _base_wait_on_iocstate - waiting on a particular ioc state ++ * @ioc_state: controller state { READY, OPERATIONAL, or RESET } ++ * @timeout: timeout in second ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout, ++ int sleep_flag) ++{ ++ u32 count, cntdn; ++ u32 current_state; ++ ++ count = 0; ++ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; ++ do { ++ current_state = mpt2sas_base_get_iocstate(ioc, 1); ++ if (current_state == ioc_state) ++ return 0; ++ if (count && current_state == MPI2_IOC_STATE_FAULT) ++ break; ++ if (sleep_flag == CAN_SLEEP) ++ usleep_range(1000, 1500); ++ else ++ udelay(500); ++ count++; ++ } while (--cntdn); ++ ++ return current_state; ++} ++ ++/** ++ * _base_wait_for_doorbell_int - waiting for controller interrupt(generated by ++ * a write to the doorbell) ++ * @ioc: per adapter object ++ * @timeout: timeout in second ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ * ++ * Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell. ++ */ ++static int ++_base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag); ++ ++static int ++_base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout, ++ int sleep_flag) ++{ ++ u32 cntdn, count; ++ u32 int_status; ++ ++ count = 0; ++ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; ++ do { ++ int_status = readl(&ioc->chip->HostInterruptStatus); ++ if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { ++ dhsprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: successful count(%d), timeout(%d)\n", ++ ioc->name, __func__, count, timeout)); ++ return 0; ++ } ++ if (sleep_flag == CAN_SLEEP) ++ usleep_range(1000, 1500); ++ else ++ udelay(500); ++ count++; ++ } while (--cntdn); ++ ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to timeout count(%d), int_status(%x)!\n", ++ ioc->name, __func__, count, int_status); ++ return -EFAULT; ++} ++ ++/** ++ * _base_wait_for_doorbell_ack - waiting for controller to read the doorbell. ++ * @ioc: per adapter object ++ * @timeout: timeout in second ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ * ++ * Notes: MPI2_HIS_SYS2IOC_DB_STATUS - set to one when host writes to ++ * doorbell. ++ */ ++static int ++_base_wait_for_doorbell_ack(struct MPT3SAS_ADAPTER *ioc, int timeout, ++ int sleep_flag) ++{ ++ u32 cntdn, count; ++ u32 int_status; ++ u32 doorbell; ++ ++ count = 0; ++ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; ++ do { ++ int_status = readl(&ioc->chip->HostInterruptStatus); ++ if (!(int_status & MPI2_HIS_SYS2IOC_DB_STATUS)) { ++ dhsprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: successful count(%d), timeout(%d)\n", ++ ioc->name, __func__, count, timeout)); ++ return 0; ++ } else if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { ++ doorbell = readl(&ioc->chip->Doorbell); ++ if ((doorbell & MPI2_IOC_STATE_MASK) == ++ MPI2_IOC_STATE_FAULT) { ++ mpt2sas_base_fault_info(ioc , doorbell); ++ return -EFAULT; ++ } ++ } else if (int_status == 0xFFFFFFFF) ++ goto out; ++ ++ if (sleep_flag == CAN_SLEEP) ++ usleep_range(1000, 1500); ++ else ++ udelay(500); ++ count++; ++ } while (--cntdn); ++ ++ out: ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to timeout count(%d), int_status(%x)!\n", ++ ioc->name, __func__, count, int_status); ++ return -EFAULT; ++} ++ ++/** ++ * _base_wait_for_doorbell_not_used - waiting for doorbell to not be in use ++ * @ioc: per adapter object ++ * @timeout: timeout in second ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ * ++ */ ++static int ++_base_wait_for_doorbell_not_used(struct MPT3SAS_ADAPTER *ioc, int timeout, ++ int sleep_flag) ++{ ++ u32 cntdn, count; ++ u32 doorbell_reg; ++ ++ count = 0; ++ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; ++ do { ++ doorbell_reg = readl(&ioc->chip->Doorbell); ++ if (!(doorbell_reg & MPI2_DOORBELL_USED)) { ++ dhsprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: successful count(%d), timeout(%d)\n", ++ ioc->name, __func__, count, timeout)); ++ return 0; ++ } ++ if (sleep_flag == CAN_SLEEP) ++ usleep_range(1000, 1500); ++ else ++ udelay(500); ++ count++; ++ } while (--cntdn); ++ ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to timeout count(%d), doorbell_reg(%x)!\n", ++ ioc->name, __func__, count, doorbell_reg); ++ return -EFAULT; ++} ++ ++/** ++ * _base_send_ioc_reset - send doorbell reset ++ * @ioc: per adapter object ++ * @reset_type: currently only supports: MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET ++ * @timeout: timeout in second ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout, ++ int sleep_flag) ++{ ++ u32 ioc_state; ++ int r = 0; ++ ++ if (reset_type != MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET) { ++ pr_err(MPT3SAS_FMT "%s: unknown reset_type\n", ++ ioc->name, __func__); ++ return -EFAULT; ++ } ++ ++ if (!(ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY)) ++ return -EFAULT; ++ ++ pr_info(MPT3SAS_FMT "sending message unit reset !!\n", ioc->name); ++ ++ writel(reset_type << MPI2_DOORBELL_FUNCTION_SHIFT, ++ &ioc->chip->Doorbell); ++ if ((_base_wait_for_doorbell_ack(ioc, 15, sleep_flag))) { ++ r = -EFAULT; ++ goto out; ++ } ++ ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, ++ timeout, sleep_flag); ++ if (ioc_state) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed going to ready state (ioc_state=0x%x)\n", ++ ioc->name, __func__, ioc_state); ++ r = -EFAULT; ++ goto out; ++ } ++ out: ++ pr_info(MPT3SAS_FMT "message unit reset: %s\n", ++ ioc->name, ((r == 0) ? "SUCCESS" : "FAILED")); ++ return r; ++} ++ ++/** ++ * _base_handshake_req_reply_wait - send request thru doorbell interface ++ * @ioc: per adapter object ++ * @request_bytes: request length ++ * @request: pointer having request payload ++ * @reply_bytes: reply length ++ * @reply: pointer to reply payload ++ * @timeout: timeout in second ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, ++ u32 *request, int reply_bytes, u16 *reply, int timeout, int sleep_flag) ++{ ++ MPI2DefaultReply_t *default_reply = (MPI2DefaultReply_t *)reply; ++ int i; ++ u8 failed; ++ u16 dummy; ++ __le32 *mfp; ++ ++ /* make sure doorbell is not in use */ ++ if ((readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_USED)) { ++ pr_err(MPT3SAS_FMT ++ "doorbell is in use (line=%d)\n", ++ ioc->name, __LINE__); ++ return -EFAULT; ++ } ++ ++ /* clear pending doorbell interrupts from previous state changes */ ++ if (readl(&ioc->chip->HostInterruptStatus) & ++ MPI2_HIS_IOC2SYS_DB_STATUS) ++ writel(0, &ioc->chip->HostInterruptStatus); ++ ++ /* send message to ioc */ ++ writel(((MPI2_FUNCTION_HANDSHAKE<chip->Doorbell); ++ ++ if ((_base_wait_for_doorbell_int(ioc, 5, NO_SLEEP))) { ++ pr_err(MPT3SAS_FMT ++ "doorbell handshake int failed (line=%d)\n", ++ ioc->name, __LINE__); ++ return -EFAULT; ++ } ++ writel(0, &ioc->chip->HostInterruptStatus); ++ ++ if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag))) { ++ pr_err(MPT3SAS_FMT ++ "doorbell handshake ack failed (line=%d)\n", ++ ioc->name, __LINE__); ++ return -EFAULT; ++ } ++ ++ /* send message 32-bits at a time */ ++ for (i = 0, failed = 0; i < request_bytes/4 && !failed; i++) { ++ writel(cpu_to_le32(request[i]), &ioc->chip->Doorbell); ++ if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag))) ++ failed = 1; ++ } ++ ++ if (failed) { ++ pr_err(MPT3SAS_FMT ++ "doorbell handshake sending request failed (line=%d)\n", ++ ioc->name, __LINE__); ++ return -EFAULT; ++ } ++ ++ /* now wait for the reply */ ++ if ((_base_wait_for_doorbell_int(ioc, timeout, sleep_flag))) { ++ pr_err(MPT3SAS_FMT ++ "doorbell handshake int failed (line=%d)\n", ++ ioc->name, __LINE__); ++ return -EFAULT; ++ } ++ ++ /* read the first two 16-bits, it gives the total length of the reply */ ++ reply[0] = le16_to_cpu(readl(&ioc->chip->Doorbell) ++ & MPI2_DOORBELL_DATA_MASK); ++ writel(0, &ioc->chip->HostInterruptStatus); ++ if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) { ++ pr_err(MPT3SAS_FMT ++ "doorbell handshake int failed (line=%d)\n", ++ ioc->name, __LINE__); ++ return -EFAULT; ++ } ++ reply[1] = le16_to_cpu(readl(&ioc->chip->Doorbell) ++ & MPI2_DOORBELL_DATA_MASK); ++ writel(0, &ioc->chip->HostInterruptStatus); ++ ++ for (i = 2; i < default_reply->MsgLength * 2; i++) { ++ if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) { ++ pr_err(MPT3SAS_FMT ++ "doorbell handshake int failed (line=%d)\n", ++ ioc->name, __LINE__); ++ return -EFAULT; ++ } ++ if (i >= reply_bytes/2) /* overflow case */ ++ dummy = readl(&ioc->chip->Doorbell); ++ else ++ reply[i] = le16_to_cpu(readl(&ioc->chip->Doorbell) ++ & MPI2_DOORBELL_DATA_MASK); ++ writel(0, &ioc->chip->HostInterruptStatus); ++ } ++ ++ _base_wait_for_doorbell_int(ioc, 5, sleep_flag); ++ if (_base_wait_for_doorbell_not_used(ioc, 5, sleep_flag) != 0) { ++ dhsprintk(ioc, pr_info(MPT3SAS_FMT ++ "doorbell is in use (line=%d)\n", ioc->name, __LINE__)); ++ } ++ writel(0, &ioc->chip->HostInterruptStatus); ++ ++ if (ioc->logging_level & MPT_DEBUG_INIT) { ++ mfp = (__le32 *)reply; ++ pr_info("\toffset:data\n"); ++ for (i = 0; i < reply_bytes/4; i++) ++ pr_info("\t[0x%02x]:%08x\n", i*4, ++ le32_to_cpu(mfp[i])); ++ } ++ return 0; ++} ++ ++/** ++ * mpt2sas_base_sas_iounit_control - send sas iounit control to FW ++ * @ioc: per adapter object ++ * @mpi_reply: the reply payload from FW ++ * @mpi_request: the request payload sent to FW ++ * ++ * The SAS IO Unit Control Request message allows the host to perform low-level ++ * operations, such as resets on the PHYs of the IO Unit, also allows the host ++ * to obtain the IOC assigned device handles for a device if it has other ++ * identifying information about the device, in addition allows the host to ++ * remove IOC resources associated with the device. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2SasIoUnitControlReply_t *mpi_reply, ++ Mpi2SasIoUnitControlRequest_t *mpi_request) ++{ ++ u16 smid; ++ u32 ioc_state; ++ unsigned long timeleft; ++ bool issue_reset = false; ++ int rc; ++ void *request; ++ u16 wait_state_count; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ mutex_lock(&ioc->base_cmds.mutex); ++ ++ if (ioc->base_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: base_cmd in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, __func__, wait_state_count); ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ rc = 0; ++ ioc->base_cmds.status = MPT3_CMD_PENDING; ++ request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->base_cmds.smid = smid; ++ memcpy(request, mpi_request, sizeof(Mpi2SasIoUnitControlRequest_t)); ++ if (mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || ++ mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) ++ ioc->ioc_link_reset_in_progress = 1; ++ init_completion(&ioc->base_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, ++ msecs_to_jiffies(10000)); ++ if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || ++ mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) && ++ ioc->ioc_link_reset_in_progress) ++ ioc->ioc_link_reset_in_progress = 0; ++ if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SasIoUnitControlRequest_t)/4); ++ if (!(ioc->base_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = true; ++ goto issue_host_reset; ++ } ++ if (ioc->base_cmds.status & MPT3_CMD_REPLY_VALID) ++ memcpy(mpi_reply, ioc->base_cmds.reply, ++ sizeof(Mpi2SasIoUnitControlReply_t)); ++ else ++ memset(mpi_reply, 0, sizeof(Mpi2SasIoUnitControlReply_t)); ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ goto out; ++ ++ issue_host_reset: ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ rc = -EFAULT; ++ out: ++ mutex_unlock(&ioc->base_cmds.mutex); ++ return rc; ++} ++ ++/** ++ * mpt2sas_base_scsi_enclosure_processor - sending request to sep device ++ * @ioc: per adapter object ++ * @mpi_reply: the reply payload from FW ++ * @mpi_request: the request payload sent to FW ++ * ++ * The SCSI Enclosure Processor request message causes the IOC to ++ * communicate with SES devices to control LED status signals. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request) ++{ ++ u16 smid; ++ u32 ioc_state; ++ unsigned long timeleft; ++ bool issue_reset = false; ++ int rc; ++ void *request; ++ u16 wait_state_count; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ mutex_lock(&ioc->base_cmds.mutex); ++ ++ if (ioc->base_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: base_cmd in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, ++ __func__, wait_state_count); ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ rc = 0; ++ ioc->base_cmds.status = MPT3_CMD_PENDING; ++ request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->base_cmds.smid = smid; ++ memcpy(request, mpi_request, sizeof(Mpi2SepReply_t)); ++ init_completion(&ioc->base_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, ++ msecs_to_jiffies(10000)); ++ if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SepRequest_t)/4); ++ if (!(ioc->base_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = false; ++ goto issue_host_reset; ++ } ++ if (ioc->base_cmds.status & MPT3_CMD_REPLY_VALID) ++ memcpy(mpi_reply, ioc->base_cmds.reply, ++ sizeof(Mpi2SepReply_t)); ++ else ++ memset(mpi_reply, 0, sizeof(Mpi2SepReply_t)); ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ goto out; ++ ++ issue_host_reset: ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ rc = -EFAULT; ++ out: ++ mutex_unlock(&ioc->base_cmds.mutex); ++ return rc; ++} ++ ++/** ++ * _base_get_port_facts - obtain port facts reply and save in ioc ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port, int sleep_flag) ++{ ++ Mpi2PortFactsRequest_t mpi_request; ++ Mpi2PortFactsReply_t mpi_reply; ++ struct mpt2sas_port_facts *pfacts; ++ int mpi_reply_sz, mpi_request_sz, r; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ mpi_reply_sz = sizeof(Mpi2PortFactsReply_t); ++ mpi_request_sz = sizeof(Mpi2PortFactsRequest_t); ++ memset(&mpi_request, 0, mpi_request_sz); ++ mpi_request.Function = MPI2_FUNCTION_PORT_FACTS; ++ mpi_request.PortNumber = port; ++ r = _base_handshake_req_reply_wait(ioc, mpi_request_sz, ++ (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP); ++ ++ if (r != 0) { ++ pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n", ++ ioc->name, __func__, r); ++ return r; ++ } ++ ++ pfacts = &ioc->pfacts[port]; ++ memset(pfacts, 0, sizeof(struct mpt2sas_port_facts)); ++ pfacts->PortNumber = mpi_reply.PortNumber; ++ pfacts->VP_ID = mpi_reply.VP_ID; ++ pfacts->VF_ID = mpi_reply.VF_ID; ++ pfacts->MaxPostedCmdBuffers = ++ le16_to_cpu(mpi_reply.MaxPostedCmdBuffers); ++ ++ return 0; ++} ++ ++/** ++ * _base_wait_for_iocstate - Wait until the card is in READY or OPERATIONAL ++ * @ioc: per adapter object ++ * @timeout: ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout, ++ int sleep_flag) ++{ ++ u32 ioc_state; ++ int rc; ++ ++ dinitprintk(ioc, printk(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ if (ioc->pci_error_recovery) { ++ dfailprintk(ioc, printk(MPT3SAS_FMT ++ "%s: host in pci error recovery\n", ioc->name, __func__)); ++ return -EFAULT; ++ } ++ ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 0); ++ dhsprintk(ioc, printk(MPT3SAS_FMT "%s: ioc_state(0x%08x)\n", ++ ioc->name, __func__, ioc_state)); ++ ++ if (((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY) || ++ (ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL) ++ return 0; ++ ++ if (ioc_state & MPI2_DOORBELL_USED) { ++ dhsprintk(ioc, printk(MPT3SAS_FMT ++ "unexpected doorbell active!\n", ioc->name)); ++ goto issue_diag_reset; ++ } ++ ++ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { ++ mpt2sas_base_fault_info(ioc, ioc_state & ++ MPI2_DOORBELL_DATA_MASK); ++ goto issue_diag_reset; ++ } ++ ++ ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, ++ timeout, sleep_flag); ++ if (ioc_state) { ++ dfailprintk(ioc, printk(MPT3SAS_FMT ++ "%s: failed going to ready state (ioc_state=0x%x)\n", ++ ioc->name, __func__, ioc_state)); ++ return -EFAULT; ++ } ++ ++ issue_diag_reset: ++ rc = _base_diag_reset(ioc, sleep_flag); ++ return rc; ++} ++ ++/** ++ * _base_get_ioc_facts - obtain ioc facts reply and save in ioc ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ Mpi2IOCFactsRequest_t mpi_request; ++ Mpi2IOCFactsReply_t mpi_reply; ++ struct mpt2sas_facts *facts; ++ int mpi_reply_sz, mpi_request_sz, r; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ r = _base_wait_for_iocstate(ioc, 10, sleep_flag); ++ if (r) { ++ dfailprintk(ioc, printk(MPT3SAS_FMT ++ "%s: failed getting to correct state\n", ++ ioc->name, __func__)); ++ return r; ++ } ++ mpi_reply_sz = sizeof(Mpi2IOCFactsReply_t); ++ mpi_request_sz = sizeof(Mpi2IOCFactsRequest_t); ++ memset(&mpi_request, 0, mpi_request_sz); ++ mpi_request.Function = MPI2_FUNCTION_IOC_FACTS; ++ r = _base_handshake_req_reply_wait(ioc, mpi_request_sz, ++ (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP); ++ ++ if (r != 0) { ++ pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n", ++ ioc->name, __func__, r); ++ return r; ++ } ++ ++ facts = &ioc->facts; ++ memset(facts, 0, sizeof(struct mpt2sas_facts)); ++ facts->MsgVersion = le16_to_cpu(mpi_reply.MsgVersion); ++ facts->HeaderVersion = le16_to_cpu(mpi_reply.HeaderVersion); ++ facts->VP_ID = mpi_reply.VP_ID; ++ facts->VF_ID = mpi_reply.VF_ID; ++ facts->IOCExceptions = le16_to_cpu(mpi_reply.IOCExceptions); ++ facts->MaxChainDepth = mpi_reply.MaxChainDepth; ++ facts->WhoInit = mpi_reply.WhoInit; ++ facts->NumberOfPorts = mpi_reply.NumberOfPorts; ++ facts->MaxMSIxVectors = mpi_reply.MaxMSIxVectors; ++ facts->RequestCredit = le16_to_cpu(mpi_reply.RequestCredit); ++ facts->MaxReplyDescriptorPostQueueDepth = ++ le16_to_cpu(mpi_reply.MaxReplyDescriptorPostQueueDepth); ++ facts->ProductID = le16_to_cpu(mpi_reply.ProductID); ++ facts->IOCCapabilities = le32_to_cpu(mpi_reply.IOCCapabilities); ++ if ((facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID)) ++ ioc->ir_firmware = 1; ++ if ((facts->IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE)) ++ ioc->rdpq_array_capable = 1; ++ facts->FWVersion.Word = le32_to_cpu(mpi_reply.FWVersion.Word); ++ facts->IOCRequestFrameSize = ++ le16_to_cpu(mpi_reply.IOCRequestFrameSize); ++ if (ioc->hba_mpi_version_belonged != MPI2_VERSION) { ++ facts->IOCMaxChainSegmentSize = ++ le16_to_cpu(mpi_reply.IOCMaxChainSegmentSize); ++ } ++ facts->MaxInitiators = le16_to_cpu(mpi_reply.MaxInitiators); ++ facts->MaxTargets = le16_to_cpu(mpi_reply.MaxTargets); ++ ioc->shost->max_id = -1; ++ facts->MaxSasExpanders = le16_to_cpu(mpi_reply.MaxSasExpanders); ++ facts->MaxEnclosures = le16_to_cpu(mpi_reply.MaxEnclosures); ++ facts->ProtocolFlags = le16_to_cpu(mpi_reply.ProtocolFlags); ++ facts->HighPriorityCredit = ++ le16_to_cpu(mpi_reply.HighPriorityCredit); ++ facts->ReplyFrameSize = mpi_reply.ReplyFrameSize; ++ facts->MaxDevHandle = le16_to_cpu(mpi_reply.MaxDevHandle); ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "hba queue depth(%d), max chains per io(%d)\n", ++ ioc->name, facts->RequestCredit, ++ facts->MaxChainDepth)); ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "request frame size(%d), reply frame size(%d)\n", ioc->name, ++ facts->IOCRequestFrameSize * 4, facts->ReplyFrameSize * 4)); ++ return 0; ++} ++ ++/** ++ * _base_send_ioc_init - send ioc_init to firmware ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ Mpi2IOCInitRequest_t mpi_request; ++ Mpi2IOCInitReply_t mpi_reply; ++ int i, r = 0; ++ ktime_t current_time; ++ u16 ioc_status; ++ u32 reply_post_free_array_sz = 0; ++ Mpi2IOCInitRDPQArrayEntry *reply_post_free_array = NULL; ++ dma_addr_t reply_post_free_array_dma; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ memset(&mpi_request, 0, sizeof(Mpi2IOCInitRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_IOC_INIT; ++ mpi_request.WhoInit = MPI2_WHOINIT_HOST_DRIVER; ++ mpi_request.VF_ID = 0; /* TODO */ ++ mpi_request.VP_ID = 0; ++ mpi_request.MsgVersion = cpu_to_le16(ioc->hba_mpi_version_belonged); ++ mpi_request.HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION); ++ ++ if (_base_is_controller_msix_enabled(ioc)) ++ mpi_request.HostMSIxVectors = ioc->reply_queue_count; ++ mpi_request.SystemRequestFrameSize = cpu_to_le16(ioc->request_sz/4); ++ mpi_request.ReplyDescriptorPostQueueDepth = ++ cpu_to_le16(ioc->reply_post_queue_depth); ++ mpi_request.ReplyFreeQueueDepth = ++ cpu_to_le16(ioc->reply_free_queue_depth); ++ ++ mpi_request.SenseBufferAddressHigh = ++ cpu_to_le32((u64)ioc->sense_dma >> 32); ++ mpi_request.SystemReplyAddressHigh = ++ cpu_to_le32((u64)ioc->reply_dma >> 32); ++ mpi_request.SystemRequestFrameBaseAddress = ++ cpu_to_le64((u64)ioc->request_dma); ++ mpi_request.ReplyFreeQueueAddress = ++ cpu_to_le64((u64)ioc->reply_free_dma); ++ ++ if (ioc->rdpq_array_enable) { ++ reply_post_free_array_sz = ioc->reply_queue_count * ++ sizeof(Mpi2IOCInitRDPQArrayEntry); ++ reply_post_free_array = pci_alloc_consistent(ioc->pdev, ++ reply_post_free_array_sz, &reply_post_free_array_dma); ++ if (!reply_post_free_array) { ++ pr_err(MPT3SAS_FMT ++ "reply_post_free_array: pci_alloc_consistent failed\n", ++ ioc->name); ++ r = -ENOMEM; ++ goto out; ++ } ++ memset(reply_post_free_array, 0, reply_post_free_array_sz); ++ for (i = 0; i < ioc->reply_queue_count; i++) ++ reply_post_free_array[i].RDPQBaseAddress = ++ cpu_to_le64( ++ (u64)ioc->reply_post[i].reply_post_free_dma); ++ mpi_request.MsgFlags = MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE; ++ mpi_request.ReplyDescriptorPostQueueAddress = ++ cpu_to_le64((u64)reply_post_free_array_dma); ++ } else { ++ mpi_request.ReplyDescriptorPostQueueAddress = ++ cpu_to_le64((u64)ioc->reply_post[0].reply_post_free_dma); ++ } ++ ++ /* This time stamp specifies number of milliseconds ++ * since epoch ~ midnight January 1, 1970. ++ */ ++ current_time = ktime_get_real(); ++ mpi_request.TimeStamp = cpu_to_le64(ktime_to_ms(current_time)); ++ ++ if (ioc->logging_level & MPT_DEBUG_INIT) { ++ __le32 *mfp; ++ int i; ++ ++ mfp = (__le32 *)&mpi_request; ++ pr_info("\toffset:data\n"); ++ for (i = 0; i < sizeof(Mpi2IOCInitRequest_t)/4; i++) ++ pr_info("\t[0x%02x]:%08x\n", i*4, ++ le32_to_cpu(mfp[i])); ++ } ++ ++ r = _base_handshake_req_reply_wait(ioc, ++ sizeof(Mpi2IOCInitRequest_t), (u32 *)&mpi_request, ++ sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 10, ++ sleep_flag); ++ ++ if (r != 0) { ++ pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n", ++ ioc->name, __func__, r); ++ goto out; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS || ++ mpi_reply.IOCLogInfo) { ++ pr_err(MPT3SAS_FMT "%s: failed\n", ioc->name, __func__); ++ r = -EIO; ++ } ++ ++out: ++ if (reply_post_free_array) ++ pci_free_consistent(ioc->pdev, reply_post_free_array_sz, ++ reply_post_free_array, ++ reply_post_free_array_dma); ++ return r; ++} ++ ++/** ++ * mpt2sas_port_enable_done - command completion routine for port enable ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_port_enable_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ u16 ioc_status; ++ ++ if (ioc->port_enable_cmds.status == MPT3_CMD_NOT_USED) ++ return 1; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (!mpi_reply) ++ return 1; ++ ++ if (mpi_reply->Function != MPI2_FUNCTION_PORT_ENABLE) ++ return 1; ++ ++ ioc->port_enable_cmds.status &= ~MPT3_CMD_PENDING; ++ ioc->port_enable_cmds.status |= MPT3_CMD_COMPLETE; ++ ioc->port_enable_cmds.status |= MPT3_CMD_REPLY_VALID; ++ memcpy(ioc->port_enable_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ ioc->port_enable_failed = 1; ++ ++ if (ioc->is_driver_loading) { ++ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ++ mpt2sas_port_enable_complete(ioc); ++ return 1; ++ } else { ++ ioc->start_scan_failed = ioc_status; ++ ioc->start_scan = 0; ++ return 1; ++ } ++ } ++ complete(&ioc->port_enable_cmds.done); ++ return 1; ++} ++ ++/** ++ * _base_send_port_enable - send port_enable(discovery stuff) to firmware ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_send_port_enable(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ Mpi2PortEnableRequest_t *mpi_request; ++ Mpi2PortEnableReply_t *mpi_reply; ++ unsigned long timeleft; ++ int r = 0; ++ u16 smid; ++ u16 ioc_status; ++ ++ pr_info(MPT3SAS_FMT "sending port enable !!\n", ioc->name); ++ ++ if (ioc->port_enable_cmds.status & MPT3_CMD_PENDING) { ++ pr_err(MPT3SAS_FMT "%s: internal command already in use\n", ++ ioc->name, __func__); ++ return -EAGAIN; ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->port_enable_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ return -EAGAIN; ++ } ++ ++ ioc->port_enable_cmds.status = MPT3_CMD_PENDING; ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->port_enable_cmds.smid = smid; ++ memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE; ++ ++ init_completion(&ioc->port_enable_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->port_enable_cmds.done, ++ 300*HZ); ++ if (!(ioc->port_enable_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2PortEnableRequest_t)/4); ++ if (ioc->port_enable_cmds.status & MPT3_CMD_RESET) ++ r = -EFAULT; ++ else ++ r = -ETIME; ++ goto out; ++ } ++ ++ mpi_reply = ioc->port_enable_cmds.reply; ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "%s: failed with (ioc_status=0x%08x)\n", ++ ioc->name, __func__, ioc_status); ++ r = -EFAULT; ++ goto out; ++ } ++ ++ out: ++ ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED; ++ pr_info(MPT3SAS_FMT "port enable: %s\n", ioc->name, ((r == 0) ? ++ "SUCCESS" : "FAILED")); ++ return r; ++} ++ ++/** ++ * mpt2sas_port_enable - initiate firmware discovery (don't wait for reply) ++ * @ioc: per adapter object ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_port_enable(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2PortEnableRequest_t *mpi_request; ++ u16 smid; ++ ++ pr_info(MPT3SAS_FMT "sending port enable !!\n", ioc->name); ++ ++ if (ioc->port_enable_cmds.status & MPT3_CMD_PENDING) { ++ pr_err(MPT3SAS_FMT "%s: internal command already in use\n", ++ ioc->name, __func__); ++ return -EAGAIN; ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->port_enable_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ return -EAGAIN; ++ } ++ ++ ioc->port_enable_cmds.status = MPT3_CMD_PENDING; ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->port_enable_cmds.smid = smid; ++ memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE; ++ ++ mpt2sas_base_put_smid_default(ioc, smid); ++ return 0; ++} ++ ++/** ++ * _base_determine_wait_on_discovery - desposition ++ * @ioc: per adapter object ++ * ++ * Decide whether to wait on discovery to complete. Used to either ++ * locate boot device, or report volumes ahead of physical devices. ++ * ++ * Returns 1 for wait, 0 for don't wait ++ */ ++static int ++_base_determine_wait_on_discovery(struct MPT3SAS_ADAPTER *ioc) ++{ ++ /* We wait for discovery to complete if IR firmware is loaded. ++ * The sas topology events arrive before PD events, so we need time to ++ * turn on the bit in ioc->pd_handles to indicate PD ++ * Also, it maybe required to report Volumes ahead of physical ++ * devices when MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING is set. ++ */ ++ if (ioc->ir_firmware) ++ return 1; ++ ++ /* if no Bios, then we don't need to wait */ ++ if (!ioc->bios_pg3.BiosVersion) ++ return 0; ++ ++ /* Bios is present, then we drop down here. ++ * ++ * If there any entries in the Bios Page 2, then we wait ++ * for discovery to complete. ++ */ ++ ++ /* Current Boot Device */ ++ if ((ioc->bios_pg2.CurrentBootDeviceForm & ++ MPI2_BIOSPAGE2_FORM_MASK) == ++ MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED && ++ /* Request Boot Device */ ++ (ioc->bios_pg2.ReqBootDeviceForm & ++ MPI2_BIOSPAGE2_FORM_MASK) == ++ MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED && ++ /* Alternate Request Boot Device */ ++ (ioc->bios_pg2.ReqAltBootDeviceForm & ++ MPI2_BIOSPAGE2_FORM_MASK) == ++ MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED) ++ return 0; ++ ++ return 1; ++} ++ ++/** ++ * _base_unmask_events - turn on notification for this event ++ * @ioc: per adapter object ++ * @event: firmware event ++ * ++ * The mask is stored in ioc->event_masks. ++ */ ++static void ++_base_unmask_events(struct MPT3SAS_ADAPTER *ioc, u16 event) ++{ ++ u32 desired_event; ++ ++ if (event >= 128) ++ return; ++ ++ desired_event = (1 << (event % 32)); ++ ++ if (event < 32) ++ ioc->event_masks[0] &= ~desired_event; ++ else if (event < 64) ++ ioc->event_masks[1] &= ~desired_event; ++ else if (event < 96) ++ ioc->event_masks[2] &= ~desired_event; ++ else if (event < 128) ++ ioc->event_masks[3] &= ~desired_event; ++} ++ ++/** ++ * _base_event_notification - send event notification ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_event_notification(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ Mpi2EventNotificationRequest_t *mpi_request; ++ unsigned long timeleft; ++ u16 smid; ++ int r = 0; ++ int i; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ if (ioc->base_cmds.status & MPT3_CMD_PENDING) { ++ pr_err(MPT3SAS_FMT "%s: internal command already in use\n", ++ ioc->name, __func__); ++ return -EAGAIN; ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ return -EAGAIN; ++ } ++ ioc->base_cmds.status = MPT3_CMD_PENDING; ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->base_cmds.smid = smid; ++ memset(mpi_request, 0, sizeof(Mpi2EventNotificationRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_EVENT_NOTIFICATION; ++ mpi_request->VF_ID = 0; /* TODO */ ++ mpi_request->VP_ID = 0; ++ for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) ++ mpi_request->EventMasks[i] = ++ cpu_to_le32(ioc->event_masks[i]); ++ init_completion(&ioc->base_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ); ++ if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2EventNotificationRequest_t)/4); ++ if (ioc->base_cmds.status & MPT3_CMD_RESET) ++ r = -EFAULT; ++ else ++ r = -ETIME; ++ } else ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s: complete\n", ++ ioc->name, __func__)); ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ return r; ++} ++ ++/** ++ * mpt2sas_base_validate_event_type - validating event types ++ * @ioc: per adapter object ++ * @event: firmware event ++ * ++ * This will turn on firmware event notification when application ++ * ask for that event. We don't mask events that are already enabled. ++ */ ++void ++mpt2sas_base_validate_event_type(struct MPT3SAS_ADAPTER *ioc, u32 *event_type) ++{ ++ int i, j; ++ u32 event_mask, desired_event; ++ u8 send_update_to_fw; ++ ++ for (i = 0, send_update_to_fw = 0; i < ++ MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) { ++ event_mask = ~event_type[i]; ++ desired_event = 1; ++ for (j = 0; j < 32; j++) { ++ if (!(event_mask & desired_event) && ++ (ioc->event_masks[i] & desired_event)) { ++ ioc->event_masks[i] &= ~desired_event; ++ send_update_to_fw = 1; ++ } ++ desired_event = (desired_event << 1); ++ } ++ } ++ ++ if (!send_update_to_fw) ++ return; ++ ++ mutex_lock(&ioc->base_cmds.mutex); ++ _base_event_notification(ioc, CAN_SLEEP); ++ mutex_unlock(&ioc->base_cmds.mutex); ++} ++ ++/** ++ * _base_diag_reset - the "big hammer" start of day reset ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ u32 host_diagnostic; ++ u32 ioc_state; ++ u32 count; ++ u32 hcb_size; ++ ++ pr_info(MPT3SAS_FMT "sending diag reset !!\n", ioc->name); ++ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT "clear interrupts\n", ++ ioc->name)); ++ ++ count = 0; ++ do { ++ /* Write magic sequence to WriteSequence register ++ * Loop until in diagnostic mode ++ */ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT ++ "write magic sequence\n", ioc->name)); ++ writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence); ++ writel(MPI2_WRSEQ_1ST_KEY_VALUE, &ioc->chip->WriteSequence); ++ writel(MPI2_WRSEQ_2ND_KEY_VALUE, &ioc->chip->WriteSequence); ++ writel(MPI2_WRSEQ_3RD_KEY_VALUE, &ioc->chip->WriteSequence); ++ writel(MPI2_WRSEQ_4TH_KEY_VALUE, &ioc->chip->WriteSequence); ++ writel(MPI2_WRSEQ_5TH_KEY_VALUE, &ioc->chip->WriteSequence); ++ writel(MPI2_WRSEQ_6TH_KEY_VALUE, &ioc->chip->WriteSequence); ++ ++ /* wait 100 msec */ ++ if (sleep_flag == CAN_SLEEP) ++ msleep(100); ++ else ++ mdelay(100); ++ ++ if (count++ > 20) ++ goto out; ++ ++ host_diagnostic = readl(&ioc->chip->HostDiagnostic); ++ drsprintk(ioc, pr_info(MPT3SAS_FMT ++ "wrote magic sequence: count(%d), host_diagnostic(0x%08x)\n", ++ ioc->name, count, host_diagnostic)); ++ ++ } while ((host_diagnostic & MPI2_DIAG_DIAG_WRITE_ENABLE) == 0); ++ ++ hcb_size = readl(&ioc->chip->HCBSize); ++ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT "diag reset: issued\n", ++ ioc->name)); ++ writel(host_diagnostic | MPI2_DIAG_RESET_ADAPTER, ++ &ioc->chip->HostDiagnostic); ++ ++ /*This delay allows the chip PCIe hardware time to finish reset tasks*/ ++ if (sleep_flag == CAN_SLEEP) ++ msleep(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000); ++ else ++ mdelay(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000); ++ ++ /* Approximately 300 second max wait */ ++ for (count = 0; count < (300000000 / ++ MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC); count++) { ++ ++ host_diagnostic = readl(&ioc->chip->HostDiagnostic); ++ ++ if (host_diagnostic == 0xFFFFFFFF) ++ goto out; ++ if (!(host_diagnostic & MPI2_DIAG_RESET_ADAPTER)) ++ break; ++ ++ /* Wait to pass the second read delay window */ ++ if (sleep_flag == CAN_SLEEP) ++ msleep(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC ++ / 1000); ++ else ++ mdelay(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC ++ / 1000); ++ } ++ ++ if (host_diagnostic & MPI2_DIAG_HCB_MODE) { ++ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT ++ "restart the adapter assuming the HCB Address points to good F/W\n", ++ ioc->name)); ++ host_diagnostic &= ~MPI2_DIAG_BOOT_DEVICE_SELECT_MASK; ++ host_diagnostic |= MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW; ++ writel(host_diagnostic, &ioc->chip->HostDiagnostic); ++ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT ++ "re-enable the HCDW\n", ioc->name)); ++ writel(hcb_size | MPI2_HCB_SIZE_HCB_ENABLE, ++ &ioc->chip->HCBSize); ++ } ++ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT "restart the adapter\n", ++ ioc->name)); ++ writel(host_diagnostic & ~MPI2_DIAG_HOLD_IOC_RESET, ++ &ioc->chip->HostDiagnostic); ++ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT ++ "disable writes to the diagnostic register\n", ioc->name)); ++ writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence); ++ ++ drsprintk(ioc, pr_info(MPT3SAS_FMT ++ "Wait for FW to go to the READY state\n", ioc->name)); ++ ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20, ++ sleep_flag); ++ if (ioc_state) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed going to ready state (ioc_state=0x%x)\n", ++ ioc->name, __func__, ioc_state); ++ goto out; ++ } ++ ++ pr_info(MPT3SAS_FMT "diag reset: SUCCESS\n", ioc->name); ++ return 0; ++ ++ out: ++ pr_err(MPT3SAS_FMT "diag reset: FAILED\n", ioc->name); ++ return -EFAULT; ++} ++ ++/** ++ * _base_make_ioc_ready - put controller in READY state ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * @type: FORCE_BIG_HAMMER or SOFT_RESET ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, ++ enum reset_type type) ++{ ++ u32 ioc_state; ++ int rc; ++ int count; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ if (ioc->pci_error_recovery) ++ return 0; ++ ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 0); ++ dhsprintk(ioc, pr_info(MPT3SAS_FMT "%s: ioc_state(0x%08x)\n", ++ ioc->name, __func__, ioc_state)); ++ ++ /* if in RESET state, it should move to READY state shortly */ ++ count = 0; ++ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_RESET) { ++ while ((ioc_state & MPI2_IOC_STATE_MASK) != ++ MPI2_IOC_STATE_READY) { ++ if (count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed going to ready state (ioc_state=0x%x)\n", ++ ioc->name, __func__, ioc_state); ++ return -EFAULT; ++ } ++ if (sleep_flag == CAN_SLEEP) ++ ssleep(1); ++ else ++ mdelay(1000); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 0); ++ } ++ } ++ ++ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY) ++ return 0; ++ ++ if (ioc_state & MPI2_DOORBELL_USED) { ++ dhsprintk(ioc, pr_info(MPT3SAS_FMT ++ "unexpected doorbell active!\n", ++ ioc->name)); ++ goto issue_diag_reset; ++ } ++ ++ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { ++ mpt2sas_base_fault_info(ioc, ioc_state & ++ MPI2_DOORBELL_DATA_MASK); ++ goto issue_diag_reset; ++ } ++ ++ if (type == FORCE_BIG_HAMMER) ++ goto issue_diag_reset; ++ ++ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL) ++ if (!(_base_send_ioc_reset(ioc, ++ MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET, 15, CAN_SLEEP))) { ++ return 0; ++ } ++ ++ issue_diag_reset: ++ rc = _base_diag_reset(ioc, CAN_SLEEP); ++ return rc; ++} ++ ++/** ++ * _base_make_ioc_operational - put controller in OPERATIONAL state ++ * @ioc: per adapter object ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ int r, i, index; ++ unsigned long flags; ++ u32 reply_address; ++ u16 smid; ++ struct _tr_list *delayed_tr, *delayed_tr_next; ++ struct _sc_list *delayed_sc, *delayed_sc_next; ++ struct _event_ack_list *delayed_event_ack, *delayed_event_ack_next; ++ u8 hide_flag; ++ struct adapter_reply_queue *reply_q; ++ Mpi2ReplyDescriptorsUnion_t *reply_post_free_contig; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ /* clean the delayed target reset list */ ++ list_for_each_entry_safe(delayed_tr, delayed_tr_next, ++ &ioc->delayed_tr_list, list) { ++ list_del(&delayed_tr->list); ++ kfree(delayed_tr); ++ } ++ ++ ++ list_for_each_entry_safe(delayed_tr, delayed_tr_next, ++ &ioc->delayed_tr_volume_list, list) { ++ list_del(&delayed_tr->list); ++ kfree(delayed_tr); ++ } ++ ++ list_for_each_entry_safe(delayed_sc, delayed_sc_next, ++ &ioc->delayed_sc_list, list) { ++ list_del(&delayed_sc->list); ++ kfree(delayed_sc); ++ } ++ ++ list_for_each_entry_safe(delayed_event_ack, delayed_event_ack_next, ++ &ioc->delayed_event_ack_list, list) { ++ list_del(&delayed_event_ack->list); ++ kfree(delayed_event_ack); ++ } ++ ++ /* initialize the scsi lookup free list */ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ INIT_LIST_HEAD(&ioc->free_list); ++ smid = 1; ++ for (i = 0; i < ioc->scsiio_depth; i++, smid++) { ++ INIT_LIST_HEAD(&ioc->scsi_lookup[i].chain_list); ++ ioc->scsi_lookup[i].cb_idx = 0xFF; ++ ioc->scsi_lookup[i].smid = smid; ++ ioc->scsi_lookup[i].scmd = NULL; ++ ioc->scsi_lookup[i].direct_io = 0; ++ list_add_tail(&ioc->scsi_lookup[i].tracker_list, ++ &ioc->free_list); ++ } ++ ++ /* hi-priority queue */ ++ INIT_LIST_HEAD(&ioc->hpr_free_list); ++ smid = ioc->hi_priority_smid; ++ for (i = 0; i < ioc->hi_priority_depth; i++, smid++) { ++ ioc->hpr_lookup[i].cb_idx = 0xFF; ++ ioc->hpr_lookup[i].smid = smid; ++ list_add_tail(&ioc->hpr_lookup[i].tracker_list, ++ &ioc->hpr_free_list); ++ } ++ ++ /* internal queue */ ++ INIT_LIST_HEAD(&ioc->internal_free_list); ++ smid = ioc->internal_smid; ++ for (i = 0; i < ioc->internal_depth; i++, smid++) { ++ ioc->internal_lookup[i].cb_idx = 0xFF; ++ ioc->internal_lookup[i].smid = smid; ++ list_add_tail(&ioc->internal_lookup[i].tracker_list, ++ &ioc->internal_free_list); ++ } ++ ++ /* chain pool */ ++ INIT_LIST_HEAD(&ioc->free_chain_list); ++ for (i = 0; i < ioc->chain_depth; i++) ++ list_add_tail(&ioc->chain_lookup[i].tracker_list, ++ &ioc->free_chain_list); ++ ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ ++ /* initialize Reply Free Queue */ ++ for (i = 0, reply_address = (u32)ioc->reply_dma ; ++ i < ioc->reply_free_queue_depth ; i++, reply_address += ++ ioc->reply_sz) ++ ioc->reply_free[i] = cpu_to_le32(reply_address); ++ ++ /* initialize reply queues */ ++ if (ioc->is_driver_loading) ++ _base_assign_reply_queues(ioc); ++ ++ /* initialize Reply Post Free Queue */ ++ index = 0; ++ reply_post_free_contig = ioc->reply_post[0].reply_post_free; ++ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { ++ /* ++ * If RDPQ is enabled, switch to the next allocation. ++ * Otherwise advance within the contiguous region. ++ */ ++ if (ioc->rdpq_array_enable) { ++ reply_q->reply_post_free = ++ ioc->reply_post[index++].reply_post_free; ++ } else { ++ reply_q->reply_post_free = reply_post_free_contig; ++ reply_post_free_contig += ioc->reply_post_queue_depth; ++ } ++ ++ reply_q->reply_post_host_index = 0; ++ for (i = 0; i < ioc->reply_post_queue_depth; i++) ++ reply_q->reply_post_free[i].Words = ++ cpu_to_le64(ULLONG_MAX); ++ if (!_base_is_controller_msix_enabled(ioc)) ++ goto skip_init_reply_post_free_queue; ++ } ++ skip_init_reply_post_free_queue: ++ ++ r = _base_send_ioc_init(ioc, sleep_flag); ++ if (r) ++ return r; ++ ++ /* initialize reply free host index */ ++ ioc->reply_free_host_index = ioc->reply_free_queue_depth - 1; ++ writel(ioc->reply_free_host_index, &ioc->chip->ReplyFreeHostIndex); ++ ++ /* initialize reply post host index */ ++ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { ++ if (ioc->msix96_vector) ++ writel((reply_q->msix_index & 7)<< ++ MPI2_RPHI_MSIX_INDEX_SHIFT, ++ ioc->replyPostRegisterIndex[reply_q->msix_index/8]); ++ else ++ writel(reply_q->msix_index << ++ MPI2_RPHI_MSIX_INDEX_SHIFT, ++ &ioc->chip->ReplyPostHostIndex); ++ ++ if (!_base_is_controller_msix_enabled(ioc)) ++ goto skip_init_reply_post_host_index; ++ } ++ ++ skip_init_reply_post_host_index: ++ ++ _base_unmask_interrupts(ioc); ++ r = _base_event_notification(ioc, sleep_flag); ++ if (r) ++ return r; ++ ++ if (sleep_flag == CAN_SLEEP) ++ _base_static_config_pages(ioc); ++ ++ ++ if (ioc->is_driver_loading) { ++ ++ if (ioc->is_warpdrive && ioc->manu_pg10.OEMIdentifier ++ == 0x80) { ++ hide_flag = (u8) ( ++ le32_to_cpu(ioc->manu_pg10.OEMSpecificFlags0) & ++ MFG_PAGE10_HIDE_SSDS_MASK); ++ if (hide_flag != MFG_PAGE10_HIDE_SSDS_MASK) ++ ioc->mfg_pg10_hide_flag = hide_flag; ++ } ++ ++ ioc->wait_for_discovery_to_complete = ++ _base_determine_wait_on_discovery(ioc); ++ ++ return r; /* scan_start and scan_finished support */ ++ } ++ ++ r = _base_send_port_enable(ioc, sleep_flag); ++ if (r) ++ return r; ++ ++ return r; ++} ++ ++/** ++ * mpt2sas_base_free_resources - free resources controller resources ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc) ++{ ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ /* synchronizing freeing resource with pci_access_mutex lock */ ++ mutex_lock(&ioc->pci_access_mutex); ++ if (ioc->chip_phys && ioc->chip) { ++ _base_mask_interrupts(ioc); ++ ioc->shost_recovery = 1; ++ _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); ++ ioc->shost_recovery = 0; ++ } ++ ++ mpt2sas_base_unmap_resources(ioc); ++ mutex_unlock(&ioc->pci_access_mutex); ++ return; ++} ++ ++/** ++ * mpt2sas_base_attach - attach controller instance ++ * @ioc: per adapter object ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_base_attach(struct MPT3SAS_ADAPTER *ioc) ++{ ++ int r, i; ++ int cpu_id, last_cpu_id = 0; ++ ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ /* setup cpu_msix_table */ ++ ioc->cpu_count = num_online_cpus(); ++ for_each_online_cpu(cpu_id) ++ last_cpu_id = cpu_id; ++ ioc->cpu_msix_table_sz = last_cpu_id + 1; ++ ioc->cpu_msix_table = kzalloc(ioc->cpu_msix_table_sz, GFP_KERNEL); ++ ioc->reply_queue_count = 1; ++ if (!ioc->cpu_msix_table) { ++ dfailprintk(ioc, pr_info(MPT3SAS_FMT ++ "allocation for cpu_msix_table failed!!!\n", ++ ioc->name)); ++ r = -ENOMEM; ++ goto out_free_resources; ++ } ++ ++ if (ioc->is_warpdrive) { ++ ioc->reply_post_host_index = kcalloc(ioc->cpu_msix_table_sz, ++ sizeof(resource_size_t *), GFP_KERNEL); ++ if (!ioc->reply_post_host_index) { ++ dfailprintk(ioc, pr_info(MPT3SAS_FMT "allocation " ++ "for cpu_msix_table failed!!!\n", ioc->name)); ++ r = -ENOMEM; ++ goto out_free_resources; ++ } ++ } ++ ++ ioc->rdpq_array_enable_assigned = 0; ++ ioc->dma_mask = 0; ++ r = mpt2sas_base_map_resources(ioc); ++ if (r) ++ goto out_free_resources; ++ ++ if (ioc->is_warpdrive) { ++ ioc->reply_post_host_index[0] = (resource_size_t __iomem *) ++ &ioc->chip->ReplyPostHostIndex; ++ ++ for (i = 1; i < ioc->cpu_msix_table_sz; i++) ++ ioc->reply_post_host_index[i] = ++ (resource_size_t __iomem *) ++ ((u8 __iomem *)&ioc->chip->Doorbell + (0x4000 + ((i - 1) ++ * 4))); ++ } ++ ++ pci_set_drvdata(ioc->pdev, ioc->shost); ++ r = _base_get_ioc_facts(ioc, CAN_SLEEP); ++ if (r) ++ goto out_free_resources; ++ ++ switch (ioc->hba_mpi_version_belonged) { ++ case MPI2_VERSION: ++ ioc->build_sg_scmd = &_base_build_sg_scmd; ++ ioc->build_sg = &_base_build_sg; ++ ioc->build_zero_len_sge = &_base_build_zero_len_sge; ++ break; ++ case MPI25_VERSION: ++ case MPI26_VERSION: ++ /* ++ * In SAS3.0, ++ * SCSI_IO, SMP_PASSTHRU, SATA_PASSTHRU, Target Assist, and ++ * Target Status - all require the IEEE formated scatter gather ++ * elements. ++ */ ++ ioc->build_sg_scmd = &_base_build_sg_scmd_ieee; ++ ioc->build_sg = &_base_build_sg_ieee; ++ ioc->build_zero_len_sge = &_base_build_zero_len_sge_ieee; ++ ioc->sge_size_ieee = sizeof(Mpi2IeeeSgeSimple64_t); ++ break; ++ } ++ ++ /* ++ * These function pointers for other requests that don't ++ * the require IEEE scatter gather elements. ++ * ++ * For example Configuration Pages and SAS IOUNIT Control don't. ++ */ ++ ioc->build_sg_mpi = &_base_build_sg; ++ ioc->build_zero_len_sge_mpi = &_base_build_zero_len_sge; ++ ++ r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); ++ if (r) ++ goto out_free_resources; ++ ++ ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts, ++ sizeof(struct mpt2sas_port_facts), GFP_KERNEL); ++ if (!ioc->pfacts) { ++ r = -ENOMEM; ++ goto out_free_resources; ++ } ++ ++ for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) { ++ r = _base_get_port_facts(ioc, i, CAN_SLEEP); ++ if (r) ++ goto out_free_resources; ++ } ++ ++ r = _base_allocate_memory_pools(ioc, CAN_SLEEP); ++ if (r) ++ goto out_free_resources; ++ ++ init_waitqueue_head(&ioc->reset_wq); ++ ++ /* allocate memory pd handle bitmask list */ ++ ioc->pd_handles_sz = (ioc->facts.MaxDevHandle / 8); ++ if (ioc->facts.MaxDevHandle % 8) ++ ioc->pd_handles_sz++; ++ ioc->pd_handles = kzalloc(ioc->pd_handles_sz, ++ GFP_KERNEL); ++ if (!ioc->pd_handles) { ++ r = -ENOMEM; ++ goto out_free_resources; ++ } ++ ioc->blocking_handles = kzalloc(ioc->pd_handles_sz, ++ GFP_KERNEL); ++ if (!ioc->blocking_handles) { ++ r = -ENOMEM; ++ goto out_free_resources; ++ } ++ ++ ioc->fwfault_debug = mpt2sas_fwfault_debug; ++ ++ /* base internal command bits */ ++ mutex_init(&ioc->base_cmds.mutex); ++ ioc->base_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ ++ /* port_enable command bits */ ++ ioc->port_enable_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ++ ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED; ++ ++ /* transport internal command bits */ ++ ioc->transport_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ++ ioc->transport_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_init(&ioc->transport_cmds.mutex); ++ ++ /* scsih internal command bits */ ++ ioc->scsih_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ++ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_init(&ioc->scsih_cmds.mutex); ++ ++ /* task management internal command bits */ ++ ioc->tm_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ++ ioc->tm_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_init(&ioc->tm_cmds.mutex); ++ ++ /* config page internal command bits */ ++ ioc->config_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ++ ioc->config_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_init(&ioc->config_cmds.mutex); ++ ++ /* ctl module internal command bits */ ++ ioc->ctl_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ++ ioc->ctl_cmds.sense = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL); ++ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_init(&ioc->ctl_cmds.mutex); ++ ++ if (!ioc->base_cmds.reply || !ioc->transport_cmds.reply || ++ !ioc->scsih_cmds.reply || !ioc->tm_cmds.reply || ++ !ioc->config_cmds.reply || !ioc->ctl_cmds.reply || ++ !ioc->ctl_cmds.sense) { ++ r = -ENOMEM; ++ goto out_free_resources; ++ } ++ ++ for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) ++ ioc->event_masks[i] = -1; ++ ++ /* here we enable the events we care about */ ++ _base_unmask_events(ioc, MPI2_EVENT_SAS_DISCOVERY); ++ _base_unmask_events(ioc, MPI2_EVENT_SAS_BROADCAST_PRIMITIVE); ++ _base_unmask_events(ioc, MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST); ++ _base_unmask_events(ioc, MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE); ++ _base_unmask_events(ioc, MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE); ++ _base_unmask_events(ioc, MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST); ++ _base_unmask_events(ioc, MPI2_EVENT_IR_VOLUME); ++ _base_unmask_events(ioc, MPI2_EVENT_IR_PHYSICAL_DISK); ++ _base_unmask_events(ioc, MPI2_EVENT_IR_OPERATION_STATUS); ++ _base_unmask_events(ioc, MPI2_EVENT_LOG_ENTRY_ADDED); ++ _base_unmask_events(ioc, MPI2_EVENT_TEMP_THRESHOLD); ++ if (ioc->hba_mpi_version_belonged == MPI26_VERSION) ++ _base_unmask_events(ioc, MPI2_EVENT_ACTIVE_CABLE_EXCEPTION); ++ ++ r = _base_make_ioc_operational(ioc, CAN_SLEEP); ++ if (r) ++ goto out_free_resources; ++ ++ ioc->non_operational_loop = 0; ++ return 0; ++ ++ out_free_resources: ++ ++ ioc->remove_host = 1; ++ ++ mpt2sas_base_free_resources(ioc); ++ _base_release_memory_pools(ioc); ++ pci_set_drvdata(ioc->pdev, NULL); ++ kfree(ioc->cpu_msix_table); ++ if (ioc->is_warpdrive) ++ kfree(ioc->reply_post_host_index); ++ kfree(ioc->pd_handles); ++ kfree(ioc->blocking_handles); ++ kfree(ioc->tm_cmds.reply); ++ kfree(ioc->transport_cmds.reply); ++ kfree(ioc->scsih_cmds.reply); ++ kfree(ioc->config_cmds.reply); ++ kfree(ioc->base_cmds.reply); ++ kfree(ioc->port_enable_cmds.reply); ++ kfree(ioc->ctl_cmds.reply); ++ kfree(ioc->ctl_cmds.sense); ++ kfree(ioc->pfacts); ++ ioc->ctl_cmds.reply = NULL; ++ ioc->base_cmds.reply = NULL; ++ ioc->tm_cmds.reply = NULL; ++ ioc->scsih_cmds.reply = NULL; ++ ioc->transport_cmds.reply = NULL; ++ ioc->config_cmds.reply = NULL; ++ ioc->pfacts = NULL; ++ return r; ++} ++ ++ ++/** ++ * mpt2sas_base_detach - remove controller instance ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_base_detach(struct MPT3SAS_ADAPTER *ioc) ++{ ++ dexitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ mpt2sas_base_stop_watchdog(ioc); ++ mpt2sas_base_free_resources(ioc); ++ _base_release_memory_pools(ioc); ++ pci_set_drvdata(ioc->pdev, NULL); ++ kfree(ioc->cpu_msix_table); ++ if (ioc->is_warpdrive) ++ kfree(ioc->reply_post_host_index); ++ kfree(ioc->pd_handles); ++ kfree(ioc->blocking_handles); ++ kfree(ioc->pfacts); ++ kfree(ioc->ctl_cmds.reply); ++ kfree(ioc->ctl_cmds.sense); ++ kfree(ioc->base_cmds.reply); ++ kfree(ioc->port_enable_cmds.reply); ++ kfree(ioc->tm_cmds.reply); ++ kfree(ioc->transport_cmds.reply); ++ kfree(ioc->scsih_cmds.reply); ++ kfree(ioc->config_cmds.reply); ++} ++ ++/** ++ * _base_reset_handler - reset callback handler (for base) ++ * @ioc: per adapter object ++ * @reset_phase: phase ++ * ++ * The handler for doing any required cleanup or initialization. ++ * ++ * The reset phase can be MPT3_IOC_PRE_RESET, MPT3_IOC_AFTER_RESET, ++ * MPT3_IOC_DONE_RESET ++ * ++ * Return nothing. ++ */ ++static void ++_base_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase) ++{ ++ mpt2sas_scsih_reset_handler(ioc, reset_phase); ++ mpt2sas_ctl_reset_handler(ioc, reset_phase); ++ switch (reset_phase) { ++ case MPT3_IOC_PRE_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_PRE_RESET\n", ioc->name, __func__)); ++ break; ++ case MPT3_IOC_AFTER_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_AFTER_RESET\n", ioc->name, __func__)); ++ if (ioc->transport_cmds.status & MPT3_CMD_PENDING) { ++ ioc->transport_cmds.status |= MPT3_CMD_RESET; ++ mpt2sas_base_free_smid(ioc, ioc->transport_cmds.smid); ++ complete(&ioc->transport_cmds.done); ++ } ++ if (ioc->base_cmds.status & MPT3_CMD_PENDING) { ++ ioc->base_cmds.status |= MPT3_CMD_RESET; ++ mpt2sas_base_free_smid(ioc, ioc->base_cmds.smid); ++ complete(&ioc->base_cmds.done); ++ } ++ if (ioc->port_enable_cmds.status & MPT3_CMD_PENDING) { ++ ioc->port_enable_failed = 1; ++ ioc->port_enable_cmds.status |= MPT3_CMD_RESET; ++ mpt2sas_base_free_smid(ioc, ioc->port_enable_cmds.smid); ++ if (ioc->is_driver_loading) { ++ ioc->start_scan_failed = ++ MPI2_IOCSTATUS_INTERNAL_ERROR; ++ ioc->start_scan = 0; ++ ioc->port_enable_cmds.status = ++ MPT3_CMD_NOT_USED; ++ } else ++ complete(&ioc->port_enable_cmds.done); ++ } ++ if (ioc->config_cmds.status & MPT3_CMD_PENDING) { ++ ioc->config_cmds.status |= MPT3_CMD_RESET; ++ mpt2sas_base_free_smid(ioc, ioc->config_cmds.smid); ++ ioc->config_cmds.smid = USHRT_MAX; ++ complete(&ioc->config_cmds.done); ++ } ++ break; ++ case MPT3_IOC_DONE_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_DONE_RESET\n", ioc->name, __func__)); ++ break; ++ } ++} ++ ++/** ++ * _wait_for_commands_to_complete - reset controller ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * ++ * This function waiting(3s) for all pending commands to complete ++ * prior to putting controller in reset. ++ */ ++static void ++_wait_for_commands_to_complete(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ++{ ++ u32 ioc_state; ++ unsigned long flags; ++ u16 i; ++ ++ ioc->pending_io_count = 0; ++ if (sleep_flag != CAN_SLEEP) ++ return; ++ ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 0); ++ if ((ioc_state & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL) ++ return; ++ ++ /* pending command count */ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ for (i = 0; i < ioc->scsiio_depth; i++) ++ if (ioc->scsi_lookup[i].cb_idx != 0xFF) ++ ioc->pending_io_count++; ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ ++ if (!ioc->pending_io_count) ++ return; ++ ++ /* wait for pending commands to complete */ ++ wait_event_timeout(ioc->reset_wq, ioc->pending_io_count == 0, 10 * HZ); ++} ++ ++/** ++ * mpt2sas_base_hard_reset_handler - reset controller ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @sleep_flag: CAN_SLEEP or NO_SLEEP ++ * @type: FORCE_BIG_HAMMER or SOFT_RESET ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, ++ enum reset_type type) ++{ ++ int r; ++ unsigned long flags; ++ u32 ioc_state; ++ u8 is_fault = 0, is_trigger = 0; ++ ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name, ++ __func__)); ++ ++ if (ioc->pci_error_recovery) { ++ pr_err(MPT3SAS_FMT "%s: pci error recovery reset\n", ++ ioc->name, __func__); ++ r = 0; ++ goto out_unlocked; ++ } ++ ++ if (mpt2sas_fwfault_debug) ++ mpt2sas_halt_firmware(ioc); ++ ++ /* TODO - What we really should be doing is pulling ++ * out all the code associated with NO_SLEEP; its never used. ++ * That is legacy code from mpt fusion driver, ported over. ++ * I will leave this BUG_ON here for now till its been resolved. ++ */ ++ BUG_ON(sleep_flag == NO_SLEEP); ++ ++ /* wait for an active reset in progress to complete */ ++ if (!mutex_trylock(&ioc->reset_in_progress_mutex)) { ++ do { ++ ssleep(1); ++ } while (ioc->shost_recovery == 1); ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++ return ioc->ioc_reset_in_progress_status; ++ } ++ ++ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ++ ioc->shost_recovery = 1; ++ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); ++ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) && ++ (!(ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED))) { ++ is_trigger = 1; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 0); ++ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) ++ is_fault = 1; ++ } ++ _base_reset_handler(ioc, MPT3_IOC_PRE_RESET); ++ _wait_for_commands_to_complete(ioc, sleep_flag); ++ _base_mask_interrupts(ioc); ++ r = _base_make_ioc_ready(ioc, sleep_flag, type); ++ if (r) ++ goto out; ++ _base_reset_handler(ioc, MPT3_IOC_AFTER_RESET); ++ ++ /* If this hard reset is called while port enable is active, then ++ * there is no reason to call make_ioc_operational ++ */ ++ if (ioc->is_driver_loading && ioc->port_enable_failed) { ++ ioc->remove_host = 1; ++ r = -EFAULT; ++ goto out; ++ } ++ r = _base_get_ioc_facts(ioc, CAN_SLEEP); ++ if (r) ++ goto out; ++ ++ if (ioc->rdpq_array_enable && !ioc->rdpq_array_capable) ++ panic("%s: Issue occurred with flashing controller firmware." ++ "Please reboot the system and ensure that the correct" ++ " firmware version is running\n", ioc->name); ++ ++ r = _base_make_ioc_operational(ioc, sleep_flag); ++ if (!r) ++ _base_reset_handler(ioc, MPT3_IOC_DONE_RESET); ++ ++ out: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT "%s: %s\n", ++ ioc->name, __func__, ((r == 0) ? "SUCCESS" : "FAILED"))); ++ ++ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ++ ioc->ioc_reset_in_progress_status = r; ++ ioc->shost_recovery = 0; ++ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); ++ ioc->ioc_reset_count++; ++ mutex_unlock(&ioc->reset_in_progress_mutex); ++ ++ out_unlocked: ++ if ((r == 0) && is_trigger) { ++ if (is_fault) ++ mpt2sas_trigger_master(ioc, MASTER_TRIGGER_FW_FAULT); ++ else ++ mpt2sas_trigger_master(ioc, ++ MASTER_TRIGGER_ADAPTER_RESET); ++ } ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++ return r; ++} +diff --git a/drivers/scsi/mpt2sas/mpt3sas_base.h b/drivers/scsi/mpt2sas/mpt3sas_base.h +new file mode 100644 +index 0000000..a580770 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_base.h +@@ -0,0 +1,1462 @@ ++/* ++ * This is the Fusion MPT base driver providing common API layer interface ++ * for access to MPT (Message Passing Technology) firmware. ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.h ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#ifndef MPT3SAS_BASE_H_INCLUDED ++#define MPT3SAS_BASE_H_INCLUDED ++ ++#include "mpi/mpi2_type.h" ++#include "mpi/mpi2.h" ++#include "mpi/mpi2_ioc.h" ++#include "mpi/mpi2_cnfg.h" ++#include "mpi/mpi2_init.h" ++#include "mpi/mpi2_raid.h" ++#include "mpi/mpi2_tool.h" ++#include "mpi/mpi2_sas.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mpt3sas_debug.h" ++#include "mpt3sas_trigger_diag.h" ++ ++/* driver versioning info */ ++#define MPT3SAS_DRIVER_NAME "mpt3sas" ++#define MPT3SAS_AUTHOR "Avago Technologies " ++#define MPT3SAS_DESCRIPTION "LSI MPT Fusion SAS 3.0 Device Driver" ++#define MPT3SAS_DRIVER_VERSION "13.100.00.00" ++#define MPT3SAS_MAJOR_VERSION 13 ++#define MPT3SAS_MINOR_VERSION 100 ++#define MPT3SAS_BUILD_VERSION 0 ++#define MPT3SAS_RELEASE_VERSION 00 ++ ++#define MPT2SAS_DRIVER_NAME "mpt2sas" ++#define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver" ++#define MPT2SAS_DRIVER_VERSION "20.102.00.00" ++#define MPT2SAS_MAJOR_VERSION 20 ++#define MPT2SAS_MINOR_VERSION 102 ++#define MPT2SAS_BUILD_VERSION 0 ++#define MPT2SAS_RELEASE_VERSION 00 ++ ++/* ++ * Set MPT3SAS_SG_DEPTH value based on user input. ++ */ ++#define MPT_MAX_PHYS_SEGMENTS SCSI_MAX_SG_SEGMENTS ++#define MPT_MIN_PHYS_SEGMENTS 16 ++ ++#ifdef CONFIG_SCSI_MPT3SAS_MAX_SGE ++#define MPT3SAS_SG_DEPTH CONFIG_SCSI_MPT3SAS_MAX_SGE ++#else ++#define MPT3SAS_SG_DEPTH MPT_MAX_PHYS_SEGMENTS ++#endif ++ ++#ifdef CONFIG_SCSI_MPT2SAS_MAX_SGE ++#define MPT2SAS_SG_DEPTH CONFIG_SCSI_MPT2SAS_MAX_SGE ++#else ++#define MPT2SAS_SG_DEPTH MPT_MAX_PHYS_SEGMENTS ++#endif ++ ++/* ++ * Generic Defines ++ */ ++#define MPT3SAS_SATA_QUEUE_DEPTH 32 ++#define MPT3SAS_SAS_QUEUE_DEPTH 254 ++#define MPT3SAS_RAID_QUEUE_DEPTH 128 ++ ++#define MPT3SAS_RAID_MAX_SECTORS 8192 ++ ++#define MPT_NAME_LENGTH 32 /* generic length of strings */ ++#define MPT_STRING_LENGTH 64 ++ ++#define MPT_MAX_CALLBACKS 32 ++ ++ ++#define CAN_SLEEP 1 ++#define NO_SLEEP 0 ++ ++#define INTERNAL_CMDS_COUNT 10 /* reserved cmds */ ++/* reserved for issuing internally framed scsi io cmds */ ++#define INTERNAL_SCSIIO_CMDS_COUNT 3 ++ ++#define MPI3_HIM_MASK 0xFFFFFFFF /* mask every bit*/ ++ ++#define MPT3SAS_INVALID_DEVICE_HANDLE 0xFFFF ++ ++#define MAX_CHAIN_ELEMT_SZ 16 ++#define DEFAULT_NUM_FWCHAIN_ELEMTS 8 ++ ++/* ++ * reset phases ++ */ ++#define MPT3_IOC_PRE_RESET 1 /* prior to host reset */ ++#define MPT3_IOC_AFTER_RESET 2 /* just after host reset */ ++#define MPT3_IOC_DONE_RESET 3 /* links re-initialized */ ++ ++/* ++ * logging format ++ */ ++#define MPT3SAS_FMT "%s: " ++ ++/* ++ * WarpDrive Specific Log codes ++ */ ++ ++#define MPT2_WARPDRIVE_LOGENTRY (0x8002) ++#define MPT2_WARPDRIVE_LC_SSDT (0x41) ++#define MPT2_WARPDRIVE_LC_SSDLW (0x43) ++#define MPT2_WARPDRIVE_LC_SSDLF (0x44) ++#define MPT2_WARPDRIVE_LC_BRMF (0x4D) ++ ++/* ++ * per target private data ++ */ ++#define MPT_TARGET_FLAGS_RAID_COMPONENT 0x01 ++#define MPT_TARGET_FLAGS_VOLUME 0x02 ++#define MPT_TARGET_FLAGS_DELETED 0x04 ++#define MPT_TARGET_FASTPATH_IO 0x08 ++ ++#define SAS2_PCI_DEVICE_B0_REVISION (0x01) ++#define SAS3_PCI_DEVICE_C0_REVISION (0x02) ++ ++/* ++ * Intel HBA branding ++ */ ++#define MPT2SAS_INTEL_RMS25JB080_BRANDING \ ++ "Intel(R) Integrated RAID Module RMS25JB080" ++#define MPT2SAS_INTEL_RMS25JB040_BRANDING \ ++ "Intel(R) Integrated RAID Module RMS25JB040" ++#define MPT2SAS_INTEL_RMS25KB080_BRANDING \ ++ "Intel(R) Integrated RAID Module RMS25KB080" ++#define MPT2SAS_INTEL_RMS25KB040_BRANDING \ ++ "Intel(R) Integrated RAID Module RMS25KB040" ++#define MPT2SAS_INTEL_RMS25LB040_BRANDING \ ++ "Intel(R) Integrated RAID Module RMS25LB040" ++#define MPT2SAS_INTEL_RMS25LB080_BRANDING \ ++ "Intel(R) Integrated RAID Module RMS25LB080" ++#define MPT2SAS_INTEL_RMS2LL080_BRANDING \ ++ "Intel Integrated RAID Module RMS2LL080" ++#define MPT2SAS_INTEL_RMS2LL040_BRANDING \ ++ "Intel Integrated RAID Module RMS2LL040" ++#define MPT2SAS_INTEL_RS25GB008_BRANDING \ ++ "Intel(R) RAID Controller RS25GB008" ++#define MPT2SAS_INTEL_SSD910_BRANDING \ ++ "Intel(R) SSD 910 Series" ++ ++#define MPT3SAS_INTEL_RMS3JC080_BRANDING \ ++ "Intel(R) Integrated RAID Module RMS3JC080" ++#define MPT3SAS_INTEL_RS3GC008_BRANDING \ ++ "Intel(R) RAID Controller RS3GC008" ++#define MPT3SAS_INTEL_RS3FC044_BRANDING \ ++ "Intel(R) RAID Controller RS3FC044" ++#define MPT3SAS_INTEL_RS3UC080_BRANDING \ ++ "Intel(R) RAID Controller RS3UC080" ++ ++/* ++ * Intel HBA SSDIDs ++ */ ++#define MPT2SAS_INTEL_RMS25JB080_SSDID 0x3516 ++#define MPT2SAS_INTEL_RMS25JB040_SSDID 0x3517 ++#define MPT2SAS_INTEL_RMS25KB080_SSDID 0x3518 ++#define MPT2SAS_INTEL_RMS25KB040_SSDID 0x3519 ++#define MPT2SAS_INTEL_RMS25LB040_SSDID 0x351A ++#define MPT2SAS_INTEL_RMS25LB080_SSDID 0x351B ++#define MPT2SAS_INTEL_RMS2LL080_SSDID 0x350E ++#define MPT2SAS_INTEL_RMS2LL040_SSDID 0x350F ++#define MPT2SAS_INTEL_RS25GB008_SSDID 0x3000 ++#define MPT2SAS_INTEL_SSD910_SSDID 0x3700 ++ ++#define MPT3SAS_INTEL_RMS3JC080_SSDID 0x3521 ++#define MPT3SAS_INTEL_RS3GC008_SSDID 0x3522 ++#define MPT3SAS_INTEL_RS3FC044_SSDID 0x3523 ++#define MPT3SAS_INTEL_RS3UC080_SSDID 0x3524 ++ ++/* ++ * Dell HBA branding ++ */ ++#define MPT2SAS_DELL_BRANDING_SIZE 32 ++ ++#define MPT2SAS_DELL_6GBPS_SAS_HBA_BRANDING "Dell 6Gbps SAS HBA" ++#define MPT2SAS_DELL_PERC_H200_ADAPTER_BRANDING "Dell PERC H200 Adapter" ++#define MPT2SAS_DELL_PERC_H200_INTEGRATED_BRANDING "Dell PERC H200 Integrated" ++#define MPT2SAS_DELL_PERC_H200_MODULAR_BRANDING "Dell PERC H200 Modular" ++#define MPT2SAS_DELL_PERC_H200_EMBEDDED_BRANDING "Dell PERC H200 Embedded" ++#define MPT2SAS_DELL_PERC_H200_BRANDING "Dell PERC H200" ++#define MPT2SAS_DELL_6GBPS_SAS_BRANDING "Dell 6Gbps SAS" ++ ++#define MPT3SAS_DELL_12G_HBA_BRANDING \ ++ "Dell 12Gbps HBA" ++ ++/* ++ * Dell HBA SSDIDs ++ */ ++#define MPT2SAS_DELL_6GBPS_SAS_HBA_SSDID 0x1F1C ++#define MPT2SAS_DELL_PERC_H200_ADAPTER_SSDID 0x1F1D ++#define MPT2SAS_DELL_PERC_H200_INTEGRATED_SSDID 0x1F1E ++#define MPT2SAS_DELL_PERC_H200_MODULAR_SSDID 0x1F1F ++#define MPT2SAS_DELL_PERC_H200_EMBEDDED_SSDID 0x1F20 ++#define MPT2SAS_DELL_PERC_H200_SSDID 0x1F21 ++#define MPT2SAS_DELL_6GBPS_SAS_SSDID 0x1F22 ++ ++#define MPT3SAS_DELL_12G_HBA_SSDID 0x1F46 ++ ++/* ++ * Cisco HBA branding ++ */ ++#define MPT3SAS_CISCO_12G_8E_HBA_BRANDING \ ++ "Cisco 9300-8E 12G SAS HBA" ++#define MPT3SAS_CISCO_12G_8I_HBA_BRANDING \ ++ "Cisco 9300-8i 12G SAS HBA" ++#define MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING \ ++ "Cisco 12G Modular SAS Pass through Controller" ++#define MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_BRANDING \ ++ "UCS C3X60 12G SAS Pass through Controller" ++/* ++ * Cisco HBA SSSDIDs ++ */ ++#define MPT3SAS_CISCO_12G_8E_HBA_SSDID 0x14C ++#define MPT3SAS_CISCO_12G_8I_HBA_SSDID 0x154 ++#define MPT3SAS_CISCO_12G_AVILA_HBA_SSDID 0x155 ++#define MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_SSDID 0x156 ++ ++/* ++ * status bits for ioc->diag_buffer_status ++ */ ++#define MPT3_DIAG_BUFFER_IS_REGISTERED (0x01) ++#define MPT3_DIAG_BUFFER_IS_RELEASED (0x02) ++#define MPT3_DIAG_BUFFER_IS_DIAG_RESET (0x04) ++ ++/* ++ * HP HBA branding ++ */ ++#define MPT2SAS_HP_3PAR_SSVID 0x1590 ++ ++#define MPT2SAS_HP_2_4_INTERNAL_BRANDING \ ++ "HP H220 Host Bus Adapter" ++#define MPT2SAS_HP_2_4_EXTERNAL_BRANDING \ ++ "HP H221 Host Bus Adapter" ++#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING \ ++ "HP H222 Host Bus Adapter" ++#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING \ ++ "HP H220i Host Bus Adapter" ++#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING \ ++ "HP H210i Host Bus Adapter" ++ ++/* ++ * HO HBA SSDIDs ++ */ ++#define MPT2SAS_HP_2_4_INTERNAL_SSDID 0x0041 ++#define MPT2SAS_HP_2_4_EXTERNAL_SSDID 0x0042 ++#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID 0x0043 ++#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID 0x0044 ++#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID 0x0046 ++ ++/* ++ * Combined Reply Queue constants, ++ * There are twelve Supplemental Reply Post Host Index Registers ++ * and each register is at offset 0x10 bytes from the previous one. ++ */ ++#define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT 12 ++#define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET (0x10) ++ ++/* OEM Identifiers */ ++#define MFG10_OEM_ID_INVALID (0x00000000) ++#define MFG10_OEM_ID_DELL (0x00000001) ++#define MFG10_OEM_ID_FSC (0x00000002) ++#define MFG10_OEM_ID_SUN (0x00000003) ++#define MFG10_OEM_ID_IBM (0x00000004) ++ ++/* GENERIC Flags 0*/ ++#define MFG10_GF0_OCE_DISABLED (0x00000001) ++#define MFG10_GF0_R1E_DRIVE_COUNT (0x00000002) ++#define MFG10_GF0_R10_DISPLAY (0x00000004) ++#define MFG10_GF0_SSD_DATA_SCRUB_DISABLE (0x00000008) ++#define MFG10_GF0_SINGLE_DRIVE_R0 (0x00000010) ++ ++#define VIRTUAL_IO_FAILED_RETRY (0x32010081) ++ ++/* OEM Specific Flags will come from OEM specific header files */ ++struct Mpi2ManufacturingPage10_t { ++ MPI2_CONFIG_PAGE_HEADER Header; /* 00h */ ++ U8 OEMIdentifier; /* 04h */ ++ U8 Reserved1; /* 05h */ ++ U16 Reserved2; /* 08h */ ++ U32 Reserved3; /* 0Ch */ ++ U32 GenericFlags0; /* 10h */ ++ U32 GenericFlags1; /* 14h */ ++ U32 Reserved4; /* 18h */ ++ U32 OEMSpecificFlags0; /* 1Ch */ ++ U32 OEMSpecificFlags1; /* 20h */ ++ U32 Reserved5[18]; /* 24h - 60h*/ ++}; ++ ++ ++/* Miscellaneous options */ ++struct Mpi2ManufacturingPage11_t { ++ MPI2_CONFIG_PAGE_HEADER Header; /* 00h */ ++ __le32 Reserved1; /* 04h */ ++ u8 Reserved2; /* 08h */ ++ u8 EEDPTagMode; /* 09h */ ++ u8 Reserved3; /* 0Ah */ ++ u8 Reserved4; /* 0Bh */ ++ __le32 Reserved5[23]; /* 0Ch-60h*/ ++}; ++ ++/** ++ * struct MPT3SAS_TARGET - starget private hostdata ++ * @starget: starget object ++ * @sas_address: target sas address ++ * @raid_device: raid_device pointer to access volume data ++ * @handle: device handle ++ * @num_luns: number luns ++ * @flags: MPT_TARGET_FLAGS_XXX flags ++ * @deleted: target flaged for deletion ++ * @tm_busy: target is busy with TM request. ++ * @sdev: The sas_device associated with this target ++ */ ++struct MPT3SAS_TARGET { ++ struct scsi_target *starget; ++ u64 sas_address; ++ struct _raid_device *raid_device; ++ u16 handle; ++ int num_luns; ++ u32 flags; ++ u8 deleted; ++ u8 tm_busy; ++ struct _sas_device *sdev; ++}; ++ ++ ++/* ++ * per device private data ++ */ ++#define MPT_DEVICE_FLAGS_INIT 0x01 ++#define MPT_DEVICE_TLR_ON 0x02 ++ ++#define MFG_PAGE10_HIDE_SSDS_MASK (0x00000003) ++#define MFG_PAGE10_HIDE_ALL_DISKS (0x00) ++#define MFG_PAGE10_EXPOSE_ALL_DISKS (0x01) ++#define MFG_PAGE10_HIDE_IF_VOL_PRESENT (0x02) ++ ++/** ++ * struct MPT3SAS_DEVICE - sdev private hostdata ++ * @sas_target: starget private hostdata ++ * @lun: lun number ++ * @flags: MPT_DEVICE_XXX flags ++ * @configured_lun: lun is configured ++ * @block: device is in SDEV_BLOCK state ++ * @tlr_snoop_check: flag used in determining whether to disable TLR ++ * @eedp_enable: eedp support enable bit ++ * @eedp_type: 0(type_1), 1(type_2), 2(type_3) ++ * @eedp_block_length: block size ++ */ ++struct MPT3SAS_DEVICE { ++ struct MPT3SAS_TARGET *sas_target; ++ unsigned int lun; ++ u32 flags; ++ u8 configured_lun; ++ u8 block; ++ u8 tlr_snoop_check; ++ u8 ignore_delay_remove; ++}; ++ ++#define MPT3_CMD_NOT_USED 0x8000 /* free */ ++#define MPT3_CMD_COMPLETE 0x0001 /* completed */ ++#define MPT3_CMD_PENDING 0x0002 /* pending */ ++#define MPT3_CMD_REPLY_VALID 0x0004 /* reply is valid */ ++#define MPT3_CMD_RESET 0x0008 /* host reset dropped the command */ ++ ++/** ++ * struct _internal_cmd - internal commands struct ++ * @mutex: mutex ++ * @done: completion ++ * @reply: reply message pointer ++ * @sense: sense data ++ * @status: MPT3_CMD_XXX status ++ * @smid: system message id ++ */ ++struct _internal_cmd { ++ struct mutex mutex; ++ struct completion done; ++ void *reply; ++ void *sense; ++ u16 status; ++ u16 smid; ++}; ++ ++ ++ ++/** ++ * struct _sas_device - attached device information ++ * @list: sas device list ++ * @starget: starget object ++ * @sas_address: device sas address ++ * @device_name: retrieved from the SAS IDENTIFY frame. ++ * @handle: device handle ++ * @sas_address_parent: sas address of parent expander or sas host ++ * @enclosure_handle: enclosure handle ++ * @enclosure_logical_id: enclosure logical identifier ++ * @volume_handle: volume handle (valid when hidden raid member) ++ * @volume_wwid: volume unique identifier ++ * @device_info: bitfield provides detailed info about the device ++ * @id: target id ++ * @channel: target channel ++ * @slot: number number ++ * @phy: phy identifier provided in sas device page 0 ++ * @responding: used in _scsih_sas_device_mark_responding ++ * @fast_path: fast path feature enable bit ++ * @pfa_led_on: flag for PFA LED status ++ * @pend_sas_rphy_add: flag to check if device is in sas_rphy_add() ++ * addition routine. ++ */ ++struct _sas_device { ++ struct list_head list; ++ struct scsi_target *starget; ++ u64 sas_address; ++ u64 device_name; ++ u16 handle; ++ u64 sas_address_parent; ++ u16 enclosure_handle; ++ u64 enclosure_logical_id; ++ u16 volume_handle; ++ u64 volume_wwid; ++ u32 device_info; ++ int id; ++ int channel; ++ u16 slot; ++ u8 phy; ++ u8 responding; ++ u8 fast_path; ++ u8 pfa_led_on; ++ u8 pend_sas_rphy_add; ++ u8 enclosure_level; ++ u8 connector_name[4]; ++ struct kref refcount; ++}; ++ ++static inline void sas_device_get(struct _sas_device *s) ++{ ++ kref_get(&s->refcount); ++} ++ ++static inline void sas_device_free(struct kref *r) ++{ ++ kfree(container_of(r, struct _sas_device, refcount)); ++} ++ ++static inline void sas_device_put(struct _sas_device *s) ++{ ++ kref_put(&s->refcount, sas_device_free); ++} ++ ++/** ++ * struct _raid_device - raid volume link list ++ * @list: sas device list ++ * @starget: starget object ++ * @sdev: scsi device struct (volumes are single lun) ++ * @wwid: unique identifier for the volume ++ * @handle: device handle ++ * @block_size: Block size of the volume ++ * @id: target id ++ * @channel: target channel ++ * @volume_type: the raid level ++ * @device_info: bitfield provides detailed info about the hidden components ++ * @num_pds: number of hidden raid components ++ * @responding: used in _scsih_raid_device_mark_responding ++ * @percent_complete: resync percent complete ++ * @direct_io_enabled: Whether direct io to PDs are allowed or not ++ * @stripe_exponent: X where 2powX is the stripe sz in blocks ++ * @block_exponent: X where 2powX is the block sz in bytes ++ * @max_lba: Maximum number of LBA in the volume ++ * @stripe_sz: Stripe Size of the volume ++ * @device_info: Device info of the volume member disk ++ * @pd_handle: Array of handles of the physical drives for direct I/O in le16 ++ */ ++#define MPT_MAX_WARPDRIVE_PDS 8 ++struct _raid_device { ++ struct list_head list; ++ struct scsi_target *starget; ++ struct scsi_device *sdev; ++ u64 wwid; ++ u16 handle; ++ u16 block_sz; ++ int id; ++ int channel; ++ u8 volume_type; ++ u8 num_pds; ++ u8 responding; ++ u8 percent_complete; ++ u8 direct_io_enabled; ++ u8 stripe_exponent; ++ u8 block_exponent; ++ u64 max_lba; ++ u32 stripe_sz; ++ u32 device_info; ++ u16 pd_handle[MPT_MAX_WARPDRIVE_PDS]; ++}; ++ ++/** ++ * struct _boot_device - boot device info ++ * @is_raid: flag to indicate whether this is volume ++ * @device: holds pointer for either struct _sas_device or ++ * struct _raid_device ++ */ ++struct _boot_device { ++ u8 is_raid; ++ void *device; ++}; ++ ++/** ++ * struct _sas_port - wide/narrow sas port information ++ * @port_list: list of ports belonging to expander ++ * @num_phys: number of phys belonging to this port ++ * @remote_identify: attached device identification ++ * @rphy: sas transport rphy object ++ * @port: sas transport wide/narrow port object ++ * @phy_list: _sas_phy list objects belonging to this port ++ */ ++struct _sas_port { ++ struct list_head port_list; ++ u8 num_phys; ++ struct sas_identify remote_identify; ++ struct sas_rphy *rphy; ++ struct sas_port *port; ++ struct list_head phy_list; ++}; ++ ++/** ++ * struct _sas_phy - phy information ++ * @port_siblings: list of phys belonging to a port ++ * @identify: phy identification ++ * @remote_identify: attached device identification ++ * @phy: sas transport phy object ++ * @phy_id: unique phy id ++ * @handle: device handle for this phy ++ * @attached_handle: device handle for attached device ++ * @phy_belongs_to_port: port has been created for this phy ++ */ ++struct _sas_phy { ++ struct list_head port_siblings; ++ struct sas_identify identify; ++ struct sas_identify remote_identify; ++ struct sas_phy *phy; ++ u8 phy_id; ++ u16 handle; ++ u16 attached_handle; ++ u8 phy_belongs_to_port; ++}; ++ ++/** ++ * struct _sas_node - sas_host/expander information ++ * @list: list of expanders ++ * @parent_dev: parent device class ++ * @num_phys: number phys belonging to this sas_host/expander ++ * @sas_address: sas address of this sas_host/expander ++ * @handle: handle for this sas_host/expander ++ * @sas_address_parent: sas address of parent expander or sas host ++ * @enclosure_handle: handle for this a member of an enclosure ++ * @device_info: bitwise defining capabilities of this sas_host/expander ++ * @responding: used in _scsih_expander_device_mark_responding ++ * @phy: a list of phys that make up this sas_host/expander ++ * @sas_port_list: list of ports attached to this sas_host/expander ++ */ ++struct _sas_node { ++ struct list_head list; ++ struct device *parent_dev; ++ u8 num_phys; ++ u64 sas_address; ++ u16 handle; ++ u64 sas_address_parent; ++ u16 enclosure_handle; ++ u64 enclosure_logical_id; ++ u8 responding; ++ struct _sas_phy *phy; ++ struct list_head sas_port_list; ++}; ++ ++/** ++ * enum reset_type - reset state ++ * @FORCE_BIG_HAMMER: issue diagnostic reset ++ * @SOFT_RESET: issue message_unit_reset, if fails to to big hammer ++ */ ++enum reset_type { ++ FORCE_BIG_HAMMER, ++ SOFT_RESET, ++}; ++ ++/** ++ * struct chain_tracker - firmware chain tracker ++ * @chain_buffer: chain buffer ++ * @chain_buffer_dma: physical address ++ * @tracker_list: list of free request (ioc->free_chain_list) ++ */ ++struct chain_tracker { ++ void *chain_buffer; ++ dma_addr_t chain_buffer_dma; ++ struct list_head tracker_list; ++}; ++ ++/** ++ * struct scsiio_tracker - scsi mf request tracker ++ * @smid: system message id ++ * @scmd: scsi request pointer ++ * @cb_idx: callback index ++ * @direct_io: To indicate whether I/O is direct (WARPDRIVE) ++ * @tracker_list: list of free request (ioc->free_list) ++ * @msix_io: IO's msix ++ */ ++struct scsiio_tracker { ++ u16 smid; ++ struct scsi_cmnd *scmd; ++ u8 cb_idx; ++ u8 direct_io; ++ struct list_head chain_list; ++ struct list_head tracker_list; ++ u16 msix_io; ++}; ++ ++/** ++ * struct request_tracker - firmware request tracker ++ * @smid: system message id ++ * @cb_idx: callback index ++ * @tracker_list: list of free request (ioc->free_list) ++ */ ++struct request_tracker { ++ u16 smid; ++ u8 cb_idx; ++ struct list_head tracker_list; ++}; ++ ++/** ++ * struct _tr_list - target reset list ++ * @handle: device handle ++ * @state: state machine ++ */ ++struct _tr_list { ++ struct list_head list; ++ u16 handle; ++ u16 state; ++}; ++ ++/** ++ * struct _sc_list - delayed SAS_IO_UNIT_CONTROL message list ++ * @handle: device handle ++ */ ++struct _sc_list { ++ struct list_head list; ++ u16 handle; ++}; ++ ++/** ++ * struct _event_ack_list - delayed event acknowledgment list ++ * @Event: Event ID ++ * @EventContext: used to track the event uniquely ++ */ ++struct _event_ack_list { ++ struct list_head list; ++ u16 Event; ++ u32 EventContext; ++}; ++ ++/** ++ * struct adapter_reply_queue - the reply queue struct ++ * @ioc: per adapter object ++ * @msix_index: msix index into vector table ++ * @vector: irq vector ++ * @reply_post_host_index: head index in the pool where FW completes IO ++ * @reply_post_free: reply post base virt address ++ * @name: the name registered to request_irq() ++ * @busy: isr is actively processing replies on another cpu ++ * @list: this list ++*/ ++struct adapter_reply_queue { ++ struct MPT3SAS_ADAPTER *ioc; ++ u8 msix_index; ++ unsigned int vector; ++ u32 reply_post_host_index; ++ Mpi2ReplyDescriptorsUnion_t *reply_post_free; ++ char name[MPT_NAME_LENGTH]; ++ atomic_t busy; ++ cpumask_var_t affinity_hint; ++ struct list_head list; ++}; ++ ++typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); ++ ++/* SAS3.0 support */ ++typedef int (*MPT_BUILD_SG_SCMD)(struct MPT3SAS_ADAPTER *ioc, ++ struct scsi_cmnd *scmd, u16 smid); ++typedef void (*MPT_BUILD_SG)(struct MPT3SAS_ADAPTER *ioc, void *psge, ++ dma_addr_t data_out_dma, size_t data_out_sz, ++ dma_addr_t data_in_dma, size_t data_in_sz); ++typedef void (*MPT_BUILD_ZERO_LEN_SGE)(struct MPT3SAS_ADAPTER *ioc, ++ void *paddr); ++ ++ ++ ++/* IOC Facts and Port Facts converted from little endian to cpu */ ++union mpi3_version_union { ++ MPI2_VERSION_STRUCT Struct; ++ u32 Word; ++}; ++ ++struct mpt2sas_facts { ++ u16 MsgVersion; ++ u16 HeaderVersion; ++ u8 IOCNumber; ++ u8 VP_ID; ++ u8 VF_ID; ++ u16 IOCExceptions; ++ u16 IOCStatus; ++ u32 IOCLogInfo; ++ u8 MaxChainDepth; ++ u8 WhoInit; ++ u8 NumberOfPorts; ++ u8 MaxMSIxVectors; ++ u16 RequestCredit; ++ u16 ProductID; ++ u32 IOCCapabilities; ++ union mpi3_version_union FWVersion; ++ u16 IOCRequestFrameSize; ++ u16 IOCMaxChainSegmentSize; ++ u16 MaxInitiators; ++ u16 MaxTargets; ++ u16 MaxSasExpanders; ++ u16 MaxEnclosures; ++ u16 ProtocolFlags; ++ u16 HighPriorityCredit; ++ u16 MaxReplyDescriptorPostQueueDepth; ++ u8 ReplyFrameSize; ++ u8 MaxVolumes; ++ u16 MaxDevHandle; ++ u16 MaxPersistentEntries; ++ u16 MinDevHandle; ++}; ++ ++struct mpt2sas_port_facts { ++ u8 PortNumber; ++ u8 VP_ID; ++ u8 VF_ID; ++ u8 PortType; ++ u16 MaxPostedCmdBuffers; ++}; ++ ++struct reply_post_struct { ++ Mpi2ReplyDescriptorsUnion_t *reply_post_free; ++ dma_addr_t reply_post_free_dma; ++}; ++ ++/** ++ * enum mutex_type - task management mutex type ++ * @TM_MUTEX_OFF: mutex is not required becuase calling function is acquiring it ++ * @TM_MUTEX_ON: mutex is required ++ */ ++enum mutex_type { ++ TM_MUTEX_OFF = 0, ++ TM_MUTEX_ON = 1, ++}; ++ ++typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc); ++/** ++ * struct MPT3SAS_ADAPTER - per adapter struct ++ * @list: ioc_list ++ * @shost: shost object ++ * @id: unique adapter id ++ * @cpu_count: number online cpus ++ * @name: generic ioc string ++ * @tmp_string: tmp string used for logging ++ * @pdev: pci pdev object ++ * @pio_chip: physical io register space ++ * @chip: memory mapped register space ++ * @chip_phys: physical addrss prior to mapping ++ * @logging_level: see mpt3sas_debug.h ++ * @fwfault_debug: debuging FW timeouts ++ * @ir_firmware: IR firmware present ++ * @bars: bitmask of BAR's that must be configured ++ * @mask_interrupts: ignore interrupt ++ * @dma_mask: used to set the consistent dma mask ++ * @fault_reset_work_q_name: fw fault work queue ++ * @fault_reset_work_q: "" ++ * @fault_reset_work: "" ++ * @firmware_event_name: fw event work queue ++ * @firmware_event_thread: "" ++ * @fw_event_lock: ++ * @fw_event_list: list of fw events ++ * @aen_event_read_flag: event log was read ++ * @broadcast_aen_busy: broadcast aen waiting to be serviced ++ * @shost_recovery: host reset in progress ++ * @ioc_reset_in_progress_lock: ++ * @ioc_link_reset_in_progress: phy/hard reset in progress ++ * @ignore_loginfos: ignore loginfos during task management ++ * @remove_host: flag for when driver unloads, to avoid sending dev resets ++ * @pci_error_recovery: flag to prevent ioc access until slot reset completes ++ * @wait_for_discovery_to_complete: flag set at driver load time when ++ * waiting on reporting devices ++ * @is_driver_loading: flag set at driver load time ++ * @port_enable_failed: flag set when port enable has failed ++ * @start_scan: flag set from scan_start callback, cleared from _mpt2sas_fw_work ++ * @start_scan_failed: means port enable failed, return's the ioc_status ++ * @msix_enable: flag indicating msix is enabled ++ * @msix_vector_count: number msix vectors ++ * @cpu_msix_table: table for mapping cpus to msix index ++ * @cpu_msix_table_sz: table size ++ * @schedule_dead_ioc_flush_running_cmds: callback to flush pending commands ++ * @scsi_io_cb_idx: shost generated commands ++ * @tm_cb_idx: task management commands ++ * @scsih_cb_idx: scsih internal commands ++ * @transport_cb_idx: transport internal commands ++ * @ctl_cb_idx: clt internal commands ++ * @base_cb_idx: base internal commands ++ * @config_cb_idx: base internal commands ++ * @tm_tr_cb_idx : device removal target reset handshake ++ * @tm_tr_volume_cb_idx : volume removal target reset ++ * @base_cmds: ++ * @transport_cmds: ++ * @scsih_cmds: ++ * @tm_cmds: ++ * @ctl_cmds: ++ * @config_cmds: ++ * @base_add_sg_single: handler for either 32/64 bit sgl's ++ * @event_type: bits indicating which events to log ++ * @event_context: unique id for each logged event ++ * @event_log: event log pointer ++ * @event_masks: events that are masked ++ * @facts: static facts data ++ * @pfacts: static port facts data ++ * @manu_pg0: static manufacturing page 0 ++ * @manu_pg10: static manufacturing page 10 ++ * @manu_pg11: static manufacturing page 11 ++ * @bios_pg2: static bios page 2 ++ * @bios_pg3: static bios page 3 ++ * @ioc_pg8: static ioc page 8 ++ * @iounit_pg0: static iounit page 0 ++ * @iounit_pg1: static iounit page 1 ++ * @iounit_pg8: static iounit page 8 ++ * @sas_hba: sas host object ++ * @sas_expander_list: expander object list ++ * @sas_node_lock: ++ * @sas_device_list: sas device object list ++ * @sas_device_init_list: sas device object list (used only at init time) ++ * @sas_device_lock: ++ * @io_missing_delay: time for IO completed by fw when PDR enabled ++ * @device_missing_delay: time for device missing by fw when PDR enabled ++ * @sas_id : used for setting volume target IDs ++ * @blocking_handles: bitmask used to identify which devices need blocking ++ * @pd_handles : bitmask for PD handles ++ * @pd_handles_sz : size of pd_handle bitmask ++ * @config_page_sz: config page size ++ * @config_page: reserve memory for config page payload ++ * @config_page_dma: ++ * @hba_queue_depth: hba request queue depth ++ * @sge_size: sg element size for either 32/64 bit ++ * @scsiio_depth: SCSI_IO queue depth ++ * @request_sz: per request frame size ++ * @request: pool of request frames ++ * @request_dma: ++ * @request_dma_sz: ++ * @scsi_lookup: firmware request tracker list ++ * @scsi_lookup_lock: ++ * @free_list: free list of request ++ * @pending_io_count: ++ * @reset_wq: ++ * @chain: pool of chains ++ * @chain_dma: ++ * @max_sges_in_main_message: number sg elements in main message ++ * @max_sges_in_chain_message: number sg elements per chain ++ * @chains_needed_per_io: max chains per io ++ * @chain_depth: total chains allocated ++ * @chain_segment_sz: gives the max number of ++ * SGEs accommodate on single chain buffer ++ * @hi_priority_smid: ++ * @hi_priority: ++ * @hi_priority_dma: ++ * @hi_priority_depth: ++ * @hpr_lookup: ++ * @hpr_free_list: ++ * @internal_smid: ++ * @internal: ++ * @internal_dma: ++ * @internal_depth: ++ * @internal_lookup: ++ * @internal_free_list: ++ * @sense: pool of sense ++ * @sense_dma: ++ * @sense_dma_pool: ++ * @reply_depth: hba reply queue depth: ++ * @reply_sz: per reply frame size: ++ * @reply: pool of replys: ++ * @reply_dma: ++ * @reply_dma_pool: ++ * @reply_free_queue_depth: reply free depth ++ * @reply_free: pool for reply free queue (32 bit addr) ++ * @reply_free_dma: ++ * @reply_free_dma_pool: ++ * @reply_free_host_index: tail index in pool to insert free replys ++ * @reply_post_queue_depth: reply post queue depth ++ * @reply_post_struct: struct for reply_post_free physical & virt address ++ * @rdpq_array_capable: FW supports multiple reply queue addresses in ioc_init ++ * @rdpq_array_enable: rdpq_array support is enabled in the driver ++ * @rdpq_array_enable_assigned: this ensures that rdpq_array_enable flag ++ * is assigned only ones ++ * @reply_queue_count: number of reply queue's ++ * @reply_queue_list: link list contaning the reply queue info ++ * @msix96_vector: 96 MSI-X vector support ++ * @replyPostRegisterIndex: index of next position in Reply Desc Post Queue ++ * @delayed_tr_list: target reset link list ++ * @delayed_tr_volume_list: volume target reset link list ++ * @delayed_sc_list: ++ * @delayed_event_ack_list: ++ * @temp_sensors_count: flag to carry the number of temperature sensors ++ * @pci_access_mutex: Mutex to synchronize ioctl,sysfs show path and ++ * pci resource handling. PCI resource freeing will lead to free ++ * vital hardware/memory resource, which might be in use by cli/sysfs ++ * path functions resulting in Null pointer reference followed by kernel ++ * crash. To avoid the above race condition we use mutex syncrhonization ++ * which ensures the syncrhonization between cli/sysfs_show path. ++ */ ++struct MPT3SAS_ADAPTER { ++ struct list_head list; ++ struct Scsi_Host *shost; ++ u8 id; ++ int cpu_count; ++ char name[MPT_NAME_LENGTH]; ++ char driver_name[MPT_NAME_LENGTH]; ++ char tmp_string[MPT_STRING_LENGTH]; ++ struct pci_dev *pdev; ++ Mpi2SystemInterfaceRegs_t __iomem *chip; ++ resource_size_t chip_phys; ++ int logging_level; ++ int fwfault_debug; ++ u8 ir_firmware; ++ int bars; ++ u8 mask_interrupts; ++ int dma_mask; ++ ++ /* fw fault handler */ ++ char fault_reset_work_q_name[20]; ++ struct workqueue_struct *fault_reset_work_q; ++ struct delayed_work fault_reset_work; ++ ++ /* fw event handler */ ++ char firmware_event_name[20]; ++ struct workqueue_struct *firmware_event_thread; ++ spinlock_t fw_event_lock; ++ struct list_head fw_event_list; ++ ++ /* misc flags */ ++ int aen_event_read_flag; ++ u8 broadcast_aen_busy; ++ u16 broadcast_aen_pending; ++ u8 shost_recovery; ++ ++ struct mutex reset_in_progress_mutex; ++ spinlock_t ioc_reset_in_progress_lock; ++ u8 ioc_link_reset_in_progress; ++ u8 ioc_reset_in_progress_status; ++ ++ u8 ignore_loginfos; ++ u8 remove_host; ++ u8 pci_error_recovery; ++ u8 wait_for_discovery_to_complete; ++ u8 is_driver_loading; ++ u8 port_enable_failed; ++ u8 start_scan; ++ u16 start_scan_failed; ++ ++ u8 msix_enable; ++ u16 msix_vector_count; ++ u8 *cpu_msix_table; ++ u16 cpu_msix_table_sz; ++ resource_size_t __iomem **reply_post_host_index; ++ u32 ioc_reset_count; ++ MPT3SAS_FLUSH_RUNNING_CMDS schedule_dead_ioc_flush_running_cmds; ++ u32 non_operational_loop; ++ ++ /* internal commands, callback index */ ++ u8 scsi_io_cb_idx; ++ u8 tm_cb_idx; ++ u8 transport_cb_idx; ++ u8 scsih_cb_idx; ++ u8 ctl_cb_idx; ++ u8 base_cb_idx; ++ u8 port_enable_cb_idx; ++ u8 config_cb_idx; ++ u8 tm_tr_cb_idx; ++ u8 tm_tr_volume_cb_idx; ++ u8 tm_sas_control_cb_idx; ++ struct _internal_cmd base_cmds; ++ struct _internal_cmd port_enable_cmds; ++ struct _internal_cmd transport_cmds; ++ struct _internal_cmd scsih_cmds; ++ struct _internal_cmd tm_cmds; ++ struct _internal_cmd ctl_cmds; ++ struct _internal_cmd config_cmds; ++ ++ MPT_ADD_SGE base_add_sg_single; ++ ++ /* function ptr for either IEEE or MPI sg elements */ ++ MPT_BUILD_SG_SCMD build_sg_scmd; ++ MPT_BUILD_SG build_sg; ++ MPT_BUILD_ZERO_LEN_SGE build_zero_len_sge; ++ u16 sge_size_ieee; ++ u16 hba_mpi_version_belonged; ++ ++ /* function ptr for MPI sg elements only */ ++ MPT_BUILD_SG build_sg_mpi; ++ MPT_BUILD_ZERO_LEN_SGE build_zero_len_sge_mpi; ++ ++ /* event log */ ++ u32 event_type[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; ++ u32 event_context; ++ void *event_log; ++ u32 event_masks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; ++ ++ /* static config pages */ ++ struct mpt2sas_facts facts; ++ struct mpt2sas_port_facts *pfacts; ++ Mpi2ManufacturingPage0_t manu_pg0; ++ struct Mpi2ManufacturingPage10_t manu_pg10; ++ struct Mpi2ManufacturingPage11_t manu_pg11; ++ Mpi2BiosPage2_t bios_pg2; ++ Mpi2BiosPage3_t bios_pg3; ++ Mpi2IOCPage8_t ioc_pg8; ++ Mpi2IOUnitPage0_t iounit_pg0; ++ Mpi2IOUnitPage1_t iounit_pg1; ++ Mpi2IOUnitPage8_t iounit_pg8; ++ ++ struct _boot_device req_boot_device; ++ struct _boot_device req_alt_boot_device; ++ struct _boot_device current_boot_device; ++ ++ /* sas hba, expander, and device list */ ++ struct _sas_node sas_hba; ++ struct list_head sas_expander_list; ++ spinlock_t sas_node_lock; ++ struct list_head sas_device_list; ++ struct list_head sas_device_init_list; ++ spinlock_t sas_device_lock; ++ struct list_head raid_device_list; ++ spinlock_t raid_device_lock; ++ u8 io_missing_delay; ++ u16 device_missing_delay; ++ int sas_id; ++ ++ void *blocking_handles; ++ void *pd_handles; ++ u16 pd_handles_sz; ++ ++ /* config page */ ++ u16 config_page_sz; ++ void *config_page; ++ dma_addr_t config_page_dma; ++ ++ /* scsiio request */ ++ u16 hba_queue_depth; ++ u16 sge_size; ++ u16 scsiio_depth; ++ u16 request_sz; ++ u8 *request; ++ dma_addr_t request_dma; ++ u32 request_dma_sz; ++ struct scsiio_tracker *scsi_lookup; ++ ulong scsi_lookup_pages; ++ spinlock_t scsi_lookup_lock; ++ struct list_head free_list; ++ int pending_io_count; ++ wait_queue_head_t reset_wq; ++ ++ /* chain */ ++ struct chain_tracker *chain_lookup; ++ struct list_head free_chain_list; ++ struct dma_pool *chain_dma_pool; ++ ulong chain_pages; ++ u16 max_sges_in_main_message; ++ u16 max_sges_in_chain_message; ++ u16 chains_needed_per_io; ++ u32 chain_depth; ++ u16 chain_segment_sz; ++ ++ /* hi-priority queue */ ++ u16 hi_priority_smid; ++ u8 *hi_priority; ++ dma_addr_t hi_priority_dma; ++ u16 hi_priority_depth; ++ struct request_tracker *hpr_lookup; ++ struct list_head hpr_free_list; ++ ++ /* internal queue */ ++ u16 internal_smid; ++ u8 *internal; ++ dma_addr_t internal_dma; ++ u16 internal_depth; ++ struct request_tracker *internal_lookup; ++ struct list_head internal_free_list; ++ ++ /* sense */ ++ u8 *sense; ++ dma_addr_t sense_dma; ++ struct dma_pool *sense_dma_pool; ++ ++ /* reply */ ++ u16 reply_sz; ++ u8 *reply; ++ dma_addr_t reply_dma; ++ u32 reply_dma_max_address; ++ u32 reply_dma_min_address; ++ struct dma_pool *reply_dma_pool; ++ ++ /* reply free queue */ ++ u16 reply_free_queue_depth; ++ __le32 *reply_free; ++ dma_addr_t reply_free_dma; ++ struct dma_pool *reply_free_dma_pool; ++ u32 reply_free_host_index; ++ ++ /* reply post queue */ ++ u16 reply_post_queue_depth; ++ struct reply_post_struct *reply_post; ++ u8 rdpq_array_capable; ++ u8 rdpq_array_enable; ++ u8 rdpq_array_enable_assigned; ++ struct dma_pool *reply_post_free_dma_pool; ++ u8 reply_queue_count; ++ struct list_head reply_queue_list; ++ ++ u8 msix96_vector; ++ /* reply post register index */ ++ resource_size_t **replyPostRegisterIndex; ++ ++ struct list_head delayed_tr_list; ++ struct list_head delayed_tr_volume_list; ++ struct list_head delayed_sc_list; ++ struct list_head delayed_event_ack_list; ++ u8 temp_sensors_count; ++ struct mutex pci_access_mutex; ++ ++ /* diag buffer support */ ++ u8 *diag_buffer[MPI2_DIAG_BUF_TYPE_COUNT]; ++ u32 diag_buffer_sz[MPI2_DIAG_BUF_TYPE_COUNT]; ++ dma_addr_t diag_buffer_dma[MPI2_DIAG_BUF_TYPE_COUNT]; ++ u8 diag_buffer_status[MPI2_DIAG_BUF_TYPE_COUNT]; ++ u32 unique_id[MPI2_DIAG_BUF_TYPE_COUNT]; ++ u32 product_specific[MPI2_DIAG_BUF_TYPE_COUNT][23]; ++ u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT]; ++ u32 ring_buffer_offset; ++ u32 ring_buffer_sz; ++ u8 is_warpdrive; ++ u8 hide_ir_msg; ++ u8 mfg_pg10_hide_flag; ++ u8 hide_drives; ++ spinlock_t diag_trigger_lock; ++ u8 diag_trigger_active; ++ struct SL_WH_MASTER_TRIGGER_T diag_trigger_master; ++ struct SL_WH_EVENT_TRIGGERS_T diag_trigger_event; ++ struct SL_WH_SCSI_TRIGGERS_T diag_trigger_scsi; ++ struct SL_WH_MPI_TRIGGERS_T diag_trigger_mpi; ++}; ++ ++typedef u8 (*MPT_CALLBACK)(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply); ++ ++ ++/* base shared API */ ++extern struct list_head mpt2sas_ioc_list; ++extern char driver_name[MPT_NAME_LENGTH]; ++/* spinlock on list operations over IOCs ++ * Case: when multiple warpdrive cards(IOCs) are in use ++ * Each IOC will added to the ioc list structure on initialization. ++ * Watchdog threads run at regular intervals to check IOC for any ++ * fault conditions which will trigger the dead_ioc thread to ++ * deallocate pci resource, resulting deleting the IOC netry from list, ++ * this deletion need to protected by spinlock to enusre that ++ * ioc removal is syncrhonized, if not synchronized it might lead to ++ * list_del corruption as the ioc list is traversed in cli path. ++ */ ++extern spinlock_t gioc_lock_mpt2sas; ++ ++void mpt2sas_base_start_watchdog(struct MPT3SAS_ADAPTER *ioc); ++void mpt2sas_base_stop_watchdog(struct MPT3SAS_ADAPTER *ioc); ++ ++int mpt2sas_base_attach(struct MPT3SAS_ADAPTER *ioc); ++void mpt2sas_base_detach(struct MPT3SAS_ADAPTER *ioc); ++int mpt2sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc); ++void mpt2sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc); ++int mpt2sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, ++ enum reset_type type); ++ ++void *mpt2sas_base_get_msg_frame(struct MPT3SAS_ADAPTER *ioc, u16 smid); ++void *mpt2sas_base_get_sense_buffer(struct MPT3SAS_ADAPTER *ioc, u16 smid); ++__le32 mpt2sas_base_get_sense_buffer_dma(struct MPT3SAS_ADAPTER *ioc, ++ u16 smid); ++ ++void mpt2sas_base_sync_reply_irqs(struct MPT3SAS_ADAPTER *ioc); ++ ++/* hi-priority queue */ ++u16 mpt2sas_base_get_smid_hpr(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx); ++u16 mpt2sas_base_get_smid_scsiio(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx, ++ struct scsi_cmnd *scmd); ++ ++u16 mpt2sas_base_get_smid(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx); ++void mpt2sas_base_free_smid(struct MPT3SAS_ADAPTER *ioc, u16 smid); ++void mpt2sas_base_put_smid_scsi_io(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ u16 handle); ++void mpt2sas_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ u16 handle); ++void mpt2sas_base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, ++ u16 smid, u16 msix_task); ++void mpt2sas_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid); ++void mpt2sas_base_initialize_callback_handler(void); ++u8 mpt2sas_base_register_callback_handler(MPT_CALLBACK cb_func); ++void mpt2sas_base_release_callback_handler(u8 cb_idx); ++ ++u8 mpt2sas_base_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply); ++u8 mpt2sas_port_enable_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ u8 msix_index, u32 reply); ++void *mpt2sas_base_get_reply_virt_addr(struct MPT3SAS_ADAPTER *ioc, ++ u32 phys_addr); ++ ++u32 mpt2sas_base_get_iocstate(struct MPT3SAS_ADAPTER *ioc, int cooked); ++ ++void mpt2sas_base_fault_info(struct MPT3SAS_ADAPTER *ioc , u16 fault_code); ++int mpt2sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2SasIoUnitControlReply_t *mpi_reply, ++ Mpi2SasIoUnitControlRequest_t *mpi_request); ++int mpt2sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request); ++ ++void mpt2sas_base_validate_event_type(struct MPT3SAS_ADAPTER *ioc, ++ u32 *event_type); ++ ++void mpt2sas_halt_firmware(struct MPT3SAS_ADAPTER *ioc); ++ ++void mpt2sas_base_update_missing_delay(struct MPT3SAS_ADAPTER *ioc, ++ u16 device_missing_delay, u8 io_missing_delay); ++ ++int mpt2sas_port_enable(struct MPT3SAS_ADAPTER *ioc); ++ ++ ++/* scsih shared API */ ++u8 mpt2sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, ++ u32 reply); ++void mpt2sas_scsih_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase); ++ ++int mpt2sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, ++ uint channel, uint id, uint lun, u8 type, u16 smid_task, ++ ulong timeout, enum mutex_type m_type); ++void mpt2sas_scsih_set_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle); ++void mpt2sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle); ++void mpt2sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address); ++void mpt2sas_device_remove_by_sas_address(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address); ++u8 mpt2sas_check_for_pending_internal_cmds(struct MPT3SAS_ADAPTER *ioc, ++ u16 smid); ++ ++struct _sas_node *mpt2sas_scsih_expander_find_by_handle( ++ struct MPT3SAS_ADAPTER *ioc, u16 handle); ++struct _sas_node *mpt2sas_scsih_expander_find_by_sas_address( ++ struct MPT3SAS_ADAPTER *ioc, u64 sas_address); ++struct _sas_device *mpt2sas_get_sdev_by_addr( ++ struct MPT3SAS_ADAPTER *ioc, u64 sas_address); ++struct _sas_device *__mpt2sas_get_sdev_by_addr( ++ struct MPT3SAS_ADAPTER *ioc, u64 sas_address); ++ ++void mpt2sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc); ++struct _raid_device * ++mpt2sas_raid_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle); ++ ++/* config shared API */ ++u8 mpt2sas_config_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply); ++int mpt2sas_config_get_number_hba_phys(struct MPT3SAS_ADAPTER *ioc, ++ u8 *num_phys); ++int mpt2sas_config_get_manufacturing_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page); ++int mpt2sas_config_get_manufacturing_pg7(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage7_t *config_page, ++ u16 sz); ++int mpt2sas_config_get_manufacturing_pg10(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, ++ struct Mpi2ManufacturingPage10_t *config_page); ++ ++int mpt2sas_config_get_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, ++ struct Mpi2ManufacturingPage11_t *config_page); ++int mpt2sas_config_set_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, ++ struct Mpi2ManufacturingPage11_t *config_page); ++ ++int mpt2sas_config_get_bios_pg2(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2BiosPage2_t *config_page); ++int mpt2sas_config_get_bios_pg3(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2BiosPage3_t *config_page); ++int mpt2sas_config_get_iounit_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2IOUnitPage0_t *config_page); ++int mpt2sas_config_get_sas_device_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage0_t *config_page, ++ u32 form, u32 handle); ++int mpt2sas_config_get_sas_device_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage1_t *config_page, ++ u32 form, u32 handle); ++int mpt2sas_config_get_sas_iounit_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage0_t *config_page, ++ u16 sz); ++int mpt2sas_config_get_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2IOUnitPage1_t *config_page); ++int mpt2sas_config_get_iounit_pg3(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage3_t *config_page, u16 sz); ++int mpt2sas_config_set_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2IOUnitPage1_t *config_page); ++int mpt2sas_config_get_iounit_pg8(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2IOUnitPage8_t *config_page); ++int mpt2sas_config_get_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, ++ u16 sz); ++int mpt2sas_config_set_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, ++ u16 sz); ++int mpt2sas_config_get_ioc_pg8(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2IOCPage8_t *config_page); ++int mpt2sas_config_get_expander_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2ExpanderPage0_t *config_page, ++ u32 form, u32 handle); ++int mpt2sas_config_get_expander_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2ExpanderPage1_t *config_page, ++ u32 phy_number, u16 handle); ++int mpt2sas_config_get_enclosure_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasEnclosurePage0_t *config_page, ++ u32 form, u32 handle); ++int mpt2sas_config_get_phy_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number); ++int mpt2sas_config_get_phy_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number); ++int mpt2sas_config_get_raid_volume_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form, ++ u32 handle); ++int mpt2sas_config_get_number_pds(struct MPT3SAS_ADAPTER *ioc, u16 handle, ++ u8 *num_pds); ++int mpt2sas_config_get_raid_volume_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form, ++ u32 handle, u16 sz); ++int mpt2sas_config_get_phys_disk_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page, ++ u32 form, u32 form_specific); ++int mpt2sas_config_get_volume_handle(struct MPT3SAS_ADAPTER *ioc, u16 pd_handle, ++ u16 *volume_handle); ++int mpt2sas_config_get_volume_wwid(struct MPT3SAS_ADAPTER *ioc, ++ u16 volume_handle, u64 *wwid); ++ ++/* ctl shared API */ ++extern struct device_attribute *mpt2sas_host_attrs[]; ++extern struct device_attribute *mpt2sas_dev_attrs[]; ++void mpt2sas_ctl_init(ushort hbas_to_enumerate); ++void mpt2sas_ctl_exit(ushort hbas_to_enumerate); ++u8 mpt2sas_ctl_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply); ++void mpt2sas_ctl_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase); ++u8 mpt2sas_ctl_event_callback(struct MPT3SAS_ADAPTER *ioc, ++ u8 msix_index, u32 reply); ++void mpt2sas_ctl_add_to_event_log(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventNotificationReply_t *mpi_reply); ++ ++void mpt2sas_enable_diag_buffer(struct MPT3SAS_ADAPTER *ioc, ++ u8 bits_to_regsiter); ++int mpt2sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type, ++ u8 *issue_reset); ++ ++/* transport shared API */ ++extern struct scsi_transport_template *mpt2sas_transport_template; ++u8 mpt2sas_transport_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply); ++struct _sas_port *mpt2sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, ++ u16 handle, u64 sas_address); ++void mpt2sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, ++ u64 sas_address_parent); ++int mpt2sas_transport_add_host_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy ++ *mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev); ++int mpt2sas_transport_add_expander_phy(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_phy *mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, ++ struct device *parent_dev); ++void mpt2sas_transport_update_links(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address, u16 handle, u8 phy_number, u8 link_rate); ++extern struct sas_function_template mpt2sas_transport_functions; ++extern struct scsi_transport_template *mpt2sas_transport_template; ++extern int scsi_internal_device_block(struct scsi_device *sdev); ++extern int scsi_internal_device_unblock(struct scsi_device *sdev, ++ enum scsi_device_state new_state); ++/* trigger data externs */ ++void mpt2sas_send_trigger_data_event(struct MPT3SAS_ADAPTER *ioc, ++ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data); ++void mpt2sas_process_trigger_data(struct MPT3SAS_ADAPTER *ioc, ++ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data); ++void mpt2sas_trigger_master(struct MPT3SAS_ADAPTER *ioc, ++ u32 tigger_bitmask); ++void mpt2sas_trigger_event(struct MPT3SAS_ADAPTER *ioc, u16 event, ++ u16 log_entry_qualifier); ++void mpt2sas_trigger_scsi(struct MPT3SAS_ADAPTER *ioc, u8 sense_key, ++ u8 asc, u8 ascq); ++void mpt2sas_trigger_mpi(struct MPT3SAS_ADAPTER *ioc, u16 ioc_status, ++ u32 loginfo); ++ ++/* warpdrive APIs */ ++u8 mpt2sas_get_num_volumes(struct MPT3SAS_ADAPTER *ioc); ++void mpt2sas_init_warpdrive_properties(struct MPT3SAS_ADAPTER *ioc, ++ struct _raid_device *raid_device); ++u8 ++mpt2sas_scsi_direct_io_get(struct MPT3SAS_ADAPTER *ioc, u16 smid); ++void ++mpt2sas_scsi_direct_io_set(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 direct_io); ++void ++mpt2sas_setup_direct_io(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, ++ struct _raid_device *raid_device, Mpi2SCSIIORequest_t *mpi_request, ++ u16 smid); ++ ++#endif /* MPT3SAS_BASE_H_INCLUDED */ +diff --git a/drivers/scsi/mpt2sas/mpt3sas_config.c b/drivers/scsi/mpt2sas/mpt3sas_config.c +new file mode 100644 +index 0000000..0f67b2c +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_config.c +@@ -0,0 +1,1716 @@ ++/* ++ * This module provides common API for accessing firmware configuration pages ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.c ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mpt3sas_base.h" ++ ++/* local definitions */ ++ ++/* Timeout for config page request (in seconds) */ ++#define MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT 15 ++ ++/* Common sgl flags for READING a config page. */ ++#define MPT3_CONFIG_COMMON_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \ ++ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \ ++ | MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT) ++ ++/* Common sgl flags for WRITING a config page. */ ++#define MPT3_CONFIG_COMMON_WRITE_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \ ++ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \ ++ | MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC) \ ++ << MPI2_SGE_FLAGS_SHIFT) ++ ++/** ++ * struct config_request - obtain dma memory via routine ++ * @sz: size ++ * @page: virt pointer ++ * @page_dma: phys pointer ++ * ++ */ ++struct config_request { ++ u16 sz; ++ void *page; ++ dma_addr_t page_dma; ++}; ++ ++/** ++ * _config_display_some_debug - debug routine ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @calling_function_name: string pass from calling function ++ * @mpi_reply: reply message frame ++ * Context: none. ++ * ++ * Function for displaying debug info helpful when debugging issues ++ * in this module. ++ */ ++static void ++_config_display_some_debug(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ char *calling_function_name, MPI2DefaultReply_t *mpi_reply) ++{ ++ Mpi2ConfigRequest_t *mpi_request; ++ char *desc = NULL; ++ ++ if (!(ioc->logging_level & MPT_DEBUG_CONFIG)) ++ return; ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ switch (mpi_request->Header.PageType & MPI2_CONFIG_PAGETYPE_MASK) { ++ case MPI2_CONFIG_PAGETYPE_IO_UNIT: ++ desc = "io_unit"; ++ break; ++ case MPI2_CONFIG_PAGETYPE_IOC: ++ desc = "ioc"; ++ break; ++ case MPI2_CONFIG_PAGETYPE_BIOS: ++ desc = "bios"; ++ break; ++ case MPI2_CONFIG_PAGETYPE_RAID_VOLUME: ++ desc = "raid_volume"; ++ break; ++ case MPI2_CONFIG_PAGETYPE_MANUFACTURING: ++ desc = "manufaucturing"; ++ break; ++ case MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK: ++ desc = "physdisk"; ++ break; ++ case MPI2_CONFIG_PAGETYPE_EXTENDED: ++ switch (mpi_request->ExtPageType) { ++ case MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT: ++ desc = "sas_io_unit"; ++ break; ++ case MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER: ++ desc = "sas_expander"; ++ break; ++ case MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE: ++ desc = "sas_device"; ++ break; ++ case MPI2_CONFIG_EXTPAGETYPE_SAS_PHY: ++ desc = "sas_phy"; ++ break; ++ case MPI2_CONFIG_EXTPAGETYPE_LOG: ++ desc = "log"; ++ break; ++ case MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE: ++ desc = "enclosure"; ++ break; ++ case MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG: ++ desc = "raid_config"; ++ break; ++ case MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING: ++ desc = "driver_mapping"; ++ break; ++ } ++ break; ++ } ++ ++ if (!desc) ++ return; ++ ++ pr_info(MPT3SAS_FMT ++ "%s: %s(%d), action(%d), form(0x%08x), smid(%d)\n", ++ ioc->name, calling_function_name, desc, ++ mpi_request->Header.PageNumber, mpi_request->Action, ++ le32_to_cpu(mpi_request->PageAddress), smid); ++ ++ if (!mpi_reply) ++ return; ++ ++ if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo) ++ pr_info(MPT3SAS_FMT ++ "\tiocstatus(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply->IOCStatus), ++ le32_to_cpu(mpi_reply->IOCLogInfo)); ++} ++ ++/** ++ * _config_alloc_config_dma_memory - obtain physical memory ++ * @ioc: per adapter object ++ * @mem: struct config_request ++ * ++ * A wrapper for obtaining dma-able memory for config page request. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_config_alloc_config_dma_memory(struct MPT3SAS_ADAPTER *ioc, ++ struct config_request *mem) ++{ ++ int r = 0; ++ ++ if (mem->sz > ioc->config_page_sz) { ++ mem->page = dma_alloc_coherent(&ioc->pdev->dev, mem->sz, ++ &mem->page_dma, GFP_KERNEL); ++ if (!mem->page) { ++ pr_err(MPT3SAS_FMT ++ "%s: dma_alloc_coherent failed asking for (%d) bytes!!\n", ++ ioc->name, __func__, mem->sz); ++ r = -ENOMEM; ++ } ++ } else { /* use tmp buffer if less than 512 bytes */ ++ mem->page = ioc->config_page; ++ mem->page_dma = ioc->config_page_dma; ++ } ++ return r; ++} ++ ++/** ++ * _config_free_config_dma_memory - wrapper to free the memory ++ * @ioc: per adapter object ++ * @mem: struct config_request ++ * ++ * A wrapper to free dma-able memory when using _config_alloc_config_dma_memory. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static void ++_config_free_config_dma_memory(struct MPT3SAS_ADAPTER *ioc, ++ struct config_request *mem) ++{ ++ if (mem->sz > ioc->config_page_sz) ++ dma_free_coherent(&ioc->pdev->dev, mem->sz, mem->page, ++ mem->page_dma); ++} ++ ++/** ++ * mpt2sas_config_done - config page completion routine ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: none. ++ * ++ * The callback handler when using _config_request. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_config_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ ++ if (ioc->config_cmds.status == MPT3_CMD_NOT_USED) ++ return 1; ++ if (ioc->config_cmds.smid != smid) ++ return 1; ++ ioc->config_cmds.status |= MPT3_CMD_COMPLETE; ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (mpi_reply) { ++ ioc->config_cmds.status |= MPT3_CMD_REPLY_VALID; ++ memcpy(ioc->config_cmds.reply, mpi_reply, ++ mpi_reply->MsgLength*4); ++ } ++ ioc->config_cmds.status &= ~MPT3_CMD_PENDING; ++ _config_display_some_debug(ioc, smid, "config_done", mpi_reply); ++ ioc->config_cmds.smid = USHRT_MAX; ++ complete(&ioc->config_cmds.done); ++ return 1; ++} ++ ++/** ++ * _config_request - main routine for sending config page requests ++ * @ioc: per adapter object ++ * @mpi_request: request message frame ++ * @mpi_reply: reply mf payload returned from firmware ++ * @timeout: timeout in seconds ++ * @config_page: contents of the config page ++ * @config_page_sz: size of config page ++ * Context: sleep ++ * ++ * A generic API for config page requests to firmware. ++ * ++ * The ioc->config_cmds.status flag should be MPT3_CMD_NOT_USED before calling ++ * this API. ++ * ++ * The callback index is set inside `ioc->config_cb_idx. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t ++ *mpi_request, Mpi2ConfigReply_t *mpi_reply, int timeout, ++ void *config_page, u16 config_page_sz) ++{ ++ u16 smid; ++ u32 ioc_state; ++ unsigned long timeleft; ++ Mpi2ConfigRequest_t *config_request; ++ int r; ++ u8 retry_count, issue_host_reset = 0; ++ u16 wait_state_count; ++ struct config_request mem; ++ u32 ioc_status = UINT_MAX; ++ ++ mutex_lock(&ioc->config_cmds.mutex); ++ if (ioc->config_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: config_cmd in use\n", ++ ioc->name, __func__); ++ mutex_unlock(&ioc->config_cmds.mutex); ++ return -EAGAIN; ++ } ++ ++ retry_count = 0; ++ memset(&mem, 0, sizeof(struct config_request)); ++ ++ mpi_request->VF_ID = 0; /* TODO */ ++ mpi_request->VP_ID = 0; ++ ++ if (config_page) { ++ mpi_request->Header.PageVersion = mpi_reply->Header.PageVersion; ++ mpi_request->Header.PageNumber = mpi_reply->Header.PageNumber; ++ mpi_request->Header.PageType = mpi_reply->Header.PageType; ++ mpi_request->Header.PageLength = mpi_reply->Header.PageLength; ++ mpi_request->ExtPageLength = mpi_reply->ExtPageLength; ++ mpi_request->ExtPageType = mpi_reply->ExtPageType; ++ if (mpi_request->Header.PageLength) ++ mem.sz = mpi_request->Header.PageLength * 4; ++ else ++ mem.sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4; ++ r = _config_alloc_config_dma_memory(ioc, &mem); ++ if (r != 0) ++ goto out; ++ if (mpi_request->Action == ++ MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT || ++ mpi_request->Action == ++ MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM) { ++ ioc->base_add_sg_single(&mpi_request->PageBufferSGE, ++ MPT3_CONFIG_COMMON_WRITE_SGLFLAGS | mem.sz, ++ mem.page_dma); ++ memcpy(mem.page, config_page, min_t(u16, mem.sz, ++ config_page_sz)); ++ } else { ++ memset(config_page, 0, config_page_sz); ++ ioc->base_add_sg_single(&mpi_request->PageBufferSGE, ++ MPT3_CONFIG_COMMON_SGLFLAGS | mem.sz, mem.page_dma); ++ memset(mem.page, 0, min_t(u16, mem.sz, config_page_sz)); ++ } ++ } ++ ++ retry_config: ++ if (retry_count) { ++ if (retry_count > 2) { /* attempt only 2 retries */ ++ r = -EFAULT; ++ goto free_mem; ++ } ++ pr_info(MPT3SAS_FMT "%s: attempting retry (%d)\n", ++ ioc->name, __func__, retry_count); ++ } ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ ioc->config_cmds.status = MPT3_CMD_NOT_USED; ++ r = -EFAULT; ++ goto free_mem; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, __func__, wait_state_count); ++ } ++ if (wait_state_count) ++ pr_info(MPT3SAS_FMT "%s: ioc is operational\n", ++ ioc->name, __func__); ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->config_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ ioc->config_cmds.status = MPT3_CMD_NOT_USED; ++ r = -EAGAIN; ++ goto free_mem; ++ } ++ ++ r = 0; ++ memset(mpi_reply, 0, sizeof(Mpi2ConfigReply_t)); ++ ioc->config_cmds.status = MPT3_CMD_PENDING; ++ config_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->config_cmds.smid = smid; ++ memcpy(config_request, mpi_request, sizeof(Mpi2ConfigRequest_t)); ++ _config_display_some_debug(ioc, smid, "config_request", NULL); ++ init_completion(&ioc->config_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->config_cmds.done, ++ timeout*HZ); ++ if (!(ioc->config_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2ConfigRequest_t)/4); ++ retry_count++; ++ if (ioc->config_cmds.smid == smid) ++ mpt2sas_base_free_smid(ioc, smid); ++ if ((ioc->shost_recovery) || (ioc->config_cmds.status & ++ MPT3_CMD_RESET) || ioc->pci_error_recovery) ++ goto retry_config; ++ issue_host_reset = 1; ++ r = -EFAULT; ++ goto free_mem; ++ } ++ ++ if (ioc->config_cmds.status & MPT3_CMD_REPLY_VALID) { ++ memcpy(mpi_reply, ioc->config_cmds.reply, ++ sizeof(Mpi2ConfigReply_t)); ++ ++ /* Reply Frame Sanity Checks to workaround FW issues */ ++ if ((mpi_request->Header.PageType & 0xF) != ++ (mpi_reply->Header.PageType & 0xF)) { ++ _debug_dump_mf(mpi_request, ioc->request_sz/4); ++ _debug_dump_reply(mpi_reply, ioc->request_sz/4); ++ panic(KERN_WARNING MPT3SAS_FMT "%s: Firmware BUG:" \ ++ " mpi_reply mismatch: Requested PageType(0x%02x)" \ ++ " Reply PageType(0x%02x)\n", \ ++ ioc->name, __func__, ++ (mpi_request->Header.PageType & 0xF), ++ (mpi_reply->Header.PageType & 0xF)); ++ } ++ ++ if (((mpi_request->Header.PageType & 0xF) == ++ MPI2_CONFIG_PAGETYPE_EXTENDED) && ++ mpi_request->ExtPageType != mpi_reply->ExtPageType) { ++ _debug_dump_mf(mpi_request, ioc->request_sz/4); ++ _debug_dump_reply(mpi_reply, ioc->request_sz/4); ++ panic(KERN_WARNING MPT3SAS_FMT "%s: Firmware BUG:" \ ++ " mpi_reply mismatch: Requested ExtPageType(0x%02x)" ++ " Reply ExtPageType(0x%02x)\n", ++ ioc->name, __func__, mpi_request->ExtPageType, ++ mpi_reply->ExtPageType); ++ } ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) ++ & MPI2_IOCSTATUS_MASK; ++ } ++ ++ if (retry_count) ++ pr_info(MPT3SAS_FMT "%s: retry (%d) completed!!\n", \ ++ ioc->name, __func__, retry_count); ++ ++ if ((ioc_status == MPI2_IOCSTATUS_SUCCESS) && ++ config_page && mpi_request->Action == ++ MPI2_CONFIG_ACTION_PAGE_READ_CURRENT) { ++ u8 *p = (u8 *)mem.page; ++ ++ /* Config Page Sanity Checks to workaround FW issues */ ++ if (p) { ++ if ((mpi_request->Header.PageType & 0xF) != ++ (p[3] & 0xF)) { ++ _debug_dump_mf(mpi_request, ioc->request_sz/4); ++ _debug_dump_reply(mpi_reply, ioc->request_sz/4); ++ _debug_dump_config(p, min_t(u16, mem.sz, ++ config_page_sz)/4); ++ panic(KERN_WARNING MPT3SAS_FMT ++ "%s: Firmware BUG:" \ ++ " config page mismatch:" ++ " Requested PageType(0x%02x)" ++ " Reply PageType(0x%02x)\n", ++ ioc->name, __func__, ++ (mpi_request->Header.PageType & 0xF), ++ (p[3] & 0xF)); ++ } ++ ++ if (((mpi_request->Header.PageType & 0xF) == ++ MPI2_CONFIG_PAGETYPE_EXTENDED) && ++ (mpi_request->ExtPageType != p[6])) { ++ _debug_dump_mf(mpi_request, ioc->request_sz/4); ++ _debug_dump_reply(mpi_reply, ioc->request_sz/4); ++ _debug_dump_config(p, min_t(u16, mem.sz, ++ config_page_sz)/4); ++ panic(KERN_WARNING MPT3SAS_FMT ++ "%s: Firmware BUG:" \ ++ " config page mismatch:" ++ " Requested ExtPageType(0x%02x)" ++ " Reply ExtPageType(0x%02x)\n", ++ ioc->name, __func__, ++ mpi_request->ExtPageType, p[6]); ++ } ++ } ++ memcpy(config_page, mem.page, min_t(u16, mem.sz, ++ config_page_sz)); ++ } ++ ++ free_mem: ++ if (config_page) ++ _config_free_config_dma_memory(ioc, &mem); ++ out: ++ ioc->config_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_unlock(&ioc->config_cmds.mutex); ++ ++ if (issue_host_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_manufacturing_pg0 - obtain manufacturing page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_manufacturing_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_manufacturing_pg7 - obtain manufacturing page 7 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @sz: size of buffer passed in config_page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_manufacturing_pg7(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage7_t *config_page, ++ u16 sz) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING; ++ mpi_request.Header.PageNumber = 7; ++ mpi_request.Header.PageVersion = MPI2_MANUFACTURING7_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sz); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_manufacturing_pg10 - obtain manufacturing page 10 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_manufacturing_pg10(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, ++ struct Mpi2ManufacturingPage10_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING; ++ mpi_request.Header.PageNumber = 10; ++ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_manufacturing_pg11 - obtain manufacturing page 11 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, ++ struct Mpi2ManufacturingPage11_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING; ++ mpi_request.Header.PageNumber = 11; ++ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_set_manufacturing_pg11 - set manufacturing page 11 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_set_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, ++ struct Mpi2ManufacturingPage11_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING; ++ mpi_request.Header.PageNumber = 11; ++ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_bios_pg2 - obtain bios page 2 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_bios_pg2(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage2_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS; ++ mpi_request.Header.PageNumber = 2; ++ mpi_request.Header.PageVersion = MPI2_BIOSPAGE2_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_bios_pg3 - obtain bios page 3 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_bios_pg3(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2BiosPage3_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS; ++ mpi_request.Header.PageNumber = 3; ++ mpi_request.Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_iounit_pg0 - obtain iounit page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_iounit_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage0_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_iounit_pg1 - obtain iounit page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; ++ mpi_request.Header.PageNumber = 1; ++ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_set_iounit_pg1 - set iounit page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_set_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; ++ mpi_request.Header.PageNumber = 1; ++ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_iounit_pg3 - obtain iounit page 3 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @sz: size of buffer passed in config_page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_iounit_pg3(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage3_t *config_page, u16 sz) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; ++ mpi_request.Header.PageNumber = 3; ++ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE3_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_iounit_pg8 - obtain iounit page 8 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_iounit_pg8(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage8_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; ++ mpi_request.Header.PageNumber = 8; ++ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE8_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_ioc_pg8 - obtain ioc page 8 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_ioc_pg8(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2IOCPage8_t *config_page) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IOC; ++ mpi_request.Header.PageNumber = 8; ++ mpi_request.Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_sas_device_pg0 - obtain sas device page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @form: GET_NEXT_HANDLE or HANDLE ++ * @handle: device handle ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_sas_device_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage0_t *config_page, ++ u32 form, u32 handle) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE; ++ mpi_request.Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION; ++ mpi_request.Header.PageNumber = 0; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = cpu_to_le32(form | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_sas_device_pg1 - obtain sas device page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @form: GET_NEXT_HANDLE or HANDLE ++ * @handle: device handle ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_sas_device_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage1_t *config_page, ++ u32 form, u32 handle) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE; ++ mpi_request.Header.PageVersion = MPI2_SASDEVICE1_PAGEVERSION; ++ mpi_request.Header.PageNumber = 1; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = cpu_to_le32(form | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_number_hba_phys - obtain number of phys on the host ++ * @ioc: per adapter object ++ * @num_phys: pointer returned with the number of phys ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_number_hba_phys(struct MPT3SAS_ADAPTER *ioc, u8 *num_phys) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ u16 ioc_status; ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasIOUnitPage0_t config_page; ++ ++ *num_phys = 0; ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, &mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, &mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, &config_page, ++ sizeof(Mpi2SasIOUnitPage0_t)); ++ if (!r) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) ++ *num_phys = config_page.NumPhys; ++ } ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_sas_iounit_pg0 - obtain sas iounit page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @sz: size of buffer passed in config_page ++ * Context: sleep. ++ * ++ * Calling function should call config_get_number_hba_phys prior to ++ * this function, so enough memory is allocated for config_page. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_sas_iounit_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage0_t *config_page, ++ u16 sz) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_sas_iounit_pg1 - obtain sas iounit page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @sz: size of buffer passed in config_page ++ * Context: sleep. ++ * ++ * Calling function should call config_get_number_hba_phys prior to ++ * this function, so enough memory is allocated for config_page. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, ++ u16 sz) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; ++ mpi_request.Header.PageNumber = 1; ++ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_set_sas_iounit_pg1 - send sas iounit page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @sz: size of buffer passed in config_page ++ * Context: sleep. ++ * ++ * Calling function should call config_get_number_hba_phys prior to ++ * this function, so enough memory is allocated for config_page. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_set_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, ++ u16 sz) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; ++ mpi_request.Header.PageNumber = 1; ++ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; ++ _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_expander_pg0 - obtain expander page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @form: GET_NEXT_HANDLE or HANDLE ++ * @handle: expander handle ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_expander_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2ExpanderPage0_t *config_page, u32 form, u32 handle) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_SASEXPANDER0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = cpu_to_le32(form | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_expander_pg1 - obtain expander page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @phy_number: phy number ++ * @handle: expander handle ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_expander_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2ExpanderPage1_t *config_page, u32 phy_number, ++ u16 handle) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER; ++ mpi_request.Header.PageNumber = 1; ++ mpi_request.Header.PageVersion = MPI2_SASEXPANDER1_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = ++ cpu_to_le32(MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM | ++ (phy_number << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_enclosure_pg0 - obtain enclosure page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @form: GET_NEXT_HANDLE or HANDLE ++ * @handle: expander handle ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_enclosure_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2SasEnclosurePage0_t *config_page, u32 form, u32 handle) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_SASENCLOSURE0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = cpu_to_le32(form | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_phy_pg0 - obtain phy page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @phy_number: phy number ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_phy_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_SASPHY0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = ++ cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_phy_pg1 - obtain phy page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @phy_number: phy number ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_phy_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY; ++ mpi_request.Header.PageNumber = 1; ++ mpi_request.Header.PageVersion = MPI2_SASPHY1_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = ++ cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_raid_volume_pg1 - obtain raid volume page 1 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @form: GET_NEXT_HANDLE or HANDLE ++ * @handle: volume handle ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_raid_volume_pg1(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form, ++ u32 handle) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME; ++ mpi_request.Header.PageNumber = 1; ++ mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE1_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = cpu_to_le32(form | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_number_pds - obtain number of phys disk assigned to volume ++ * @ioc: per adapter object ++ * @handle: volume handle ++ * @num_pds: returns pds count ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_number_pds(struct MPT3SAS_ADAPTER *ioc, u16 handle, ++ u8 *num_pds) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ Mpi2RaidVolPage0_t config_page; ++ Mpi2ConfigReply_t mpi_reply; ++ int r; ++ u16 ioc_status; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ *num_pds = 0; ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, &mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = ++ cpu_to_le32(MPI2_RAID_VOLUME_PGAD_FORM_HANDLE | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, &mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, &config_page, ++ sizeof(Mpi2RaidVolPage0_t)); ++ if (!r) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) ++ *num_pds = config_page.NumPhysDisks; ++ } ++ ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_raid_volume_pg0 - obtain raid volume page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @form: GET_NEXT_HANDLE or HANDLE ++ * @handle: volume handle ++ * @sz: size of buffer passed in config_page ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_raid_volume_pg0(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form, ++ u32 handle, u16 sz) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = cpu_to_le32(form | handle); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_phys_disk_pg0 - obtain phys disk page 0 ++ * @ioc: per adapter object ++ * @mpi_reply: reply mf payload returned from firmware ++ * @config_page: contents of the config page ++ * @form: GET_NEXT_PHYSDISKNUM, PHYSDISKNUM, DEVHANDLE ++ * @form_specific: specific to the form ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_phys_disk_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t ++ *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page, u32 form, ++ u32 form_specific) ++{ ++ Mpi2ConfigRequest_t mpi_request; ++ int r; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK; ++ mpi_request.Header.PageNumber = 0; ++ mpi_request.Header.PageVersion = MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.PageAddress = cpu_to_le32(form | form_specific); ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ r = _config_request(ioc, &mpi_request, mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ sizeof(*config_page)); ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_volume_handle - returns volume handle for give hidden ++ * raid components ++ * @ioc: per adapter object ++ * @pd_handle: phys disk handle ++ * @volume_handle: volume handle ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_volume_handle(struct MPT3SAS_ADAPTER *ioc, u16 pd_handle, ++ u16 *volume_handle) ++{ ++ Mpi2RaidConfigurationPage0_t *config_page = NULL; ++ Mpi2ConfigRequest_t mpi_request; ++ Mpi2ConfigReply_t mpi_reply; ++ int r, i, config_page_sz; ++ u16 ioc_status; ++ int config_num; ++ u16 element_type; ++ u16 phys_disk_dev_handle; ++ ++ *volume_handle = 0; ++ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_CONFIG; ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; ++ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; ++ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG; ++ mpi_request.Header.PageVersion = MPI2_RAIDCONFIG0_PAGEVERSION; ++ mpi_request.Header.PageNumber = 0; ++ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); ++ r = _config_request(ioc, &mpi_request, &mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); ++ if (r) ++ goto out; ++ ++ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; ++ config_page_sz = (le16_to_cpu(mpi_reply.ExtPageLength) * 4); ++ config_page = kmalloc(config_page_sz, GFP_KERNEL); ++ if (!config_page) { ++ r = -1; ++ goto out; ++ } ++ ++ config_num = 0xff; ++ while (1) { ++ mpi_request.PageAddress = cpu_to_le32(config_num + ++ MPI2_RAID_PGAD_FORM_GET_NEXT_CONFIGNUM); ++ r = _config_request(ioc, &mpi_request, &mpi_reply, ++ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, ++ config_page_sz); ++ if (r) ++ goto out; ++ r = -1; ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ goto out; ++ for (i = 0; i < config_page->NumElements; i++) { ++ element_type = le16_to_cpu(config_page-> ++ ConfigElement[i].ElementFlags) & ++ MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE; ++ if (element_type == ++ MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT || ++ element_type == ++ MPI2_RAIDCONFIG0_EFLAGS_OCE_ELEMENT) { ++ phys_disk_dev_handle = ++ le16_to_cpu(config_page->ConfigElement[i]. ++ PhysDiskDevHandle); ++ if (phys_disk_dev_handle == pd_handle) { ++ *volume_handle = ++ le16_to_cpu(config_page-> ++ ConfigElement[i].VolDevHandle); ++ r = 0; ++ goto out; ++ } ++ } else if (element_type == ++ MPI2_RAIDCONFIG0_EFLAGS_HOT_SPARE_ELEMENT) { ++ *volume_handle = 0; ++ r = 0; ++ goto out; ++ } ++ } ++ config_num = config_page->ConfigNum; ++ } ++ out: ++ kfree(config_page); ++ return r; ++} ++ ++/** ++ * mpt2sas_config_get_volume_wwid - returns wwid given the volume handle ++ * @ioc: per adapter object ++ * @volume_handle: volume handle ++ * @wwid: volume wwid ++ * Context: sleep. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_config_get_volume_wwid(struct MPT3SAS_ADAPTER *ioc, u16 volume_handle, ++ u64 *wwid) ++{ ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2RaidVolPage1_t raid_vol_pg1; ++ ++ *wwid = 0; ++ if (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, ++ &raid_vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, ++ volume_handle))) { ++ *wwid = le64_to_cpu(raid_vol_pg1.WWID); ++ return 0; ++ } else ++ return -1; ++} +diff --git a/drivers/scsi/mpt2sas/mpt3sas_ctl.c b/drivers/scsi/mpt2sas/mpt3sas_ctl.c +new file mode 100644 +index 0000000..5c0cc30 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_ctl.c +@@ -0,0 +1,3483 @@ ++/* ++ * Management Module Support for MPT (Message Passing Technology) based ++ * controllers ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_ctl.c ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "mpt3sas_base.h" ++#include "mpt3sas_ctl.h" ++ ++ ++static struct fasync_struct *async_queue; ++static DECLARE_WAIT_QUEUE_HEAD(ctl_poll_wait); ++ ++ ++/** ++ * enum block_state - blocking state ++ * @NON_BLOCKING: non blocking ++ * @BLOCKING: blocking ++ * ++ * These states are for ioctls that need to wait for a response ++ * from firmware, so they probably require sleep. ++ */ ++enum block_state { ++ NON_BLOCKING, ++ BLOCKING, ++}; ++ ++/** ++ * _ctl_sas_device_find_by_handle - sas device search ++ * @ioc: per adapter object ++ * @handle: sas device handle (assigned by firmware) ++ * Context: Calling function should acquire ioc->sas_device_lock ++ * ++ * This searches for sas_device based on sas_address, then return sas_device ++ * object. ++ */ ++static struct _sas_device * ++_ctl_sas_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _sas_device *sas_device, *r; ++ ++ r = NULL; ++ list_for_each_entry(sas_device, &ioc->sas_device_list, list) { ++ if (sas_device->handle != handle) ++ continue; ++ r = sas_device; ++ goto out; ++ } ++ ++ out: ++ return r; ++} ++ ++/** ++ * _ctl_display_some_debug - debug routine ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @calling_function_name: string pass from calling function ++ * @mpi_reply: reply message frame ++ * Context: none. ++ * ++ * Function for displaying debug info helpful when debugging issues ++ * in this module. ++ */ ++static void ++_ctl_display_some_debug(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ char *calling_function_name, MPI2DefaultReply_t *mpi_reply) ++{ ++ Mpi2ConfigRequest_t *mpi_request; ++ char *desc = NULL; ++ ++ if (!(ioc->logging_level & MPT_DEBUG_IOCTL)) ++ return; ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ switch (mpi_request->Function) { ++ case MPI2_FUNCTION_SCSI_IO_REQUEST: ++ { ++ Mpi2SCSIIORequest_t *scsi_request = ++ (Mpi2SCSIIORequest_t *)mpi_request; ++ ++ snprintf(ioc->tmp_string, MPT_STRING_LENGTH, ++ "scsi_io, cmd(0x%02x), cdb_len(%d)", ++ scsi_request->CDB.CDB32[0], ++ le16_to_cpu(scsi_request->IoFlags) & 0xF); ++ desc = ioc->tmp_string; ++ break; ++ } ++ case MPI2_FUNCTION_SCSI_TASK_MGMT: ++ desc = "task_mgmt"; ++ break; ++ case MPI2_FUNCTION_IOC_INIT: ++ desc = "ioc_init"; ++ break; ++ case MPI2_FUNCTION_IOC_FACTS: ++ desc = "ioc_facts"; ++ break; ++ case MPI2_FUNCTION_CONFIG: ++ { ++ Mpi2ConfigRequest_t *config_request = ++ (Mpi2ConfigRequest_t *)mpi_request; ++ ++ snprintf(ioc->tmp_string, MPT_STRING_LENGTH, ++ "config, type(0x%02x), ext_type(0x%02x), number(%d)", ++ (config_request->Header.PageType & ++ MPI2_CONFIG_PAGETYPE_MASK), config_request->ExtPageType, ++ config_request->Header.PageNumber); ++ desc = ioc->tmp_string; ++ break; ++ } ++ case MPI2_FUNCTION_PORT_FACTS: ++ desc = "port_facts"; ++ break; ++ case MPI2_FUNCTION_PORT_ENABLE: ++ desc = "port_enable"; ++ break; ++ case MPI2_FUNCTION_EVENT_NOTIFICATION: ++ desc = "event_notification"; ++ break; ++ case MPI2_FUNCTION_FW_DOWNLOAD: ++ desc = "fw_download"; ++ break; ++ case MPI2_FUNCTION_FW_UPLOAD: ++ desc = "fw_upload"; ++ break; ++ case MPI2_FUNCTION_RAID_ACTION: ++ desc = "raid_action"; ++ break; ++ case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH: ++ { ++ Mpi2SCSIIORequest_t *scsi_request = ++ (Mpi2SCSIIORequest_t *)mpi_request; ++ ++ snprintf(ioc->tmp_string, MPT_STRING_LENGTH, ++ "raid_pass, cmd(0x%02x), cdb_len(%d)", ++ scsi_request->CDB.CDB32[0], ++ le16_to_cpu(scsi_request->IoFlags) & 0xF); ++ desc = ioc->tmp_string; ++ break; ++ } ++ case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: ++ desc = "sas_iounit_cntl"; ++ break; ++ case MPI2_FUNCTION_SATA_PASSTHROUGH: ++ desc = "sata_pass"; ++ break; ++ case MPI2_FUNCTION_DIAG_BUFFER_POST: ++ desc = "diag_buffer_post"; ++ break; ++ case MPI2_FUNCTION_DIAG_RELEASE: ++ desc = "diag_release"; ++ break; ++ case MPI2_FUNCTION_SMP_PASSTHROUGH: ++ desc = "smp_passthrough"; ++ break; ++ } ++ ++ if (!desc) ++ return; ++ ++ pr_info(MPT3SAS_FMT "%s: %s, smid(%d)\n", ++ ioc->name, calling_function_name, desc, smid); ++ ++ if (!mpi_reply) ++ return; ++ ++ if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo) ++ pr_info(MPT3SAS_FMT ++ "\tiocstatus(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply->IOCStatus), ++ le32_to_cpu(mpi_reply->IOCLogInfo)); ++ ++ if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || ++ mpi_request->Function == ++ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { ++ Mpi2SCSIIOReply_t *scsi_reply = ++ (Mpi2SCSIIOReply_t *)mpi_reply; ++ struct _sas_device *sas_device = NULL; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = _ctl_sas_device_find_by_handle(ioc, ++ le16_to_cpu(scsi_reply->DevHandle)); ++ if (sas_device) { ++ pr_warn(MPT3SAS_FMT "\tsas_address(0x%016llx), phy(%d)\n", ++ ioc->name, (unsigned long long) ++ sas_device->sas_address, sas_device->phy); ++ pr_warn(MPT3SAS_FMT ++ "\tenclosure_logical_id(0x%016llx), slot(%d)\n", ++ ioc->name, (unsigned long long) ++ sas_device->enclosure_logical_id, sas_device->slot); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ if (scsi_reply->SCSIState || scsi_reply->SCSIStatus) ++ pr_info(MPT3SAS_FMT ++ "\tscsi_state(0x%02x), scsi_status" ++ "(0x%02x)\n", ioc->name, ++ scsi_reply->SCSIState, ++ scsi_reply->SCSIStatus); ++ } ++} ++ ++/** ++ * mpt2sas_ctl_done - ctl module completion routine ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: none. ++ * ++ * The callback handler when using ioc->ctl_cb_idx. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_ctl_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ Mpi2SCSIIOReply_t *scsiio_reply; ++ const void *sense_data; ++ u32 sz; ++ ++ if (ioc->ctl_cmds.status == MPT3_CMD_NOT_USED) ++ return 1; ++ if (ioc->ctl_cmds.smid != smid) ++ return 1; ++ ioc->ctl_cmds.status |= MPT3_CMD_COMPLETE; ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (mpi_reply) { ++ memcpy(ioc->ctl_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); ++ ioc->ctl_cmds.status |= MPT3_CMD_REPLY_VALID; ++ /* get sense data */ ++ if (mpi_reply->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || ++ mpi_reply->Function == ++ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { ++ scsiio_reply = (Mpi2SCSIIOReply_t *)mpi_reply; ++ if (scsiio_reply->SCSIState & ++ MPI2_SCSI_STATE_AUTOSENSE_VALID) { ++ sz = min_t(u32, SCSI_SENSE_BUFFERSIZE, ++ le32_to_cpu(scsiio_reply->SenseCount)); ++ sense_data = mpt2sas_base_get_sense_buffer(ioc, ++ smid); ++ memcpy(ioc->ctl_cmds.sense, sense_data, sz); ++ } ++ } ++ } ++ _ctl_display_some_debug(ioc, smid, "ctl_done", mpi_reply); ++ ioc->ctl_cmds.status &= ~MPT3_CMD_PENDING; ++ complete(&ioc->ctl_cmds.done); ++ return 1; ++} ++ ++/** ++ * _ctl_check_event_type - determines when an event needs logging ++ * @ioc: per adapter object ++ * @event: firmware event ++ * ++ * The bitmask in ioc->event_type[] indicates which events should be ++ * be saved in the driver event_log. This bitmask is set by application. ++ * ++ * Returns 1 when event should be captured, or zero means no match. ++ */ ++static int ++_ctl_check_event_type(struct MPT3SAS_ADAPTER *ioc, u16 event) ++{ ++ u16 i; ++ u32 desired_event; ++ ++ if (event >= 128 || !event || !ioc->event_log) ++ return 0; ++ ++ desired_event = (1 << (event % 32)); ++ if (!desired_event) ++ desired_event = 1; ++ i = event / 32; ++ return desired_event & ioc->event_type[i]; ++} ++ ++/** ++ * mpt2sas_ctl_add_to_event_log - add event ++ * @ioc: per adapter object ++ * @mpi_reply: reply message frame ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_ctl_add_to_event_log(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventNotificationReply_t *mpi_reply) ++{ ++ struct MPT3_IOCTL_EVENTS *event_log; ++ u16 event; ++ int i; ++ u32 sz, event_data_sz; ++ u8 send_aen = 0; ++ ++ if (!ioc->event_log) ++ return; ++ ++ event = le16_to_cpu(mpi_reply->Event); ++ ++ if (_ctl_check_event_type(ioc, event)) { ++ ++ /* insert entry into circular event_log */ ++ i = ioc->event_context % MPT3SAS_CTL_EVENT_LOG_SIZE; ++ event_log = ioc->event_log; ++ event_log[i].event = event; ++ event_log[i].context = ioc->event_context++; ++ ++ event_data_sz = le16_to_cpu(mpi_reply->EventDataLength)*4; ++ sz = min_t(u32, event_data_sz, MPT3_EVENT_DATA_SIZE); ++ memset(event_log[i].data, 0, MPT3_EVENT_DATA_SIZE); ++ memcpy(event_log[i].data, mpi_reply->EventData, sz); ++ send_aen = 1; ++ } ++ ++ /* This aen_event_read_flag flag is set until the ++ * application has read the event log. ++ * For MPI2_EVENT_LOG_ENTRY_ADDED, we always notify. ++ */ ++ if (event == MPI2_EVENT_LOG_ENTRY_ADDED || ++ (send_aen && !ioc->aen_event_read_flag)) { ++ ioc->aen_event_read_flag = 1; ++ wake_up_interruptible(&ctl_poll_wait); ++ if (async_queue) ++ kill_fasync(&async_queue, SIGIO, POLL_IN); ++ } ++} ++ ++/** ++ * mpt2sas_ctl_event_callback - firmware event handler (called at ISR time) ++ * @ioc: per adapter object ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: interrupt. ++ * ++ * This function merely adds a new work task into ioc->firmware_event_thread. ++ * The tasks are worked from _firmware_event_work in user context. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_ctl_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, ++ u32 reply) ++{ ++ Mpi2EventNotificationReply_t *mpi_reply; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (mpi_reply) ++ mpt2sas_ctl_add_to_event_log(ioc, mpi_reply); ++ return 1; ++} ++ ++/** ++ * _ctl_verify_adapter - validates ioc_number passed from application ++ * @ioc: per adapter object ++ * @iocpp: The ioc pointer is returned in this. ++ * @mpi_version: will be MPI2_VERSION for mpt2ctl ioctl device & ++ * MPI25_VERSION | MPI26_VERSION for mpt3ctl ioctl device. ++ * ++ * Return (-1) means error, else ioc_number. ++ */ ++static int ++_ctl_verify_adapter(int ioc_number, struct MPT3SAS_ADAPTER **iocpp, ++ int mpi_version) ++{ ++ struct MPT3SAS_ADAPTER *ioc; ++ int version = 0; ++ /* global ioc lock to protect controller on list operations */ ++ spin_lock(&gioc_lock_mpt2sas); ++ list_for_each_entry(ioc, &mpt2sas_ioc_list, list) { ++ if (ioc->id != ioc_number) ++ continue; ++ /* Check whether this ioctl command is from right ++ * ioctl device or not, if not continue the search. ++ */ ++ version = ioc->hba_mpi_version_belonged; ++ /* MPI25_VERSION and MPI26_VERSION uses same ioctl ++ * device. ++ */ ++ if (mpi_version == (MPI25_VERSION | MPI26_VERSION)) { ++ if ((version == MPI25_VERSION) || ++ (version == MPI26_VERSION)) ++ goto out; ++ else ++ continue; ++ } else { ++ if (version != mpi_version) ++ continue; ++ } ++out: ++ spin_unlock(&gioc_lock_mpt2sas); ++ *iocpp = ioc; ++ return ioc_number; ++ } ++ spin_unlock(&gioc_lock_mpt2sas); ++ *iocpp = NULL; ++ return -1; ++} ++ ++/** ++ * mpt2sas_ctl_reset_handler - reset callback handler (for ctl) ++ * @ioc: per adapter object ++ * @reset_phase: phase ++ * ++ * The handler for doing any required cleanup or initialization. ++ * ++ * The reset phase can be MPT3_IOC_PRE_RESET, MPT3_IOC_AFTER_RESET, ++ * MPT3_IOC_DONE_RESET ++ */ ++void ++mpt2sas_ctl_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase) ++{ ++ int i; ++ u8 issue_reset; ++ ++ switch (reset_phase) { ++ case MPT3_IOC_PRE_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_PRE_RESET\n", ioc->name, __func__)); ++ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) { ++ if (!(ioc->diag_buffer_status[i] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED)) ++ continue; ++ if ((ioc->diag_buffer_status[i] & ++ MPT3_DIAG_BUFFER_IS_RELEASED)) ++ continue; ++ mpt2sas_send_diag_release(ioc, i, &issue_reset); ++ } ++ break; ++ case MPT3_IOC_AFTER_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_AFTER_RESET\n", ioc->name, __func__)); ++ if (ioc->ctl_cmds.status & MPT3_CMD_PENDING) { ++ ioc->ctl_cmds.status |= MPT3_CMD_RESET; ++ mpt2sas_base_free_smid(ioc, ioc->ctl_cmds.smid); ++ complete(&ioc->ctl_cmds.done); ++ } ++ break; ++ case MPT3_IOC_DONE_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_DONE_RESET\n", ioc->name, __func__)); ++ ++ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) { ++ if (!(ioc->diag_buffer_status[i] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED)) ++ continue; ++ if ((ioc->diag_buffer_status[i] & ++ MPT3_DIAG_BUFFER_IS_RELEASED)) ++ continue; ++ ioc->diag_buffer_status[i] |= ++ MPT3_DIAG_BUFFER_IS_DIAG_RESET; ++ } ++ break; ++ } ++} ++ ++/** ++ * _ctl_fasync_mpt2sas - ++ * @fd - ++ * @filep - ++ * @mode - ++ * ++ * Called when application request fasyn callback handler. ++ */ ++int ++_ctl_fasync_mpt2sas(int fd, struct file *filep, int mode) ++{ ++ return fasync_helper(fd, filep, mode, &async_queue); ++} ++ ++/** ++ * _ctl_poll_mpt2sas - ++ * @file - ++ * @wait - ++ * ++ */ ++unsigned int ++_ctl_poll_mpt2sas(struct file *filep, poll_table *wait) ++{ ++ struct MPT3SAS_ADAPTER *ioc; ++ ++ poll_wait(filep, &ctl_poll_wait, wait); ++ ++ /* global ioc lock to protect controller on list operations */ ++ spin_lock(&gioc_lock_mpt2sas); ++ list_for_each_entry(ioc, &mpt2sas_ioc_list, list) { ++ if (ioc->aen_event_read_flag) { ++ spin_unlock(&gioc_lock_mpt2sas); ++ return POLLIN | POLLRDNORM; ++ } ++ } ++ spin_unlock(&gioc_lock_mpt2sas); ++ return 0; ++} ++ ++/** ++ * _ctl_set_task_mid - assign an active smid to tm request ++ * @ioc: per adapter object ++ * @karg - (struct mpt3_ioctl_command) ++ * @tm_request - pointer to mf from user space ++ * ++ * Returns 0 when an smid if found, else fail. ++ * during failure, the reply frame is filled. ++ */ ++static int ++_ctl_set_task_mid(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command *karg, ++ Mpi2SCSITaskManagementRequest_t *tm_request) ++{ ++ u8 found = 0; ++ u16 i; ++ u16 handle; ++ struct scsi_cmnd *scmd; ++ struct MPT3SAS_DEVICE *priv_data; ++ unsigned long flags; ++ Mpi2SCSITaskManagementReply_t *tm_reply; ++ u32 sz; ++ u32 lun; ++ char *desc = NULL; ++ ++ if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK) ++ desc = "abort_task"; ++ else if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK) ++ desc = "query_task"; ++ else ++ return 0; ++ ++ lun = scsilun_to_int((struct scsi_lun *)tm_request->LUN); ++ ++ handle = le16_to_cpu(tm_request->DevHandle); ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ for (i = ioc->scsiio_depth; i && !found; i--) { ++ scmd = ioc->scsi_lookup[i - 1].scmd; ++ if (scmd == NULL || scmd->device == NULL || ++ scmd->device->hostdata == NULL) ++ continue; ++ if (lun != scmd->device->lun) ++ continue; ++ priv_data = scmd->device->hostdata; ++ if (priv_data->sas_target == NULL) ++ continue; ++ if (priv_data->sas_target->handle != handle) ++ continue; ++ tm_request->TaskMID = cpu_to_le16(ioc->scsi_lookup[i - 1].smid); ++ found = 1; ++ } ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ ++ if (!found) { ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: handle(0x%04x), lun(%d), no active mid!!\n", ++ ioc->name, ++ desc, le16_to_cpu(tm_request->DevHandle), lun)); ++ tm_reply = ioc->ctl_cmds.reply; ++ tm_reply->DevHandle = tm_request->DevHandle; ++ tm_reply->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; ++ tm_reply->TaskType = tm_request->TaskType; ++ tm_reply->MsgLength = sizeof(Mpi2SCSITaskManagementReply_t)/4; ++ tm_reply->VP_ID = tm_request->VP_ID; ++ tm_reply->VF_ID = tm_request->VF_ID; ++ sz = min_t(u32, karg->max_reply_bytes, ioc->reply_sz); ++ if (copy_to_user(karg->reply_frame_buf_ptr, ioc->ctl_cmds.reply, ++ sz)) ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ return 1; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: handle(0x%04x), lun(%d), task_mid(%d)\n", ioc->name, ++ desc, le16_to_cpu(tm_request->DevHandle), lun, ++ le16_to_cpu(tm_request->TaskMID))); ++ return 0; ++} ++ ++/** ++ * _ctl_do_mpt_command - main handler for MPT3COMMAND opcode ++ * @ioc: per adapter object ++ * @karg - (struct mpt3_ioctl_command) ++ * @mf - pointer to mf in user space ++ */ ++static long ++_ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, ++ void __user *mf) ++{ ++ MPI2RequestHeader_t *mpi_request = NULL, *request; ++ MPI2DefaultReply_t *mpi_reply; ++ u32 ioc_state; ++ u16 ioc_status; ++ u16 smid; ++ unsigned long timeout, timeleft; ++ u8 issue_reset; ++ u32 sz; ++ void *psge; ++ void *data_out = NULL; ++ dma_addr_t data_out_dma = 0; ++ size_t data_out_sz = 0; ++ void *data_in = NULL; ++ dma_addr_t data_in_dma = 0; ++ size_t data_in_sz = 0; ++ long ret; ++ u16 wait_state_count; ++ ++ issue_reset = 0; ++ ++ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: ctl_cmd in use\n", ++ ioc->name, __func__); ++ ret = -EAGAIN; ++ goto out; ++ } ++ ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ ret = -EFAULT; ++ goto out; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, ++ __func__, wait_state_count); ++ } ++ if (wait_state_count) ++ pr_info(MPT3SAS_FMT "%s: ioc is operational\n", ++ ioc->name, __func__); ++ ++ mpi_request = kzalloc(ioc->request_sz, GFP_KERNEL); ++ if (!mpi_request) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed obtaining a memory for mpi_request\n", ++ ioc->name, __func__); ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ /* Check for overflow and wraparound */ ++ if (karg.data_sge_offset * 4 > ioc->request_sz || ++ karg.data_sge_offset > (UINT_MAX / 4)) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* copy in request message frame from user */ ++ if (copy_from_user(mpi_request, mf, karg.data_sge_offset*4)) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, __LINE__, ++ __func__); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { ++ smid = mpt2sas_base_get_smid_hpr(ioc, ioc->ctl_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ ret = -EAGAIN; ++ goto out; ++ } ++ } else { ++ ++ smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->ctl_cb_idx, NULL); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ ret = -EAGAIN; ++ goto out; ++ } ++ } ++ ++ ret = 0; ++ ioc->ctl_cmds.status = MPT3_CMD_PENDING; ++ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); ++ request = mpt2sas_base_get_msg_frame(ioc, smid); ++ memcpy(request, mpi_request, karg.data_sge_offset*4); ++ ioc->ctl_cmds.smid = smid; ++ data_out_sz = karg.data_out_size; ++ data_in_sz = karg.data_in_size; ++ ++ if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || ++ mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { ++ if (!le16_to_cpu(mpi_request->FunctionDependent1) || ++ le16_to_cpu(mpi_request->FunctionDependent1) > ++ ioc->facts.MaxDevHandle) { ++ ret = -EINVAL; ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ } ++ ++ /* obtain dma-able memory for data transfer */ ++ if (data_out_sz) /* WRITE */ { ++ data_out = pci_alloc_consistent(ioc->pdev, data_out_sz, ++ &data_out_dma); ++ if (!data_out) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ ret = -ENOMEM; ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ if (copy_from_user(data_out, karg.data_out_buf_ptr, ++ data_out_sz)) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ ret = -EFAULT; ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ } ++ ++ if (data_in_sz) /* READ */ { ++ data_in = pci_alloc_consistent(ioc->pdev, data_in_sz, ++ &data_in_dma); ++ if (!data_in) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ ret = -ENOMEM; ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ } ++ ++ psge = (void *)request + (karg.data_sge_offset*4); ++ ++ /* send command to firmware */ ++ _ctl_display_some_debug(ioc, smid, "ctl_request", NULL); ++ ++ init_completion(&ioc->ctl_cmds.done); ++ switch (mpi_request->Function) { ++ case MPI2_FUNCTION_SCSI_IO_REQUEST: ++ case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH: ++ { ++ Mpi2SCSIIORequest_t *scsiio_request = ++ (Mpi2SCSIIORequest_t *)request; ++ scsiio_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE; ++ scsiio_request->SenseBufferLowAddress = ++ mpt2sas_base_get_sense_buffer_dma(ioc, smid); ++ memset(ioc->ctl_cmds.sense, 0, SCSI_SENSE_BUFFERSIZE); ++ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, ++ data_in_dma, data_in_sz); ++ ++ if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST) ++ mpt2sas_base_put_smid_scsi_io(ioc, smid, ++ le16_to_cpu(mpi_request->FunctionDependent1)); ++ else ++ mpt2sas_base_put_smid_default(ioc, smid); ++ break; ++ } ++ case MPI2_FUNCTION_SCSI_TASK_MGMT: ++ { ++ Mpi2SCSITaskManagementRequest_t *tm_request = ++ (Mpi2SCSITaskManagementRequest_t *)request; ++ ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "TASK_MGMT: handle(0x%04x), task_type(0x%02x)\n", ++ ioc->name, ++ le16_to_cpu(tm_request->DevHandle), tm_request->TaskType)); ++ ++ if (tm_request->TaskType == ++ MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK || ++ tm_request->TaskType == ++ MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK) { ++ if (_ctl_set_task_mid(ioc, &karg, tm_request)) { ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ } ++ ++ mpt2sas_scsih_set_tm_flag(ioc, le16_to_cpu( ++ tm_request->DevHandle)); ++ ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz, ++ data_in_dma, data_in_sz); ++ mpt2sas_base_put_smid_hi_priority(ioc, smid, 0); ++ break; ++ } ++ case MPI2_FUNCTION_SMP_PASSTHROUGH: ++ { ++ Mpi2SmpPassthroughRequest_t *smp_request = ++ (Mpi2SmpPassthroughRequest_t *)mpi_request; ++ u8 *data; ++ ++ /* ioc determines which port to use */ ++ smp_request->PhysicalPort = 0xFF; ++ if (smp_request->PassthroughFlags & ++ MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE) ++ data = (u8 *)&smp_request->SGL; ++ else { ++ if (unlikely(data_out == NULL)) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ mpt2sas_base_free_smid(ioc, smid); ++ ret = -EINVAL; ++ goto out; ++ } ++ data = data_out; ++ } ++ ++ if (data[1] == 0x91 && (data[10] == 1 || data[10] == 2)) { ++ ioc->ioc_link_reset_in_progress = 1; ++ ioc->ignore_loginfos = 1; ++ } ++ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma, ++ data_in_sz); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ break; ++ } ++ case MPI2_FUNCTION_SATA_PASSTHROUGH: ++ case MPI2_FUNCTION_FW_DOWNLOAD: ++ case MPI2_FUNCTION_FW_UPLOAD: ++ { ++ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma, ++ data_in_sz); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ break; ++ } ++ case MPI2_FUNCTION_TOOLBOX: ++ { ++ Mpi2ToolboxCleanRequest_t *toolbox_request = ++ (Mpi2ToolboxCleanRequest_t *)mpi_request; ++ ++ if (toolbox_request->Tool == MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL) { ++ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, ++ data_in_dma, data_in_sz); ++ } else { ++ ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz, ++ data_in_dma, data_in_sz); ++ } ++ mpt2sas_base_put_smid_default(ioc, smid); ++ break; ++ } ++ case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: ++ { ++ Mpi2SasIoUnitControlRequest_t *sasiounit_request = ++ (Mpi2SasIoUnitControlRequest_t *)mpi_request; ++ ++ if (sasiounit_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET ++ || sasiounit_request->Operation == ++ MPI2_SAS_OP_PHY_LINK_RESET) { ++ ioc->ioc_link_reset_in_progress = 1; ++ ioc->ignore_loginfos = 1; ++ } ++ /* drop to default case for posting the request */ ++ } ++ default: ++ ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz, ++ data_in_dma, data_in_sz); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ break; ++ } ++ ++ if (karg.timeout < MPT3_IOCTL_DEFAULT_TIMEOUT) ++ timeout = MPT3_IOCTL_DEFAULT_TIMEOUT; ++ else ++ timeout = karg.timeout; ++ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, ++ timeout*HZ); ++ if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { ++ Mpi2SCSITaskManagementRequest_t *tm_request = ++ (Mpi2SCSITaskManagementRequest_t *)mpi_request; ++ mpt2sas_scsih_clear_tm_flag(ioc, le16_to_cpu( ++ tm_request->DevHandle)); ++ mpt2sas_trigger_master(ioc, MASTER_TRIGGER_TASK_MANAGMENT); ++ } else if ((mpi_request->Function == MPI2_FUNCTION_SMP_PASSTHROUGH || ++ mpi_request->Function == MPI2_FUNCTION_SAS_IO_UNIT_CONTROL) && ++ ioc->ioc_link_reset_in_progress) { ++ ioc->ioc_link_reset_in_progress = 0; ++ ioc->ignore_loginfos = 0; ++ } ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, ++ __func__); ++ _debug_dump_mf(mpi_request, karg.data_sge_offset); ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ goto issue_host_reset; ++ } ++ ++ mpi_reply = ioc->ctl_cmds.reply; ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; ++ ++ if (mpi_reply->Function == MPI2_FUNCTION_SCSI_TASK_MGMT && ++ (ioc->logging_level & MPT_DEBUG_TM)) { ++ Mpi2SCSITaskManagementReply_t *tm_reply = ++ (Mpi2SCSITaskManagementReply_t *)mpi_reply; ++ ++ pr_info(MPT3SAS_FMT "TASK_MGMT: " \ ++ "IOCStatus(0x%04x), IOCLogInfo(0x%08x), " ++ "TerminationCount(0x%08x)\n", ioc->name, ++ le16_to_cpu(tm_reply->IOCStatus), ++ le32_to_cpu(tm_reply->IOCLogInfo), ++ le32_to_cpu(tm_reply->TerminationCount)); ++ } ++ ++ /* copy out xdata to user */ ++ if (data_in_sz) { ++ if (copy_to_user(karg.data_in_buf_ptr, data_in, ++ data_in_sz)) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ ret = -ENODATA; ++ goto out; ++ } ++ } ++ ++ /* copy out reply message frame to user */ ++ if (karg.max_reply_bytes) { ++ sz = min_t(u32, karg.max_reply_bytes, ioc->reply_sz); ++ if (copy_to_user(karg.reply_frame_buf_ptr, ioc->ctl_cmds.reply, ++ sz)) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ ret = -ENODATA; ++ goto out; ++ } ++ } ++ ++ /* copy out sense to user */ ++ if (karg.max_sense_bytes && (mpi_request->Function == ++ MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function == ++ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { ++ sz = min_t(u32, karg.max_sense_bytes, SCSI_SENSE_BUFFERSIZE); ++ if (copy_to_user(karg.sense_data_ptr, ioc->ctl_cmds.sense, ++ sz)) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ ret = -ENODATA; ++ goto out; ++ } ++ } ++ ++ issue_host_reset: ++ if (issue_reset) { ++ ret = -ENODATA; ++ if ((mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || ++ mpi_request->Function == ++ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH || ++ mpi_request->Function == MPI2_FUNCTION_SATA_PASSTHROUGH)) { ++ pr_info(MPT3SAS_FMT "issue target reset: handle = (0x%04x)\n", ++ ioc->name, ++ le16_to_cpu(mpi_request->FunctionDependent1)); ++ mpt2sas_halt_firmware(ioc); ++ mpt2sas_scsih_issue_tm(ioc, ++ le16_to_cpu(mpi_request->FunctionDependent1), 0, 0, ++ 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30, ++ TM_MUTEX_ON); ++ } else ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ } ++ ++ out: ++ ++ /* free memory associated with sg buffers */ ++ if (data_in) ++ pci_free_consistent(ioc->pdev, data_in_sz, data_in, ++ data_in_dma); ++ ++ if (data_out) ++ pci_free_consistent(ioc->pdev, data_out_sz, data_out, ++ data_out_dma); ++ ++ kfree(mpi_request); ++ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED; ++ return ret; ++} ++ ++/** ++ * _ctl_getiocinfo - main handler for MPT3IOCINFO opcode ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ */ ++static long ++_ctl_getiocinfo(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_ioctl_iocinfo karg; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name, ++ __func__)); ++ ++ memset(&karg, 0 , sizeof(karg)); ++ if (ioc->pfacts) ++ karg.port_number = ioc->pfacts[0].PortNumber; ++ karg.hw_rev = ioc->pdev->revision; ++ karg.pci_id = ioc->pdev->device; ++ karg.subsystem_device = ioc->pdev->subsystem_device; ++ karg.subsystem_vendor = ioc->pdev->subsystem_vendor; ++ karg.pci_information.u.bits.bus = ioc->pdev->bus->number; ++ karg.pci_information.u.bits.device = PCI_SLOT(ioc->pdev->devfn); ++ karg.pci_information.u.bits.function = PCI_FUNC(ioc->pdev->devfn); ++ karg.pci_information.segment_id = pci_domain_nr(ioc->pdev->bus); ++ karg.firmware_version = ioc->facts.FWVersion.Word; ++ strcpy(karg.driver_version, ioc->driver_name); ++ strcat(karg.driver_version, "-"); ++ switch (ioc->hba_mpi_version_belonged) { ++ case MPI2_VERSION: ++ if (ioc->is_warpdrive) ++ karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2_SSS6200; ++ else ++ karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2; ++ strcat(karg.driver_version, MPT2SAS_DRIVER_VERSION); ++ break; ++ case MPI25_VERSION: ++ case MPI26_VERSION: ++ karg.adapter_type = MPT3_IOCTL_INTERFACE_SAS3; ++ strcat(karg.driver_version, MPT3SAS_DRIVER_VERSION); ++ break; ++ } ++ karg.bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion); ++ ++ if (copy_to_user(arg, &karg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ return 0; ++} ++ ++/** ++ * _ctl_eventquery - main handler for MPT3EVENTQUERY opcode ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ */ ++static long ++_ctl_eventquery(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_ioctl_eventquery karg; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name, ++ __func__)); ++ ++ karg.event_entries = MPT3SAS_CTL_EVENT_LOG_SIZE; ++ memcpy(karg.event_types, ioc->event_type, ++ MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32)); ++ ++ if (copy_to_user(arg, &karg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ return 0; ++} ++ ++/** ++ * _ctl_eventenable - main handler for MPT3EVENTENABLE opcode ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ */ ++static long ++_ctl_eventenable(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_ioctl_eventenable karg; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name, ++ __func__)); ++ ++ memcpy(ioc->event_type, karg.event_types, ++ MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32)); ++ mpt2sas_base_validate_event_type(ioc, ioc->event_type); ++ ++ if (ioc->event_log) ++ return 0; ++ /* initialize event_log */ ++ ioc->event_context = 0; ++ ioc->aen_event_read_flag = 0; ++ ioc->event_log = kcalloc(MPT3SAS_CTL_EVENT_LOG_SIZE, ++ sizeof(struct MPT3_IOCTL_EVENTS), GFP_KERNEL); ++ if (!ioc->event_log) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -ENOMEM; ++ } ++ return 0; ++} ++ ++/** ++ * _ctl_eventreport - main handler for MPT3EVENTREPORT opcode ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ */ ++static long ++_ctl_eventreport(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_ioctl_eventreport karg; ++ u32 number_bytes, max_events, max; ++ struct mpt3_ioctl_eventreport __user *uarg = arg; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name, ++ __func__)); ++ ++ number_bytes = karg.hdr.max_data_size - ++ sizeof(struct mpt3_ioctl_header); ++ max_events = number_bytes/sizeof(struct MPT3_IOCTL_EVENTS); ++ max = min_t(u32, MPT3SAS_CTL_EVENT_LOG_SIZE, max_events); ++ ++ /* If fewer than 1 event is requested, there must have ++ * been some type of error. ++ */ ++ if (!max || !ioc->event_log) ++ return -ENODATA; ++ ++ number_bytes = max * sizeof(struct MPT3_IOCTL_EVENTS); ++ if (copy_to_user(uarg->event_data, ioc->event_log, number_bytes)) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ /* reset flag so SIGIO can restart */ ++ ioc->aen_event_read_flag = 0; ++ return 0; ++} ++ ++/** ++ * _ctl_do_reset - main handler for MPT3HARDRESET opcode ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ */ ++static long ++_ctl_do_reset(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_ioctl_diag_reset karg; ++ int retval; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery || ++ ioc->is_driver_loading) ++ return -EAGAIN; ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name, ++ __func__)); ++ ++ retval = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ pr_info(MPT3SAS_FMT "host reset: %s\n", ++ ioc->name, ((!retval) ? "SUCCESS" : "FAILED")); ++ return 0; ++} ++ ++/** ++ * _ctl_btdh_search_sas_device - searching for sas device ++ * @ioc: per adapter object ++ * @btdh: btdh ioctl payload ++ */ ++static int ++_ctl_btdh_search_sas_device(struct MPT3SAS_ADAPTER *ioc, ++ struct mpt3_ioctl_btdh_mapping *btdh) ++{ ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ int rc = 0; ++ ++ if (list_empty(&ioc->sas_device_list)) ++ return rc; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ list_for_each_entry(sas_device, &ioc->sas_device_list, list) { ++ if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF && ++ btdh->handle == sas_device->handle) { ++ btdh->bus = sas_device->channel; ++ btdh->id = sas_device->id; ++ rc = 1; ++ goto out; ++ } else if (btdh->bus == sas_device->channel && btdh->id == ++ sas_device->id && btdh->handle == 0xFFFF) { ++ btdh->handle = sas_device->handle; ++ rc = 1; ++ goto out; ++ } ++ } ++ out: ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ return rc; ++} ++ ++/** ++ * _ctl_btdh_search_raid_device - searching for raid device ++ * @ioc: per adapter object ++ * @btdh: btdh ioctl payload ++ */ ++static int ++_ctl_btdh_search_raid_device(struct MPT3SAS_ADAPTER *ioc, ++ struct mpt3_ioctl_btdh_mapping *btdh) ++{ ++ struct _raid_device *raid_device; ++ unsigned long flags; ++ int rc = 0; ++ ++ if (list_empty(&ioc->raid_device_list)) ++ return rc; ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ list_for_each_entry(raid_device, &ioc->raid_device_list, list) { ++ if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF && ++ btdh->handle == raid_device->handle) { ++ btdh->bus = raid_device->channel; ++ btdh->id = raid_device->id; ++ rc = 1; ++ goto out; ++ } else if (btdh->bus == raid_device->channel && btdh->id == ++ raid_device->id && btdh->handle == 0xFFFF) { ++ btdh->handle = raid_device->handle; ++ rc = 1; ++ goto out; ++ } ++ } ++ out: ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ return rc; ++} ++ ++/** ++ * _ctl_btdh_mapping - main handler for MPT3BTDHMAPPING opcode ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ */ ++static long ++_ctl_btdh_mapping(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_ioctl_btdh_mapping karg; ++ int rc; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ rc = _ctl_btdh_search_sas_device(ioc, &karg); ++ if (!rc) ++ _ctl_btdh_search_raid_device(ioc, &karg); ++ ++ if (copy_to_user(arg, &karg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ return 0; ++} ++ ++/** ++ * _ctl_diag_capability - return diag buffer capability ++ * @ioc: per adapter object ++ * @buffer_type: specifies either TRACE, SNAPSHOT, or EXTENDED ++ * ++ * returns 1 when diag buffer support is enabled in firmware ++ */ ++static u8 ++_ctl_diag_capability(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type) ++{ ++ u8 rc = 0; ++ ++ switch (buffer_type) { ++ case MPI2_DIAG_BUF_TYPE_TRACE: ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) ++ rc = 1; ++ break; ++ case MPI2_DIAG_BUF_TYPE_SNAPSHOT: ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) ++ rc = 1; ++ break; ++ case MPI2_DIAG_BUF_TYPE_EXTENDED: ++ if (ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) ++ rc = 1; ++ } ++ ++ return rc; ++} ++ ++ ++/** ++ * _ctl_diag_register_2 - wrapper for registering diag buffer support ++ * @ioc: per adapter object ++ * @diag_register: the diag_register struct passed in from user space ++ * ++ */ ++static long ++_ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc, ++ struct mpt3_diag_register *diag_register) ++{ ++ int rc, i; ++ void *request_data = NULL; ++ dma_addr_t request_data_dma; ++ u32 request_data_sz = 0; ++ Mpi2DiagBufferPostRequest_t *mpi_request; ++ Mpi2DiagBufferPostReply_t *mpi_reply; ++ u8 buffer_type; ++ unsigned long timeleft; ++ u16 smid; ++ u16 ioc_status; ++ u32 ioc_state; ++ u8 issue_reset = 0; ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: ctl_cmd in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ buffer_type = diag_register->buffer_type; ++ if (!_ctl_diag_capability(ioc, buffer_type)) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have capability for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -EPERM; ++ } ++ ++ if (ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) { ++ pr_err(MPT3SAS_FMT ++ "%s: already has a registered buffer for buffer_type(0x%02x)\n", ++ ioc->name, __func__, ++ buffer_type); ++ return -EINVAL; ++ } ++ ++ if (diag_register->requested_buffer_size % 4) { ++ pr_err(MPT3SAS_FMT ++ "%s: the requested_buffer_size is not 4 byte aligned\n", ++ ioc->name, __func__); ++ return -EINVAL; ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ rc = 0; ++ ioc->ctl_cmds.status = MPT3_CMD_PENDING; ++ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->ctl_cmds.smid = smid; ++ ++ request_data = ioc->diag_buffer[buffer_type]; ++ request_data_sz = diag_register->requested_buffer_size; ++ ioc->unique_id[buffer_type] = diag_register->unique_id; ++ ioc->diag_buffer_status[buffer_type] = 0; ++ memcpy(ioc->product_specific[buffer_type], ++ diag_register->product_specific, MPT3_PRODUCT_SPECIFIC_DWORDS); ++ ioc->diagnostic_flags[buffer_type] = diag_register->diagnostic_flags; ++ ++ if (request_data) { ++ request_data_dma = ioc->diag_buffer_dma[buffer_type]; ++ if (request_data_sz != ioc->diag_buffer_sz[buffer_type]) { ++ pci_free_consistent(ioc->pdev, ++ ioc->diag_buffer_sz[buffer_type], ++ request_data, request_data_dma); ++ request_data = NULL; ++ } ++ } ++ ++ if (request_data == NULL) { ++ ioc->diag_buffer_sz[buffer_type] = 0; ++ ioc->diag_buffer_dma[buffer_type] = 0; ++ request_data = pci_alloc_consistent( ++ ioc->pdev, request_data_sz, &request_data_dma); ++ if (request_data == NULL) { ++ pr_err(MPT3SAS_FMT "%s: failed allocating memory" \ ++ " for diag buffers, requested size(%d)\n", ++ ioc->name, __func__, request_data_sz); ++ mpt2sas_base_free_smid(ioc, smid); ++ return -ENOMEM; ++ } ++ ioc->diag_buffer[buffer_type] = request_data; ++ ioc->diag_buffer_sz[buffer_type] = request_data_sz; ++ ioc->diag_buffer_dma[buffer_type] = request_data_dma; ++ } ++ ++ mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST; ++ mpi_request->BufferType = diag_register->buffer_type; ++ mpi_request->Flags = cpu_to_le32(diag_register->diagnostic_flags); ++ mpi_request->BufferAddress = cpu_to_le64(request_data_dma); ++ mpi_request->BufferLength = cpu_to_le32(request_data_sz); ++ mpi_request->VF_ID = 0; /* TODO */ ++ mpi_request->VP_ID = 0; ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: diag_buffer(0x%p), dma(0x%llx), sz(%d)\n", ++ ioc->name, __func__, request_data, ++ (unsigned long long)request_data_dma, ++ le32_to_cpu(mpi_request->BufferLength))); ++ ++ for (i = 0; i < MPT3_PRODUCT_SPECIFIC_DWORDS; i++) ++ mpi_request->ProductSpecific[i] = ++ cpu_to_le32(ioc->product_specific[buffer_type][i]); ++ ++ init_completion(&ioc->ctl_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, ++ MPT3_IOCTL_DEFAULT_TIMEOUT*HZ); ++ ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, ++ __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2DiagBufferPostRequest_t)/4); ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ goto issue_host_reset; ++ } ++ ++ /* process the completed Reply Message Frame */ ++ if ((ioc->ctl_cmds.status & MPT3_CMD_REPLY_VALID) == 0) { ++ pr_err(MPT3SAS_FMT "%s: no reply message\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ++ mpi_reply = ioc->ctl_cmds.reply; ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; ++ ++ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ++ ioc->diag_buffer_status[buffer_type] |= ++ MPT3_DIAG_BUFFER_IS_REGISTERED; ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: success\n", ++ ioc->name, __func__)); ++ } else { ++ pr_info(MPT3SAS_FMT ++ "%s: ioc_status(0x%04x) log_info(0x%08x)\n", ++ ioc->name, __func__, ++ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); ++ rc = -EFAULT; ++ } ++ ++ issue_host_reset: ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ ++ out: ++ ++ if (rc && request_data) ++ pci_free_consistent(ioc->pdev, request_data_sz, ++ request_data, request_data_dma); ++ ++ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED; ++ return rc; ++} ++ ++/** ++ * mpt2sas_enable_diag_buffer - enabling diag_buffers support driver load time ++ * @ioc: per adapter object ++ * @bits_to_register: bitwise field where trace is bit 0, and snapshot is bit 1 ++ * ++ * This is called when command line option diag_buffer_enable is enabled ++ * at driver load time. ++ */ ++void ++mpt2sas_enable_diag_buffer(struct MPT3SAS_ADAPTER *ioc, u8 bits_to_register) ++{ ++ struct mpt3_diag_register diag_register; ++ ++ memset(&diag_register, 0, sizeof(struct mpt3_diag_register)); ++ ++ if (bits_to_register & 1) { ++ pr_info(MPT3SAS_FMT "registering trace buffer support\n", ++ ioc->name); ++ ioc->diag_trigger_master.MasterData = ++ (MASTER_TRIGGER_FW_FAULT + MASTER_TRIGGER_ADAPTER_RESET); ++ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE; ++ /* register for 2MB buffers */ ++ diag_register.requested_buffer_size = 2 * (1024 * 1024); ++ diag_register.unique_id = 0x7075900; ++ _ctl_diag_register_2(ioc, &diag_register); ++ } ++ ++ if (bits_to_register & 2) { ++ pr_info(MPT3SAS_FMT "registering snapshot buffer support\n", ++ ioc->name); ++ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_SNAPSHOT; ++ /* register for 2MB buffers */ ++ diag_register.requested_buffer_size = 2 * (1024 * 1024); ++ diag_register.unique_id = 0x7075901; ++ _ctl_diag_register_2(ioc, &diag_register); ++ } ++ ++ if (bits_to_register & 4) { ++ pr_info(MPT3SAS_FMT "registering extended buffer support\n", ++ ioc->name); ++ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_EXTENDED; ++ /* register for 2MB buffers */ ++ diag_register.requested_buffer_size = 2 * (1024 * 1024); ++ diag_register.unique_id = 0x7075901; ++ _ctl_diag_register_2(ioc, &diag_register); ++ } ++} ++ ++/** ++ * _ctl_diag_register - application register with driver ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ * ++ * This will allow the driver to setup any required buffers that will be ++ * needed by firmware to communicate with the driver. ++ */ ++static long ++_ctl_diag_register(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_diag_register karg; ++ long rc; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ rc = _ctl_diag_register_2(ioc, &karg); ++ return rc; ++} ++ ++/** ++ * _ctl_diag_unregister - application unregister with driver ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ * ++ * This will allow the driver to cleanup any memory allocated for diag ++ * messages and to free up any resources. ++ */ ++static long ++_ctl_diag_unregister(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_diag_unregister karg; ++ void *request_data; ++ dma_addr_t request_data_dma; ++ u32 request_data_sz; ++ u8 buffer_type; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ buffer_type = karg.unique_id & 0x000000ff; ++ if (!_ctl_diag_capability(ioc, buffer_type)) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have capability for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -EPERM; ++ } ++ ++ if ((ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ pr_err(MPT3SAS_FMT ++ "%s: buffer_type(0x%02x) is not registered\n", ++ ioc->name, __func__, buffer_type); ++ return -EINVAL; ++ } ++ if ((ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) == 0) { ++ pr_err(MPT3SAS_FMT ++ "%s: buffer_type(0x%02x) has not been released\n", ++ ioc->name, __func__, buffer_type); ++ return -EINVAL; ++ } ++ ++ if (karg.unique_id != ioc->unique_id[buffer_type]) { ++ pr_err(MPT3SAS_FMT ++ "%s: unique_id(0x%08x) is not registered\n", ++ ioc->name, __func__, karg.unique_id); ++ return -EINVAL; ++ } ++ ++ request_data = ioc->diag_buffer[buffer_type]; ++ if (!request_data) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have memory allocated for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -ENOMEM; ++ } ++ ++ request_data_sz = ioc->diag_buffer_sz[buffer_type]; ++ request_data_dma = ioc->diag_buffer_dma[buffer_type]; ++ pci_free_consistent(ioc->pdev, request_data_sz, ++ request_data, request_data_dma); ++ ioc->diag_buffer[buffer_type] = NULL; ++ ioc->diag_buffer_status[buffer_type] = 0; ++ return 0; ++} ++ ++/** ++ * _ctl_diag_query - query relevant info associated with diag buffers ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ * ++ * The application will send only buffer_type and unique_id. Driver will ++ * inspect unique_id first, if valid, fill in all the info. If unique_id is ++ * 0x00, the driver will return info specified by Buffer Type. ++ */ ++static long ++_ctl_diag_query(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_diag_query karg; ++ void *request_data; ++ int i; ++ u8 buffer_type; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ karg.application_flags = 0; ++ buffer_type = karg.buffer_type; ++ ++ if (!_ctl_diag_capability(ioc, buffer_type)) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have capability for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -EPERM; ++ } ++ ++ if ((ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ pr_err(MPT3SAS_FMT ++ "%s: buffer_type(0x%02x) is not registered\n", ++ ioc->name, __func__, buffer_type); ++ return -EINVAL; ++ } ++ ++ if (karg.unique_id & 0xffffff00) { ++ if (karg.unique_id != ioc->unique_id[buffer_type]) { ++ pr_err(MPT3SAS_FMT ++ "%s: unique_id(0x%08x) is not registered\n", ++ ioc->name, __func__, karg.unique_id); ++ return -EINVAL; ++ } ++ } ++ ++ request_data = ioc->diag_buffer[buffer_type]; ++ if (!request_data) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have buffer for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -ENOMEM; ++ } ++ ++ if (ioc->diag_buffer_status[buffer_type] & MPT3_DIAG_BUFFER_IS_RELEASED) ++ karg.application_flags = (MPT3_APP_FLAGS_APP_OWNED | ++ MPT3_APP_FLAGS_BUFFER_VALID); ++ else ++ karg.application_flags = (MPT3_APP_FLAGS_APP_OWNED | ++ MPT3_APP_FLAGS_BUFFER_VALID | ++ MPT3_APP_FLAGS_FW_BUFFER_ACCESS); ++ ++ for (i = 0; i < MPT3_PRODUCT_SPECIFIC_DWORDS; i++) ++ karg.product_specific[i] = ++ ioc->product_specific[buffer_type][i]; ++ ++ karg.total_buffer_size = ioc->diag_buffer_sz[buffer_type]; ++ karg.driver_added_buffer_size = 0; ++ karg.unique_id = ioc->unique_id[buffer_type]; ++ karg.diagnostic_flags = ioc->diagnostic_flags[buffer_type]; ++ ++ if (copy_to_user(arg, &karg, sizeof(struct mpt3_diag_query))) { ++ pr_err(MPT3SAS_FMT ++ "%s: unable to write mpt3_diag_query data @ %p\n", ++ ioc->name, __func__, arg); ++ return -EFAULT; ++ } ++ return 0; ++} ++ ++/** ++ * mpt2sas_send_diag_release - Diag Release Message ++ * @ioc: per adapter object ++ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED ++ * @issue_reset - specifies whether host reset is required. ++ * ++ */ ++int ++mpt2sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type, ++ u8 *issue_reset) ++{ ++ Mpi2DiagReleaseRequest_t *mpi_request; ++ Mpi2DiagReleaseReply_t *mpi_reply; ++ u16 smid; ++ u16 ioc_status; ++ u32 ioc_state; ++ int rc; ++ unsigned long timeleft; ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ rc = 0; ++ *issue_reset = 0; ++ ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) ++ ioc->diag_buffer_status[buffer_type] |= ++ MPT3_DIAG_BUFFER_IS_RELEASED; ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: skipping due to FAULT state\n", ioc->name, ++ __func__)); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: ctl_cmd in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ ioc->ctl_cmds.status = MPT3_CMD_PENDING; ++ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->ctl_cmds.smid = smid; ++ ++ mpi_request->Function = MPI2_FUNCTION_DIAG_RELEASE; ++ mpi_request->BufferType = buffer_type; ++ mpi_request->VF_ID = 0; /* TODO */ ++ mpi_request->VP_ID = 0; ++ ++ init_completion(&ioc->ctl_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, ++ MPT3_IOCTL_DEFAULT_TIMEOUT*HZ); ++ ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, ++ __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2DiagReleaseRequest_t)/4); ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_RESET)) ++ *issue_reset = 1; ++ rc = -EFAULT; ++ goto out; ++ } ++ ++ /* process the completed Reply Message Frame */ ++ if ((ioc->ctl_cmds.status & MPT3_CMD_REPLY_VALID) == 0) { ++ pr_err(MPT3SAS_FMT "%s: no reply message\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ++ mpi_reply = ioc->ctl_cmds.reply; ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; ++ ++ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ++ ioc->diag_buffer_status[buffer_type] |= ++ MPT3_DIAG_BUFFER_IS_RELEASED; ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: success\n", ++ ioc->name, __func__)); ++ } else { ++ pr_info(MPT3SAS_FMT ++ "%s: ioc_status(0x%04x) log_info(0x%08x)\n", ++ ioc->name, __func__, ++ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); ++ rc = -EFAULT; ++ } ++ ++ out: ++ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED; ++ return rc; ++} ++ ++/** ++ * _ctl_diag_release - request to send Diag Release Message to firmware ++ * @arg - user space buffer containing ioctl content ++ * ++ * This allows ownership of the specified buffer to returned to the driver, ++ * allowing an application to read the buffer without fear that firmware is ++ * overwritting information in the buffer. ++ */ ++static long ++_ctl_diag_release(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_diag_release karg; ++ void *request_data; ++ int rc; ++ u8 buffer_type; ++ u8 issue_reset = 0; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ buffer_type = karg.unique_id & 0x000000ff; ++ if (!_ctl_diag_capability(ioc, buffer_type)) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have capability for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -EPERM; ++ } ++ ++ if ((ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ pr_err(MPT3SAS_FMT ++ "%s: buffer_type(0x%02x) is not registered\n", ++ ioc->name, __func__, buffer_type); ++ return -EINVAL; ++ } ++ ++ if (karg.unique_id != ioc->unique_id[buffer_type]) { ++ pr_err(MPT3SAS_FMT ++ "%s: unique_id(0x%08x) is not registered\n", ++ ioc->name, __func__, karg.unique_id); ++ return -EINVAL; ++ } ++ ++ if (ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) { ++ pr_err(MPT3SAS_FMT ++ "%s: buffer_type(0x%02x) is already released\n", ++ ioc->name, __func__, ++ buffer_type); ++ return 0; ++ } ++ ++ request_data = ioc->diag_buffer[buffer_type]; ++ ++ if (!request_data) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have memory allocated for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -ENOMEM; ++ } ++ ++ /* buffers were released by due to host reset */ ++ if ((ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_DIAG_RESET)) { ++ ioc->diag_buffer_status[buffer_type] |= ++ MPT3_DIAG_BUFFER_IS_RELEASED; ++ ioc->diag_buffer_status[buffer_type] &= ++ ~MPT3_DIAG_BUFFER_IS_DIAG_RESET; ++ pr_err(MPT3SAS_FMT ++ "%s: buffer_type(0x%02x) was released due to host reset\n", ++ ioc->name, __func__, buffer_type); ++ return 0; ++ } ++ ++ rc = mpt2sas_send_diag_release(ioc, buffer_type, &issue_reset); ++ ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ ++ return rc; ++} ++ ++/** ++ * _ctl_diag_read_buffer - request for copy of the diag buffer ++ * @ioc: per adapter object ++ * @arg - user space buffer containing ioctl content ++ */ ++static long ++_ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ++{ ++ struct mpt3_diag_read_buffer karg; ++ struct mpt3_diag_read_buffer __user *uarg = arg; ++ void *request_data, *diag_data; ++ Mpi2DiagBufferPostRequest_t *mpi_request; ++ Mpi2DiagBufferPostReply_t *mpi_reply; ++ int rc, i; ++ u8 buffer_type; ++ unsigned long timeleft, request_size, copy_size; ++ u16 smid; ++ u16 ioc_status; ++ u8 issue_reset = 0; ++ ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, ++ __func__)); ++ ++ buffer_type = karg.unique_id & 0x000000ff; ++ if (!_ctl_diag_capability(ioc, buffer_type)) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have capability for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -EPERM; ++ } ++ ++ if (karg.unique_id != ioc->unique_id[buffer_type]) { ++ pr_err(MPT3SAS_FMT ++ "%s: unique_id(0x%08x) is not registered\n", ++ ioc->name, __func__, karg.unique_id); ++ return -EINVAL; ++ } ++ ++ request_data = ioc->diag_buffer[buffer_type]; ++ if (!request_data) { ++ pr_err(MPT3SAS_FMT ++ "%s: doesn't have buffer for buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type); ++ return -ENOMEM; ++ } ++ ++ request_size = ioc->diag_buffer_sz[buffer_type]; ++ ++ if ((karg.starting_offset % 4) || (karg.bytes_to_read % 4)) { ++ pr_err(MPT3SAS_FMT "%s: either the starting_offset " \ ++ "or bytes_to_read are not 4 byte aligned\n", ioc->name, ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (karg.starting_offset > request_size) ++ return -EINVAL; ++ ++ diag_data = (void *)(request_data + karg.starting_offset); ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: diag_buffer(%p), offset(%d), sz(%d)\n", ++ ioc->name, __func__, ++ diag_data, karg.starting_offset, karg.bytes_to_read)); ++ ++ /* Truncate data on requests that are too large */ ++ if ((diag_data + karg.bytes_to_read < diag_data) || ++ (diag_data + karg.bytes_to_read > request_data + request_size)) ++ copy_size = request_size - karg.starting_offset; ++ else ++ copy_size = karg.bytes_to_read; ++ ++ if (copy_to_user((void __user *)uarg->diagnostic_data, ++ diag_data, copy_size)) { ++ pr_err(MPT3SAS_FMT ++ "%s: Unable to write mpt_diag_read_buffer_t data @ %p\n", ++ ioc->name, __func__, diag_data); ++ return -EFAULT; ++ } ++ ++ if ((karg.flags & MPT3_FLAGS_REREGISTER) == 0) ++ return 0; ++ ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: Reregister buffer_type(0x%02x)\n", ++ ioc->name, __func__, buffer_type)); ++ if ((ioc->diag_buffer_status[buffer_type] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) == 0) { ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: buffer_type(0x%02x) is still registered\n", ++ ioc->name, __func__, buffer_type)); ++ return 0; ++ } ++ /* Get a free request frame and save the message context. ++ */ ++ ++ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: ctl_cmd in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ rc = 0; ++ ioc->ctl_cmds.status = MPT3_CMD_PENDING; ++ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->ctl_cmds.smid = smid; ++ ++ mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST; ++ mpi_request->BufferType = buffer_type; ++ mpi_request->BufferLength = ++ cpu_to_le32(ioc->diag_buffer_sz[buffer_type]); ++ mpi_request->BufferAddress = ++ cpu_to_le64(ioc->diag_buffer_dma[buffer_type]); ++ for (i = 0; i < MPT3_PRODUCT_SPECIFIC_DWORDS; i++) ++ mpi_request->ProductSpecific[i] = ++ cpu_to_le32(ioc->product_specific[buffer_type][i]); ++ mpi_request->VF_ID = 0; /* TODO */ ++ mpi_request->VP_ID = 0; ++ ++ init_completion(&ioc->ctl_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, ++ MPT3_IOCTL_DEFAULT_TIMEOUT*HZ); ++ ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, ++ __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2DiagBufferPostRequest_t)/4); ++ if (!(ioc->ctl_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ goto issue_host_reset; ++ } ++ ++ /* process the completed Reply Message Frame */ ++ if ((ioc->ctl_cmds.status & MPT3_CMD_REPLY_VALID) == 0) { ++ pr_err(MPT3SAS_FMT "%s: no reply message\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ++ mpi_reply = ioc->ctl_cmds.reply; ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; ++ ++ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ++ ioc->diag_buffer_status[buffer_type] |= ++ MPT3_DIAG_BUFFER_IS_REGISTERED; ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: success\n", ++ ioc->name, __func__)); ++ } else { ++ pr_info(MPT3SAS_FMT ++ "%s: ioc_status(0x%04x) log_info(0x%08x)\n", ++ ioc->name, __func__, ++ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); ++ rc = -EFAULT; ++ } ++ ++ issue_host_reset: ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ ++ out: ++ ++ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED; ++ return rc; ++} ++ ++ ++ ++#ifdef CONFIG_COMPAT ++/** ++ * _ctl_compat_mpt_command - convert 32bit pointers to 64bit. ++ * @ioc: per adapter object ++ * @cmd - ioctl opcode ++ * @arg - (struct mpt3_ioctl_command32) ++ * ++ * MPT3COMMAND32 - Handle 32bit applications running on 64bit os. ++ */ ++static long ++_ctl_compat_mpt_command(struct MPT3SAS_ADAPTER *ioc, unsigned cmd, ++ void __user *arg) ++{ ++ struct mpt3_ioctl_command32 karg32; ++ struct mpt3_ioctl_command32 __user *uarg; ++ struct mpt3_ioctl_command karg; ++ ++ if (_IOC_SIZE(cmd) != sizeof(struct mpt3_ioctl_command32)) ++ return -EINVAL; ++ ++ uarg = (struct mpt3_ioctl_command32 __user *) arg; ++ ++ if (copy_from_user(&karg32, (char __user *)arg, sizeof(karg32))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ memset(&karg, 0, sizeof(struct mpt3_ioctl_command)); ++ karg.hdr.ioc_number = karg32.hdr.ioc_number; ++ karg.hdr.port_number = karg32.hdr.port_number; ++ karg.hdr.max_data_size = karg32.hdr.max_data_size; ++ karg.timeout = karg32.timeout; ++ karg.max_reply_bytes = karg32.max_reply_bytes; ++ karg.data_in_size = karg32.data_in_size; ++ karg.data_out_size = karg32.data_out_size; ++ karg.max_sense_bytes = karg32.max_sense_bytes; ++ karg.data_sge_offset = karg32.data_sge_offset; ++ karg.reply_frame_buf_ptr = compat_ptr(karg32.reply_frame_buf_ptr); ++ karg.data_in_buf_ptr = compat_ptr(karg32.data_in_buf_ptr); ++ karg.data_out_buf_ptr = compat_ptr(karg32.data_out_buf_ptr); ++ karg.sense_data_ptr = compat_ptr(karg32.sense_data_ptr); ++ return _ctl_do_mpt_command(ioc, karg, &uarg->mf); ++} ++#endif ++ ++/** ++ * _ctl_ioctl_main - main ioctl entry point ++ * @file - (struct file) ++ * @cmd - ioctl opcode ++ * @arg - user space data buffer ++ * @compat - handles 32 bit applications in 64bit os ++ * @mpi_version: will be MPI2_VERSION for mpt2ctl ioctl device & ++ * MPI25_VERSION | MPI26_VERSION for mpt3ctl ioctl device. ++ */ ++static long ++_ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg, ++ u8 compat, u16 mpi_version) ++{ ++ struct MPT3SAS_ADAPTER *ioc; ++ struct mpt3_ioctl_header ioctl_header; ++ enum block_state state; ++ long ret = -EINVAL; ++ ++ /* get IOCTL header */ ++ if (copy_from_user(&ioctl_header, (char __user *)arg, ++ sizeof(struct mpt3_ioctl_header))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ return -EFAULT; ++ } ++ ++ if (_ctl_verify_adapter(ioctl_header.ioc_number, ++ &ioc, mpi_version) == -1 || !ioc) ++ return -ENODEV; ++ ++ /* pci_access_mutex lock acquired by ioctl path */ ++ mutex_lock(&ioc->pci_access_mutex); ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery || ++ ioc->is_driver_loading || ioc->remove_host) { ++ ret = -EAGAIN; ++ goto out_unlock_pciaccess; ++ } ++ ++ state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING; ++ if (state == NON_BLOCKING) { ++ if (!mutex_trylock(&ioc->ctl_cmds.mutex)) { ++ ret = -EAGAIN; ++ goto out_unlock_pciaccess; ++ } ++ } else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) { ++ ret = -ERESTARTSYS; ++ goto out_unlock_pciaccess; ++ } ++ ++ ++ switch (cmd) { ++ case MPT3IOCINFO: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_iocinfo)) ++ ret = _ctl_getiocinfo(ioc, arg); ++ break; ++#ifdef CONFIG_COMPAT ++ case MPT3COMMAND32: ++#endif ++ case MPT3COMMAND: ++ { ++ struct mpt3_ioctl_command __user *uarg; ++ struct mpt3_ioctl_command karg; ++ ++#ifdef CONFIG_COMPAT ++ if (compat) { ++ ret = _ctl_compat_mpt_command(ioc, cmd, arg); ++ break; ++ } ++#endif ++ if (copy_from_user(&karg, arg, sizeof(karg))) { ++ pr_err("failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ ret = -EFAULT; ++ break; ++ } ++ ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_command)) { ++ uarg = arg; ++ ret = _ctl_do_mpt_command(ioc, karg, &uarg->mf); ++ } ++ break; ++ } ++ case MPT3EVENTQUERY: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_eventquery)) ++ ret = _ctl_eventquery(ioc, arg); ++ break; ++ case MPT3EVENTENABLE: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_eventenable)) ++ ret = _ctl_eventenable(ioc, arg); ++ break; ++ case MPT3EVENTREPORT: ++ ret = _ctl_eventreport(ioc, arg); ++ break; ++ case MPT3HARDRESET: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_diag_reset)) ++ ret = _ctl_do_reset(ioc, arg); ++ break; ++ case MPT3BTDHMAPPING: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_btdh_mapping)) ++ ret = _ctl_btdh_mapping(ioc, arg); ++ break; ++ case MPT3DIAGREGISTER: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_register)) ++ ret = _ctl_diag_register(ioc, arg); ++ break; ++ case MPT3DIAGUNREGISTER: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_unregister)) ++ ret = _ctl_diag_unregister(ioc, arg); ++ break; ++ case MPT3DIAGQUERY: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_query)) ++ ret = _ctl_diag_query(ioc, arg); ++ break; ++ case MPT3DIAGRELEASE: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_release)) ++ ret = _ctl_diag_release(ioc, arg); ++ break; ++ case MPT3DIAGREADBUFFER: ++ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_read_buffer)) ++ ret = _ctl_diag_read_buffer(ioc, arg); ++ break; ++ default: ++ dctlprintk(ioc, pr_info(MPT3SAS_FMT ++ "unsupported ioctl opcode(0x%08x)\n", ioc->name, cmd)); ++ break; ++ } ++ ++ mutex_unlock(&ioc->ctl_cmds.mutex); ++out_unlock_pciaccess: ++ mutex_unlock(&ioc->pci_access_mutex); ++ return ret; ++} ++ ++/** ++ * _ctl_ioctl_mpt2sas - mpt3ctl main ioctl entry point (unlocked) ++ * @file - (struct file) ++ * @cmd - ioctl opcode ++ * @arg - ++ */ ++long ++_ctl_ioctl_mpt2sas(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long ret; ++ ++ /* pass MPI25_VERSION | MPI26_VERSION value, ++ * to indicate that this ioctl cmd ++ * came from mpt3ctl ioctl device. ++ */ ++ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 0, ++ MPI25_VERSION | MPI26_VERSION); ++ return ret; ++} ++ ++/** ++ * _ctl_mpt2_ioctl_mpt2sas - mpt2ctl main ioctl entry point (unlocked) ++ * @file - (struct file) ++ * @cmd - ioctl opcode ++ * @arg - ++ */ ++long ++_ctl_mpt2_ioctl_mpt2sas(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long ret; ++ ++ /* pass MPI2_VERSION value, to indicate that this ioctl cmd ++ * came from mpt2ctl ioctl device. ++ */ ++ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 0, MPI2_VERSION); ++ return ret; ++} ++#ifdef CONFIG_COMPAT ++/** ++ *_ ctl_ioctl_compat - main ioctl entry point (compat) ++ * @file - ++ * @cmd - ++ * @arg - ++ * ++ * This routine handles 32 bit applications in 64bit os. ++ */ ++long ++_ctl_ioctl_compat_mpt2sas(struct file *file, unsigned cmd, unsigned long arg) ++{ ++ long ret; ++ ++ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 1, ++ MPI25_VERSION | MPI26_VERSION); ++ return ret; ++} ++ ++/** ++ *_ ctl_mpt2_ioctl_compat - main ioctl entry point (compat) ++ * @file - ++ * @cmd - ++ * @arg - ++ * ++ * This routine handles 32 bit applications in 64bit os. ++ */ ++long ++_ctl_mpt2_ioctl_compat_mpt2sas(struct file *file, unsigned cmd, unsigned long arg) ++{ ++ long ret; ++ ++ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 1, MPI2_VERSION); ++ return ret; ++} ++#endif ++ ++/* scsi host attributes */ ++/** ++ * _ctl_version_fw_show - firmware version ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_version_fw_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n", ++ (ioc->facts.FWVersion.Word & 0xFF000000) >> 24, ++ (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16, ++ (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8, ++ ioc->facts.FWVersion.Word & 0x000000FF); ++} ++static DEVICE_ATTR(version_fw, S_IRUGO, _ctl_version_fw_show, NULL); ++ ++/** ++ * _ctl_version_bios_show - bios version ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_version_bios_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ u32 version = le32_to_cpu(ioc->bios_pg3.BiosVersion); ++ ++ return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n", ++ (version & 0xFF000000) >> 24, ++ (version & 0x00FF0000) >> 16, ++ (version & 0x0000FF00) >> 8, ++ version & 0x000000FF); ++} ++static DEVICE_ATTR(version_bios, S_IRUGO, _ctl_version_bios_show, NULL); ++ ++/** ++ * _ctl_version_mpi_show - MPI (message passing interface) version ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_version_mpi_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%03x.%02x\n", ++ ioc->facts.MsgVersion, ioc->facts.HeaderVersion >> 8); ++} ++static DEVICE_ATTR(version_mpi, S_IRUGO, _ctl_version_mpi_show, NULL); ++ ++/** ++ * _ctl_version_product_show - product name ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_version_product_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.ChipName); ++} ++static DEVICE_ATTR(version_product, S_IRUGO, _ctl_version_product_show, NULL); ++ ++/** ++ * _ctl_version_nvdata_persistent_show - ndvata persistent version ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_version_nvdata_persistent_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%08xh\n", ++ le32_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word)); ++} ++static DEVICE_ATTR(version_nvdata_persistent, S_IRUGO, ++ _ctl_version_nvdata_persistent_show, NULL); ++ ++/** ++ * _ctl_version_nvdata_default_show - nvdata default version ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_version_nvdata_default_show(struct device *cdev, struct device_attribute ++ *attr, char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%08xh\n", ++ le32_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word)); ++} ++static DEVICE_ATTR(version_nvdata_default, S_IRUGO, ++ _ctl_version_nvdata_default_show, NULL); ++ ++/** ++ * _ctl_board_name_show - board name ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_board_name_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardName); ++} ++static DEVICE_ATTR(board_name, S_IRUGO, _ctl_board_name_show, NULL); ++ ++/** ++ * _ctl_board_assembly_show - board assembly name ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_board_assembly_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardAssembly); ++} ++static DEVICE_ATTR(board_assembly, S_IRUGO, _ctl_board_assembly_show, NULL); ++ ++/** ++ * _ctl_board_tracer_show - board tracer number ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_board_tracer_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardTracerNumber); ++} ++static DEVICE_ATTR(board_tracer, S_IRUGO, _ctl_board_tracer_show, NULL); ++ ++/** ++ * _ctl_io_delay_show - io missing delay ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is for firmware implemention for deboucing device ++ * removal events. ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_io_delay_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->io_missing_delay); ++} ++static DEVICE_ATTR(io_delay, S_IRUGO, _ctl_io_delay_show, NULL); ++ ++/** ++ * _ctl_device_delay_show - device missing delay ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is for firmware implemention for deboucing device ++ * removal events. ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_device_delay_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->device_missing_delay); ++} ++static DEVICE_ATTR(device_delay, S_IRUGO, _ctl_device_delay_show, NULL); ++ ++/** ++ * _ctl_fw_queue_depth_show - global credits ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is firmware queue depth limit ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_fw_queue_depth_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->facts.RequestCredit); ++} ++static DEVICE_ATTR(fw_queue_depth, S_IRUGO, _ctl_fw_queue_depth_show, NULL); ++ ++/** ++ * _ctl_sas_address_show - sas address ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is the controller sas address ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_host_sas_address_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++ ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "0x%016llx\n", ++ (unsigned long long)ioc->sas_hba.sas_address); ++} ++static DEVICE_ATTR(host_sas_address, S_IRUGO, ++ _ctl_host_sas_address_show, NULL); ++ ++/** ++ * _ctl_logging_level_show - logging level ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_logging_level_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%08xh\n", ioc->logging_level); ++} ++static ssize_t ++_ctl_logging_level_store(struct device *cdev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ int val = 0; ++ ++ if (sscanf(buf, "%x", &val) != 1) ++ return -EINVAL; ++ ++ ioc->logging_level = val; ++ pr_info(MPT3SAS_FMT "logging_level=%08xh\n", ioc->name, ++ ioc->logging_level); ++ return strlen(buf); ++} ++static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR, _ctl_logging_level_show, ++ _ctl_logging_level_store); ++ ++/** ++ * _ctl_fwfault_debug_show - show/store fwfault_debug ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * mpt2sas_fwfault_debug is command line option ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_fwfault_debug_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", ioc->fwfault_debug); ++} ++static ssize_t ++_ctl_fwfault_debug_store(struct device *cdev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ int val = 0; ++ ++ if (sscanf(buf, "%d", &val) != 1) ++ return -EINVAL; ++ ++ ioc->fwfault_debug = val; ++ pr_info(MPT3SAS_FMT "fwfault_debug=%d\n", ioc->name, ++ ioc->fwfault_debug); ++ return strlen(buf); ++} ++static DEVICE_ATTR(fwfault_debug, S_IRUGO | S_IWUSR, ++ _ctl_fwfault_debug_show, _ctl_fwfault_debug_store); ++ ++/** ++ * _ctl_ioc_reset_count_show - ioc reset count ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is firmware queue depth limit ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_ioc_reset_count_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", ioc->ioc_reset_count); ++} ++static DEVICE_ATTR(ioc_reset_count, S_IRUGO, _ctl_ioc_reset_count_show, NULL); ++ ++/** ++ * _ctl_ioc_reply_queue_count_show - number of reply queues ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is number of reply queues ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_ioc_reply_queue_count_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++{ ++ u8 reply_queue_count; ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ if ((ioc->facts.IOCCapabilities & ++ MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable) ++ reply_queue_count = ioc->reply_queue_count; ++ else ++ reply_queue_count = 1; ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", reply_queue_count); ++} ++static DEVICE_ATTR(reply_queue_count, S_IRUGO, _ctl_ioc_reply_queue_count_show, ++ NULL); ++ ++/** ++ * _ctl_BRM_status_show - Backup Rail Monitor Status ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is number of reply queues ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ Mpi2IOUnitPage3_t *io_unit_pg3 = NULL; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 backup_rail_monitor_status = 0; ++ u16 ioc_status; ++ int sz; ++ ssize_t rc = 0; ++ ++ if (!ioc->is_warpdrive) { ++ pr_err(MPT3SAS_FMT "%s: BRM attribute is only for" ++ " warpdrive\n", ioc->name, __func__); ++ goto out; ++ } ++ /* pci_access_mutex lock acquired by sysfs show path */ ++ mutex_lock(&ioc->pci_access_mutex); ++ if (ioc->pci_error_recovery || ioc->remove_host) { ++ mutex_unlock(&ioc->pci_access_mutex); ++ return 0; ++ } ++ ++ /* allocate upto GPIOVal 36 entries */ ++ sz = offsetof(Mpi2IOUnitPage3_t, GPIOVal) + (sizeof(u16) * 36); ++ io_unit_pg3 = kzalloc(sz, GFP_KERNEL); ++ if (!io_unit_pg3) { ++ pr_err(MPT3SAS_FMT "%s: failed allocating memory " ++ "for iounit_pg3: (%d) bytes\n", ioc->name, __func__, sz); ++ goto out; ++ } ++ ++ if (mpt2sas_config_get_iounit_pg3(ioc, &mpi_reply, io_unit_pg3, sz) != ++ 0) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed reading iounit_pg3\n", ioc->name, ++ __func__); ++ goto out; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "%s: iounit_pg3 failed with " ++ "ioc_status(0x%04x)\n", ioc->name, __func__, ioc_status); ++ goto out; ++ } ++ ++ if (io_unit_pg3->GPIOCount < 25) { ++ pr_err(MPT3SAS_FMT "%s: iounit_pg3->GPIOCount less than " ++ "25 entries, detected (%d) entries\n", ioc->name, __func__, ++ io_unit_pg3->GPIOCount); ++ goto out; ++ } ++ ++ /* BRM status is in bit zero of GPIOVal[24] */ ++ backup_rail_monitor_status = le16_to_cpu(io_unit_pg3->GPIOVal[24]); ++ rc = snprintf(buf, PAGE_SIZE, "%d\n", (backup_rail_monitor_status & 1)); ++ ++ out: ++ kfree(io_unit_pg3); ++ mutex_unlock(&ioc->pci_access_mutex); ++ return rc; ++} ++static DEVICE_ATTR(BRM_status, S_IRUGO, _ctl_BRM_status_show, NULL); ++ ++struct DIAG_BUFFER_START { ++ __le32 Size; ++ __le32 DiagVersion; ++ u8 BufferType; ++ u8 Reserved[3]; ++ __le32 Reserved1; ++ __le32 Reserved2; ++ __le32 Reserved3; ++}; ++ ++/** ++ * _ctl_host_trace_buffer_size_show - host buffer size (trace only) ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_host_trace_buffer_size_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ u32 size = 0; ++ struct DIAG_BUFFER_START *request_data; ++ ++ if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) { ++ pr_err(MPT3SAS_FMT ++ "%s: host_trace_buffer is not registered\n", ++ ioc->name, __func__); ++ return 0; ++ } ++ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ pr_err(MPT3SAS_FMT ++ "%s: host_trace_buffer is not registered\n", ++ ioc->name, __func__); ++ return 0; ++ } ++ ++ request_data = (struct DIAG_BUFFER_START *) ++ ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]; ++ if ((le32_to_cpu(request_data->DiagVersion) == 0x00000000 || ++ le32_to_cpu(request_data->DiagVersion) == 0x01000000 || ++ le32_to_cpu(request_data->DiagVersion) == 0x01010000) && ++ le32_to_cpu(request_data->Reserved3) == 0x4742444c) ++ size = le32_to_cpu(request_data->Size); ++ ++ ioc->ring_buffer_sz = size; ++ return snprintf(buf, PAGE_SIZE, "%d\n", size); ++} ++static DEVICE_ATTR(host_trace_buffer_size, S_IRUGO, ++ _ctl_host_trace_buffer_size_show, NULL); ++ ++/** ++ * _ctl_host_trace_buffer_show - firmware ring buffer (trace only) ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ * ++ * You will only be able to read 4k bytes of ring buffer at a time. ++ * In order to read beyond 4k bytes, you will have to write out the ++ * offset to the same attribute, it will move the pointer. ++ */ ++static ssize_t ++_ctl_host_trace_buffer_show(struct device *cdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ void *request_data; ++ u32 size; ++ ++ if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) { ++ pr_err(MPT3SAS_FMT ++ "%s: host_trace_buffer is not registered\n", ++ ioc->name, __func__); ++ return 0; ++ } ++ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ pr_err(MPT3SAS_FMT ++ "%s: host_trace_buffer is not registered\n", ++ ioc->name, __func__); ++ return 0; ++ } ++ ++ if (ioc->ring_buffer_offset > ioc->ring_buffer_sz) ++ return 0; ++ ++ size = ioc->ring_buffer_sz - ioc->ring_buffer_offset; ++ size = (size >= PAGE_SIZE) ? (PAGE_SIZE - 1) : size; ++ request_data = ioc->diag_buffer[0] + ioc->ring_buffer_offset; ++ memcpy(buf, request_data, size); ++ return size; ++} ++ ++static ssize_t ++_ctl_host_trace_buffer_store(struct device *cdev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ int val = 0; ++ ++ if (sscanf(buf, "%d", &val) != 1) ++ return -EINVAL; ++ ++ ioc->ring_buffer_offset = val; ++ return strlen(buf); ++} ++static DEVICE_ATTR(host_trace_buffer, S_IRUGO | S_IWUSR, ++ _ctl_host_trace_buffer_show, _ctl_host_trace_buffer_store); ++ ++ ++/*****************************************/ ++ ++/** ++ * _ctl_host_trace_buffer_enable_show - firmware ring buffer (trace only) ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ * ++ * This is a mechnism to post/release host_trace_buffers ++ */ ++static ssize_t ++_ctl_host_trace_buffer_enable_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ if ((!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) || ++ ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0)) ++ return snprintf(buf, PAGE_SIZE, "off\n"); ++ else if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED)) ++ return snprintf(buf, PAGE_SIZE, "release\n"); ++ else ++ return snprintf(buf, PAGE_SIZE, "post\n"); ++} ++ ++static ssize_t ++_ctl_host_trace_buffer_enable_store(struct device *cdev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ char str[10] = ""; ++ struct mpt3_diag_register diag_register; ++ u8 issue_reset = 0; ++ ++ /* don't allow post/release occurr while recovery is active */ ++ if (ioc->shost_recovery || ioc->remove_host || ++ ioc->pci_error_recovery || ioc->is_driver_loading) ++ return -EBUSY; ++ ++ if (sscanf(buf, "%9s", str) != 1) ++ return -EINVAL; ++ ++ if (!strcmp(str, "post")) { ++ /* exit out if host buffers are already posted */ ++ if ((ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) && ++ (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) && ++ ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) == 0)) ++ goto out; ++ memset(&diag_register, 0, sizeof(struct mpt3_diag_register)); ++ pr_info(MPT3SAS_FMT "posting host trace buffers\n", ++ ioc->name); ++ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE; ++ diag_register.requested_buffer_size = (1024 * 1024); ++ diag_register.unique_id = 0x7075900; ++ ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] = 0; ++ _ctl_diag_register_2(ioc, &diag_register); ++ } else if (!strcmp(str, "release")) { ++ /* exit out if host buffers are already released */ ++ if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) ++ goto out; ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) ++ goto out; ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED)) ++ goto out; ++ pr_info(MPT3SAS_FMT "releasing host trace buffer\n", ++ ioc->name); ++ mpt2sas_send_diag_release(ioc, MPI2_DIAG_BUF_TYPE_TRACE, ++ &issue_reset); ++ } ++ ++ out: ++ return strlen(buf); ++} ++static DEVICE_ATTR(host_trace_buffer_enable, S_IRUGO | S_IWUSR, ++ _ctl_host_trace_buffer_enable_show, ++ _ctl_host_trace_buffer_enable_store); ++ ++/*********** diagnostic trigger suppport *********************************/ ++ ++/** ++ * _ctl_diag_trigger_master_show - show the diag_trigger_master attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_master_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++ ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t rc; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ rc = sizeof(struct SL_WH_MASTER_TRIGGER_T); ++ memcpy(buf, &ioc->diag_trigger_master, rc); ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return rc; ++} ++ ++/** ++ * _ctl_diag_trigger_master_store - store the diag_trigger_master attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_master_store(struct device *cdev, ++ struct device_attribute *attr, const char *buf, size_t count) ++ ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t rc; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ rc = min(sizeof(struct SL_WH_MASTER_TRIGGER_T), count); ++ memset(&ioc->diag_trigger_master, 0, ++ sizeof(struct SL_WH_MASTER_TRIGGER_T)); ++ memcpy(&ioc->diag_trigger_master, buf, rc); ++ ioc->diag_trigger_master.MasterData |= ++ (MASTER_TRIGGER_FW_FAULT + MASTER_TRIGGER_ADAPTER_RESET); ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return rc; ++} ++static DEVICE_ATTR(diag_trigger_master, S_IRUGO | S_IWUSR, ++ _ctl_diag_trigger_master_show, _ctl_diag_trigger_master_store); ++ ++ ++/** ++ * _ctl_diag_trigger_event_show - show the diag_trigger_event attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_event_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t rc; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ rc = sizeof(struct SL_WH_EVENT_TRIGGERS_T); ++ memcpy(buf, &ioc->diag_trigger_event, rc); ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return rc; ++} ++ ++/** ++ * _ctl_diag_trigger_event_store - store the diag_trigger_event attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_event_store(struct device *cdev, ++ struct device_attribute *attr, const char *buf, size_t count) ++ ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t sz; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ sz = min(sizeof(struct SL_WH_EVENT_TRIGGERS_T), count); ++ memset(&ioc->diag_trigger_event, 0, ++ sizeof(struct SL_WH_EVENT_TRIGGERS_T)); ++ memcpy(&ioc->diag_trigger_event, buf, sz); ++ if (ioc->diag_trigger_event.ValidEntries > NUM_VALID_ENTRIES) ++ ioc->diag_trigger_event.ValidEntries = NUM_VALID_ENTRIES; ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return sz; ++} ++static DEVICE_ATTR(diag_trigger_event, S_IRUGO | S_IWUSR, ++ _ctl_diag_trigger_event_show, _ctl_diag_trigger_event_store); ++ ++ ++/** ++ * _ctl_diag_trigger_scsi_show - show the diag_trigger_scsi attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_scsi_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t rc; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ rc = sizeof(struct SL_WH_SCSI_TRIGGERS_T); ++ memcpy(buf, &ioc->diag_trigger_scsi, rc); ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return rc; ++} ++ ++/** ++ * _ctl_diag_trigger_scsi_store - store the diag_trigger_scsi attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_scsi_store(struct device *cdev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t sz; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ sz = min(sizeof(struct SL_WH_SCSI_TRIGGERS_T), count); ++ memset(&ioc->diag_trigger_scsi, 0, ++ sizeof(struct SL_WH_EVENT_TRIGGERS_T)); ++ memcpy(&ioc->diag_trigger_scsi, buf, sz); ++ if (ioc->diag_trigger_scsi.ValidEntries > NUM_VALID_ENTRIES) ++ ioc->diag_trigger_scsi.ValidEntries = NUM_VALID_ENTRIES; ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return sz; ++} ++static DEVICE_ATTR(diag_trigger_scsi, S_IRUGO | S_IWUSR, ++ _ctl_diag_trigger_scsi_show, _ctl_diag_trigger_scsi_store); ++ ++ ++/** ++ * _ctl_diag_trigger_scsi_show - show the diag_trigger_mpi attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_mpi_show(struct device *cdev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t rc; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ rc = sizeof(struct SL_WH_MPI_TRIGGERS_T); ++ memcpy(buf, &ioc->diag_trigger_mpi, rc); ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return rc; ++} ++ ++/** ++ * _ctl_diag_trigger_mpi_store - store the diag_trigger_mpi attribute ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * A sysfs 'read/write' shost attribute. ++ */ ++static ssize_t ++_ctl_diag_trigger_mpi_store(struct device *cdev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ unsigned long flags; ++ ssize_t sz; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ sz = min(sizeof(struct SL_WH_MPI_TRIGGERS_T), count); ++ memset(&ioc->diag_trigger_mpi, 0, ++ sizeof(ioc->diag_trigger_mpi)); ++ memcpy(&ioc->diag_trigger_mpi, buf, sz); ++ if (ioc->diag_trigger_mpi.ValidEntries > NUM_VALID_ENTRIES) ++ ioc->diag_trigger_mpi.ValidEntries = NUM_VALID_ENTRIES; ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return sz; ++} ++ ++static DEVICE_ATTR(diag_trigger_mpi, S_IRUGO | S_IWUSR, ++ _ctl_diag_trigger_mpi_show, _ctl_diag_trigger_mpi_store); ++ ++/*********** diagnostic trigger suppport *** END ****************************/ ++ ++ ++ ++/*****************************************/ ++ ++struct device_attribute *mpt2sas_host_attrs[] = { ++ &dev_attr_version_fw, ++ &dev_attr_version_bios, ++ &dev_attr_version_mpi, ++ &dev_attr_version_product, ++ &dev_attr_version_nvdata_persistent, ++ &dev_attr_version_nvdata_default, ++ &dev_attr_board_name, ++ &dev_attr_board_assembly, ++ &dev_attr_board_tracer, ++ &dev_attr_io_delay, ++ &dev_attr_device_delay, ++ &dev_attr_logging_level, ++ &dev_attr_fwfault_debug, ++ &dev_attr_fw_queue_depth, ++ &dev_attr_host_sas_address, ++ &dev_attr_ioc_reset_count, ++ &dev_attr_host_trace_buffer_size, ++ &dev_attr_host_trace_buffer, ++ &dev_attr_host_trace_buffer_enable, ++ &dev_attr_reply_queue_count, ++ &dev_attr_diag_trigger_master, ++ &dev_attr_diag_trigger_event, ++ &dev_attr_diag_trigger_scsi, ++ &dev_attr_diag_trigger_mpi, ++ &dev_attr_BRM_status, ++ NULL, ++}; ++ ++/* device attributes */ ++ ++/** ++ * _ctl_device_sas_address_show - sas address ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is the sas address for the target ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_device_sas_address_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct scsi_device *sdev = to_scsi_device(dev); ++ struct MPT3SAS_DEVICE *sas_device_priv_data = sdev->hostdata; ++ ++ return snprintf(buf, PAGE_SIZE, "0x%016llx\n", ++ (unsigned long long)sas_device_priv_data->sas_target->sas_address); ++} ++static DEVICE_ATTR(sas_address, S_IRUGO, _ctl_device_sas_address_show, NULL); ++ ++/** ++ * _ctl_device_handle_show - device handle ++ * @cdev - pointer to embedded class device ++ * @buf - the buffer returned ++ * ++ * This is the firmware assigned device handle ++ * ++ * A sysfs 'read-only' shost attribute. ++ */ ++static ssize_t ++_ctl_device_handle_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct scsi_device *sdev = to_scsi_device(dev); ++ struct MPT3SAS_DEVICE *sas_device_priv_data = sdev->hostdata; ++ ++ return snprintf(buf, PAGE_SIZE, "0x%04x\n", ++ sas_device_priv_data->sas_target->handle); ++} ++static DEVICE_ATTR(sas_device_handle, S_IRUGO, _ctl_device_handle_show, NULL); ++ ++struct device_attribute *mpt2sas_dev_attrs[] = { ++ &dev_attr_sas_address, ++ &dev_attr_sas_device_handle, ++ NULL, ++}; ++ ++/* file operations table for mpt3ctl device */ ++static const struct file_operations ctl_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = _ctl_ioctl_mpt2sas, ++ .poll = _ctl_poll_mpt2sas, ++ .fasync = _ctl_fasync_mpt2sas, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = _ctl_ioctl_compat_mpt2sas, ++#endif ++}; ++ ++/* file operations table for mpt2ctl device */ ++static const struct file_operations ctl_gen2_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = _ctl_mpt2_ioctl_mpt2sas, ++ .poll = _ctl_poll_mpt2sas, ++ .fasync = _ctl_fasync_mpt2sas, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = _ctl_mpt2_ioctl_compat_mpt2sas, ++#endif ++}; ++ ++static struct miscdevice ctl_dev = { ++ .minor = MPT3SAS_MINOR, ++ .name = MPT3SAS_DEV_NAME, ++ .fops = &ctl_fops, ++}; ++ ++static struct miscdevice gen2_ctl_dev = { ++ .minor = MPT2SAS_MINOR, ++ .name = MPT2SAS_DEV_NAME, ++ .fops = &ctl_gen2_fops, ++}; ++ ++/** ++ * mpt2sas_ctl_init - main entry point for ctl. ++ * ++ */ ++void ++mpt2sas_ctl_init(ushort hbas_to_enumerate) ++{ ++ async_queue = NULL; ++ ++ /* Don't register mpt3ctl ioctl device if ++ * hbas_to_enumarate is one. ++ */ ++ if (hbas_to_enumerate != 1) ++ if (misc_register(&ctl_dev) < 0) ++ pr_err("%s can't register misc device [minor=%d]\n", ++ MPT3SAS_DRIVER_NAME, MPT3SAS_MINOR); ++ ++ /* Don't register mpt3ctl ioctl device if ++ * hbas_to_enumarate is two. ++ */ ++ if (hbas_to_enumerate != 2) ++ if (misc_register(&gen2_ctl_dev) < 0) ++ pr_err("%s can't register misc device [minor=%d]\n", ++ MPT2SAS_DRIVER_NAME, MPT2SAS_MINOR); ++ ++ init_waitqueue_head(&ctl_poll_wait); ++} ++ ++/** ++ * mpt2sas_ctl_exit - exit point for ctl ++ * ++ */ ++void ++mpt2sas_ctl_exit(ushort hbas_to_enumerate) ++{ ++ struct MPT3SAS_ADAPTER *ioc; ++ int i; ++ ++ list_for_each_entry(ioc, &mpt2sas_ioc_list, list) { ++ ++ /* free memory associated to diag buffers */ ++ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) { ++ if (!ioc->diag_buffer[i]) ++ continue; ++ if (!(ioc->diag_buffer_status[i] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED)) ++ continue; ++ if ((ioc->diag_buffer_status[i] & ++ MPT3_DIAG_BUFFER_IS_RELEASED)) ++ continue; ++ pci_free_consistent(ioc->pdev, ioc->diag_buffer_sz[i], ++ ioc->diag_buffer[i], ioc->diag_buffer_dma[i]); ++ ioc->diag_buffer[i] = NULL; ++ ioc->diag_buffer_status[i] = 0; ++ } ++ ++ kfree(ioc->event_log); ++ } ++ if (hbas_to_enumerate != 1) ++ misc_deregister(&ctl_dev); ++ if (hbas_to_enumerate != 2) ++ misc_deregister(&gen2_ctl_dev); ++} +diff --git a/drivers/scsi/mpt2sas/mpt3sas_ctl.h b/drivers/scsi/mpt2sas/mpt3sas_ctl.h +new file mode 100644 +index 0000000..8940835 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_ctl.h +@@ -0,0 +1,423 @@ ++/* ++ * Management Module Support for MPT (Message Passing Technology) based ++ * controllers ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_ctl.h ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#ifndef MPT3SAS_CTL_H_INCLUDED ++#define MPT3SAS_CTL_H_INCLUDED ++ ++#ifdef __KERNEL__ ++#include ++#endif ++ ++#ifndef MPT2SAS_MINOR ++#define MPT2SAS_MINOR (MPT_MINOR + 1) ++#endif ++#ifndef MPT3SAS_MINOR ++#define MPT3SAS_MINOR (MPT_MINOR + 2) ++#endif ++#define MPT2SAS_DEV_NAME "mpt2ctl" ++#define MPT3SAS_DEV_NAME "mpt3ctl" ++#define MPT3_MAGIC_NUMBER 'L' ++#define MPT3_IOCTL_DEFAULT_TIMEOUT (10) /* in seconds */ ++ ++/** ++ * IOCTL opcodes ++ */ ++#define MPT3IOCINFO _IOWR(MPT3_MAGIC_NUMBER, 17, \ ++ struct mpt3_ioctl_iocinfo) ++#define MPT3COMMAND _IOWR(MPT3_MAGIC_NUMBER, 20, \ ++ struct mpt3_ioctl_command) ++#ifdef CONFIG_COMPAT ++#define MPT3COMMAND32 _IOWR(MPT3_MAGIC_NUMBER, 20, \ ++ struct mpt3_ioctl_command32) ++#endif ++#define MPT3EVENTQUERY _IOWR(MPT3_MAGIC_NUMBER, 21, \ ++ struct mpt3_ioctl_eventquery) ++#define MPT3EVENTENABLE _IOWR(MPT3_MAGIC_NUMBER, 22, \ ++ struct mpt3_ioctl_eventenable) ++#define MPT3EVENTREPORT _IOWR(MPT3_MAGIC_NUMBER, 23, \ ++ struct mpt3_ioctl_eventreport) ++#define MPT3HARDRESET _IOWR(MPT3_MAGIC_NUMBER, 24, \ ++ struct mpt3_ioctl_diag_reset) ++#define MPT3BTDHMAPPING _IOWR(MPT3_MAGIC_NUMBER, 31, \ ++ struct mpt3_ioctl_btdh_mapping) ++ ++/* diag buffer support */ ++#define MPT3DIAGREGISTER _IOWR(MPT3_MAGIC_NUMBER, 26, \ ++ struct mpt3_diag_register) ++#define MPT3DIAGRELEASE _IOWR(MPT3_MAGIC_NUMBER, 27, \ ++ struct mpt3_diag_release) ++#define MPT3DIAGUNREGISTER _IOWR(MPT3_MAGIC_NUMBER, 28, \ ++ struct mpt3_diag_unregister) ++#define MPT3DIAGQUERY _IOWR(MPT3_MAGIC_NUMBER, 29, \ ++ struct mpt3_diag_query) ++#define MPT3DIAGREADBUFFER _IOWR(MPT3_MAGIC_NUMBER, 30, \ ++ struct mpt3_diag_read_buffer) ++ ++/** ++ * struct mpt3_ioctl_header - main header structure ++ * @ioc_number - IOC unit number ++ * @port_number - IOC port number ++ * @max_data_size - maximum number bytes to transfer on read ++ */ ++struct mpt3_ioctl_header { ++ uint32_t ioc_number; ++ uint32_t port_number; ++ uint32_t max_data_size; ++}; ++ ++/** ++ * struct mpt3_ioctl_diag_reset - diagnostic reset ++ * @hdr - generic header ++ */ ++struct mpt3_ioctl_diag_reset { ++ struct mpt3_ioctl_header hdr; ++}; ++ ++ ++/** ++ * struct mpt3_ioctl_pci_info - pci device info ++ * @device - pci device id ++ * @function - pci function id ++ * @bus - pci bus id ++ * @segment_id - pci segment id ++ */ ++struct mpt3_ioctl_pci_info { ++ union { ++ struct { ++ uint32_t device:5; ++ uint32_t function:3; ++ uint32_t bus:24; ++ } bits; ++ uint32_t word; ++ } u; ++ uint32_t segment_id; ++}; ++ ++ ++#define MPT2_IOCTL_INTERFACE_SCSI (0x00) ++#define MPT2_IOCTL_INTERFACE_FC (0x01) ++#define MPT2_IOCTL_INTERFACE_FC_IP (0x02) ++#define MPT2_IOCTL_INTERFACE_SAS (0x03) ++#define MPT2_IOCTL_INTERFACE_SAS2 (0x04) ++#define MPT2_IOCTL_INTERFACE_SAS2_SSS6200 (0x05) ++#define MPT3_IOCTL_INTERFACE_SAS3 (0x06) ++#define MPT2_IOCTL_VERSION_LENGTH (32) ++ ++/** ++ * struct mpt3_ioctl_iocinfo - generic controller info ++ * @hdr - generic header ++ * @adapter_type - type of adapter (spi, fc, sas) ++ * @port_number - port number ++ * @pci_id - PCI Id ++ * @hw_rev - hardware revision ++ * @sub_system_device - PCI subsystem Device ID ++ * @sub_system_vendor - PCI subsystem Vendor ID ++ * @rsvd0 - reserved ++ * @firmware_version - firmware version ++ * @bios_version - BIOS version ++ * @driver_version - driver version - 32 ASCII characters ++ * @rsvd1 - reserved ++ * @scsi_id - scsi id of adapter 0 ++ * @rsvd2 - reserved ++ * @pci_information - pci info (2nd revision) ++ */ ++struct mpt3_ioctl_iocinfo { ++ struct mpt3_ioctl_header hdr; ++ uint32_t adapter_type; ++ uint32_t port_number; ++ uint32_t pci_id; ++ uint32_t hw_rev; ++ uint32_t subsystem_device; ++ uint32_t subsystem_vendor; ++ uint32_t rsvd0; ++ uint32_t firmware_version; ++ uint32_t bios_version; ++ uint8_t driver_version[MPT2_IOCTL_VERSION_LENGTH]; ++ uint8_t rsvd1; ++ uint8_t scsi_id; ++ uint16_t rsvd2; ++ struct mpt3_ioctl_pci_info pci_information; ++}; ++ ++ ++/* number of event log entries */ ++#define MPT3SAS_CTL_EVENT_LOG_SIZE (50) ++ ++/** ++ * struct mpt3_ioctl_eventquery - query event count and type ++ * @hdr - generic header ++ * @event_entries - number of events returned by get_event_report ++ * @rsvd - reserved ++ * @event_types - type of events currently being captured ++ */ ++struct mpt3_ioctl_eventquery { ++ struct mpt3_ioctl_header hdr; ++ uint16_t event_entries; ++ uint16_t rsvd; ++ uint32_t event_types[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; ++}; ++ ++/** ++ * struct mpt3_ioctl_eventenable - enable/disable event capturing ++ * @hdr - generic header ++ * @event_types - toggle off/on type of events to be captured ++ */ ++struct mpt3_ioctl_eventenable { ++ struct mpt3_ioctl_header hdr; ++ uint32_t event_types[4]; ++}; ++ ++#define MPT3_EVENT_DATA_SIZE (192) ++/** ++ * struct MPT3_IOCTL_EVENTS - ++ * @event - the event that was reported ++ * @context - unique value for each event assigned by driver ++ * @data - event data returned in fw reply message ++ */ ++struct MPT3_IOCTL_EVENTS { ++ uint32_t event; ++ uint32_t context; ++ uint8_t data[MPT3_EVENT_DATA_SIZE]; ++}; ++ ++/** ++ * struct mpt3_ioctl_eventreport - returing event log ++ * @hdr - generic header ++ * @event_data - (see struct MPT3_IOCTL_EVENTS) ++ */ ++struct mpt3_ioctl_eventreport { ++ struct mpt3_ioctl_header hdr; ++ struct MPT3_IOCTL_EVENTS event_data[1]; ++}; ++ ++/** ++ * struct mpt3_ioctl_command - generic mpt firmware passthru ioctl ++ * @hdr - generic header ++ * @timeout - command timeout in seconds. (if zero then use driver default ++ * value). ++ * @reply_frame_buf_ptr - reply location ++ * @data_in_buf_ptr - destination for read ++ * @data_out_buf_ptr - data source for write ++ * @sense_data_ptr - sense data location ++ * @max_reply_bytes - maximum number of reply bytes to be sent to app. ++ * @data_in_size - number bytes for data transfer in (read) ++ * @data_out_size - number bytes for data transfer out (write) ++ * @max_sense_bytes - maximum number of bytes for auto sense buffers ++ * @data_sge_offset - offset in words from the start of the request message to ++ * the first SGL ++ * @mf[1]; ++ */ ++struct mpt3_ioctl_command { ++ struct mpt3_ioctl_header hdr; ++ uint32_t timeout; ++ void __user *reply_frame_buf_ptr; ++ void __user *data_in_buf_ptr; ++ void __user *data_out_buf_ptr; ++ void __user *sense_data_ptr; ++ uint32_t max_reply_bytes; ++ uint32_t data_in_size; ++ uint32_t data_out_size; ++ uint32_t max_sense_bytes; ++ uint32_t data_sge_offset; ++ uint8_t mf[1]; ++}; ++ ++#ifdef CONFIG_COMPAT ++struct mpt3_ioctl_command32 { ++ struct mpt3_ioctl_header hdr; ++ uint32_t timeout; ++ uint32_t reply_frame_buf_ptr; ++ uint32_t data_in_buf_ptr; ++ uint32_t data_out_buf_ptr; ++ uint32_t sense_data_ptr; ++ uint32_t max_reply_bytes; ++ uint32_t data_in_size; ++ uint32_t data_out_size; ++ uint32_t max_sense_bytes; ++ uint32_t data_sge_offset; ++ uint8_t mf[1]; ++}; ++#endif ++ ++/** ++ * struct mpt3_ioctl_btdh_mapping - mapping info ++ * @hdr - generic header ++ * @id - target device identification number ++ * @bus - SCSI bus number that the target device exists on ++ * @handle - device handle for the target device ++ * @rsvd - reserved ++ * ++ * To obtain a bus/id the application sets ++ * handle to valid handle, and bus/id to 0xFFFF. ++ * ++ * To obtain the device handle the application sets ++ * bus/id valid value, and the handle to 0xFFFF. ++ */ ++struct mpt3_ioctl_btdh_mapping { ++ struct mpt3_ioctl_header hdr; ++ uint32_t id; ++ uint32_t bus; ++ uint16_t handle; ++ uint16_t rsvd; ++}; ++ ++ ++ ++/* application flags for mpt3_diag_register, mpt3_diag_query */ ++#define MPT3_APP_FLAGS_APP_OWNED (0x0001) ++#define MPT3_APP_FLAGS_BUFFER_VALID (0x0002) ++#define MPT3_APP_FLAGS_FW_BUFFER_ACCESS (0x0004) ++ ++/* flags for mpt3_diag_read_buffer */ ++#define MPT3_FLAGS_REREGISTER (0x0001) ++ ++#define MPT3_PRODUCT_SPECIFIC_DWORDS 23 ++ ++/** ++ * struct mpt3_diag_register - application register with driver ++ * @hdr - generic header ++ * @reserved - ++ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED ++ * @application_flags - misc flags ++ * @diagnostic_flags - specifies flags affecting command processing ++ * @product_specific - product specific information ++ * @requested_buffer_size - buffers size in bytes ++ * @unique_id - tag specified by application that is used to signal ownership ++ * of the buffer. ++ * ++ * This will allow the driver to setup any required buffers that will be ++ * needed by firmware to communicate with the driver. ++ */ ++struct mpt3_diag_register { ++ struct mpt3_ioctl_header hdr; ++ uint8_t reserved; ++ uint8_t buffer_type; ++ uint16_t application_flags; ++ uint32_t diagnostic_flags; ++ uint32_t product_specific[MPT3_PRODUCT_SPECIFIC_DWORDS]; ++ uint32_t requested_buffer_size; ++ uint32_t unique_id; ++}; ++ ++/** ++ * struct mpt3_diag_unregister - application unregister with driver ++ * @hdr - generic header ++ * @unique_id - tag uniquely identifies the buffer to be unregistered ++ * ++ * This will allow the driver to cleanup any memory allocated for diag ++ * messages and to free up any resources. ++ */ ++struct mpt3_diag_unregister { ++ struct mpt3_ioctl_header hdr; ++ uint32_t unique_id; ++}; ++ ++/** ++ * struct mpt3_diag_query - query relevant info associated with diag buffers ++ * @hdr - generic header ++ * @reserved - ++ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED ++ * @application_flags - misc flags ++ * @diagnostic_flags - specifies flags affecting command processing ++ * @product_specific - product specific information ++ * @total_buffer_size - diag buffer size in bytes ++ * @driver_added_buffer_size - size of extra space appended to end of buffer ++ * @unique_id - unique id associated with this buffer. ++ * ++ * The application will send only buffer_type and unique_id. Driver will ++ * inspect unique_id first, if valid, fill in all the info. If unique_id is ++ * 0x00, the driver will return info specified by Buffer Type. ++ */ ++struct mpt3_diag_query { ++ struct mpt3_ioctl_header hdr; ++ uint8_t reserved; ++ uint8_t buffer_type; ++ uint16_t application_flags; ++ uint32_t diagnostic_flags; ++ uint32_t product_specific[MPT3_PRODUCT_SPECIFIC_DWORDS]; ++ uint32_t total_buffer_size; ++ uint32_t driver_added_buffer_size; ++ uint32_t unique_id; ++}; ++ ++/** ++ * struct mpt3_diag_release - request to send Diag Release Message to firmware ++ * @hdr - generic header ++ * @unique_id - tag uniquely identifies the buffer to be released ++ * ++ * This allows ownership of the specified buffer to returned to the driver, ++ * allowing an application to read the buffer without fear that firmware is ++ * overwritting information in the buffer. ++ */ ++struct mpt3_diag_release { ++ struct mpt3_ioctl_header hdr; ++ uint32_t unique_id; ++}; ++ ++/** ++ * struct mpt3_diag_read_buffer - request for copy of the diag buffer ++ * @hdr - generic header ++ * @status - ++ * @reserved - ++ * @flags - misc flags ++ * @starting_offset - starting offset within drivers buffer where to start ++ * reading data at into the specified application buffer ++ * @bytes_to_read - number of bytes to copy from the drivers buffer into the ++ * application buffer starting at starting_offset. ++ * @unique_id - unique id associated with this buffer. ++ * @diagnostic_data - data payload ++ */ ++struct mpt3_diag_read_buffer { ++ struct mpt3_ioctl_header hdr; ++ uint8_t status; ++ uint8_t reserved; ++ uint16_t flags; ++ uint32_t starting_offset; ++ uint32_t bytes_to_read; ++ uint32_t unique_id; ++ uint32_t diagnostic_data[1]; ++}; ++ ++#endif /* MPT3SAS_CTL_H_INCLUDED */ +diff --git a/drivers/scsi/mpt2sas/mpt3sas_debug.h b/drivers/scsi/mpt2sas/mpt3sas_debug.h +new file mode 100644 +index 0000000..cceeb2c +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_debug.h +@@ -0,0 +1,206 @@ ++/* ++ * Logging Support for MPT (Message Passing Technology) based controllers ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_debug.c ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#ifndef MPT3SAS_DEBUG_H_INCLUDED ++#define MPT3SAS_DEBUG_H_INCLUDED ++ ++#define MPT_DEBUG 0x00000001 ++#define MPT_DEBUG_MSG_FRAME 0x00000002 ++#define MPT_DEBUG_SG 0x00000004 ++#define MPT_DEBUG_EVENTS 0x00000008 ++#define MPT_DEBUG_EVENT_WORK_TASK 0x00000010 ++#define MPT_DEBUG_INIT 0x00000020 ++#define MPT_DEBUG_EXIT 0x00000040 ++#define MPT_DEBUG_FAIL 0x00000080 ++#define MPT_DEBUG_TM 0x00000100 ++#define MPT_DEBUG_REPLY 0x00000200 ++#define MPT_DEBUG_HANDSHAKE 0x00000400 ++#define MPT_DEBUG_CONFIG 0x00000800 ++#define MPT_DEBUG_DL 0x00001000 ++#define MPT_DEBUG_RESET 0x00002000 ++#define MPT_DEBUG_SCSI 0x00004000 ++#define MPT_DEBUG_IOCTL 0x00008000 ++#define MPT_DEBUG_SAS 0x00020000 ++#define MPT_DEBUG_TRANSPORT 0x00040000 ++#define MPT_DEBUG_TASK_SET_FULL 0x00080000 ++ ++#define MPT_DEBUG_TRIGGER_DIAG 0x00200000 ++ ++ ++#define MPT_CHECK_LOGGING(IOC, CMD, BITS) \ ++{ \ ++ if (IOC->logging_level & BITS) \ ++ CMD; \ ++} ++ ++/* ++ * debug macros ++ */ ++ ++#define dprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG) ++ ++#define dsgprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SG) ++ ++#define devtprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EVENTS) ++ ++#define dewtprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EVENT_WORK_TASK) ++ ++#define dinitprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_INIT) ++ ++#define dexitprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EXIT) ++ ++#define dfailprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_FAIL) ++ ++#define dtmprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TM) ++ ++#define dreplyprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_REPLY) ++ ++#define dhsprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_HANDSHAKE) ++ ++#define dcprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_CONFIG) ++ ++#define ddlprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_DL) ++ ++#define drsprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_RESET) ++ ++#define dsprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SCSI) ++ ++#define dctlprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_IOCTL) ++ ++#define dsasprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SAS) ++ ++#define dsastransport(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SAS_WIDE) ++ ++#define dmfprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_MSG_FRAME) ++ ++#define dtsfprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TASK_SET_FULL) ++ ++#define dtransportprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TRANSPORT) ++ ++#define dTriggerDiagPrintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TRIGGER_DIAG) ++ ++ ++ ++/* inline functions for dumping debug data*/ ++ ++/** ++ * _debug_dump_mf - print message frame contents ++ * @mpi_request: pointer to message frame ++ * @sz: number of dwords ++ */ ++static inline void ++_debug_dump_mf(void *mpi_request, int sz) ++{ ++ int i; ++ __le32 *mfp = (__le32 *)mpi_request; ++ ++ pr_info("mf:\n\t"); ++ for (i = 0; i < sz; i++) { ++ if (i && ((i % 8) == 0)) ++ pr_info("\n\t"); ++ pr_info("%08x ", le32_to_cpu(mfp[i])); ++ } ++ pr_info("\n"); ++} ++/** ++ * _debug_dump_reply - print message frame contents ++ * @mpi_request: pointer to message frame ++ * @sz: number of dwords ++ */ ++static inline void ++_debug_dump_reply(void *mpi_request, int sz) ++{ ++ int i; ++ __le32 *mfp = (__le32 *)mpi_request; ++ ++ pr_info("reply:\n\t"); ++ for (i = 0; i < sz; i++) { ++ if (i && ((i % 8) == 0)) ++ pr_info("\n\t"); ++ pr_info("%08x ", le32_to_cpu(mfp[i])); ++ } ++ pr_info("\n"); ++} ++/** ++ * _debug_dump_config - print config page contents ++ * @mpi_request: pointer to message frame ++ * @sz: number of dwords ++ */ ++static inline void ++_debug_dump_config(void *mpi_request, int sz) ++{ ++ int i; ++ __le32 *mfp = (__le32 *)mpi_request; ++ ++ pr_info("config:\n\t"); ++ for (i = 0; i < sz; i++) { ++ if (i && ((i % 8) == 0)) ++ pr_info("\n\t"); ++ pr_info("%08x ", le32_to_cpu(mfp[i])); ++ } ++ pr_info("\n"); ++} ++ ++#endif /* MPT3SAS_DEBUG_H_INCLUDED */ +diff --git a/drivers/scsi/mpt2sas/mpt3sas_scsih.c b/drivers/scsi/mpt2sas/mpt3sas_scsih.c +new file mode 100644 +index 0000000..0a3ad2b +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_scsih.c +@@ -0,0 +1,9356 @@ ++/* ++ * Scsi Host Layer for MPT (Message Passing Technology) based controllers ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_scsih.c ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mpt3sas_base.h" ++ ++#define RAID_CHANNEL 1 ++/* forward proto's */ ++static void _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_node *sas_expander); ++static void _firmware_event_work(struct work_struct *work); ++ ++static void _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_device *sas_device); ++static int _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, ++ u8 retry_count, u8 is_pd); ++ ++static u8 _scsih_check_for_pending_tm(struct MPT3SAS_ADAPTER *ioc, u16 smid); ++ ++/* global parameters */ ++LIST_HEAD(mpt2sas_ioc_list); ++/* global ioc lock for list operations */ ++DEFINE_SPINLOCK(gioc_lock_mpt2sas); ++ ++MODULE_AUTHOR(MPT3SAS_AUTHOR); ++#ifdef MPT2SAS_SCSI ++MODULE_DESCRIPTION(MPT2SAS_DESCRIPTION); ++MODULE_VERSION(MPT2SAS_DRIVER_VERSION); ++#else ++MODULE_DESCRIPTION(MPT3SAS_DESCRIPTION); ++MODULE_VERSION(MPT3SAS_DRIVER_VERSION); ++#endif /* MPT2SAS_SCSI */ ++MODULE_LICENSE("GPL"); ++ ++/* local parameters */ ++static u8 scsi_io_cb_idx = -1; ++static u8 tm_cb_idx = -1; ++static u8 ctl_cb_idx = -1; ++static u8 base_cb_idx = -1; ++static u8 port_enable_cb_idx = -1; ++static u8 transport_cb_idx = -1; ++static u8 scsih_cb_idx = -1; ++static u8 config_cb_idx = -1; ++static int mpt2_ids; ++static int mpt3_ids; ++ ++static u8 tm_tr_cb_idx = -1 ; ++static u8 tm_tr_volume_cb_idx = -1 ; ++static u8 tm_sas_control_cb_idx = -1; ++ ++/* command line options */ ++static u32 logging_level; ++MODULE_PARM_DESC(logging_level, ++ " bits for enabling additional logging info (default=0)"); ++ ++ ++static ushort max_sectors = 0xFFFF; ++module_param(max_sectors, ushort, 0); ++MODULE_PARM_DESC(max_sectors, "max sectors, range 64 to 32767 default=32767"); ++ ++ ++static int missing_delay[2] = {-1, -1}; ++module_param_array(missing_delay, int, NULL, 0); ++MODULE_PARM_DESC(missing_delay, " device missing delay , io missing delay"); ++ ++/* scsi-mid layer global parmeter is max_report_luns, which is 511 */ ++#define MPT3SAS_MAX_LUN (16895) ++static int max_lun = MPT3SAS_MAX_LUN; ++module_param(max_lun, int, 0); ++MODULE_PARM_DESC(max_lun, " max lun, default=16895 "); ++ ++/* diag_buffer_enable is bitwise ++ * bit 0 set = TRACE ++ * bit 1 set = SNAPSHOT ++ * bit 2 set = EXTENDED ++ * ++ * Either bit can be set, or both ++ */ ++static int diag_buffer_enable = -1; ++module_param(diag_buffer_enable, int, 0); ++MODULE_PARM_DESC(diag_buffer_enable, ++ " post diag buffers (TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)"); ++static int disable_discovery = -1; ++module_param(disable_discovery, int, 0); ++MODULE_PARM_DESC(disable_discovery, " disable discovery "); ++ ++ ++/* permit overriding the host protection capabilities mask (EEDP/T10 PI) */ ++static int prot_mask = -1; ++module_param(prot_mask, int, 0); ++MODULE_PARM_DESC(prot_mask, " host protection capabilities mask, def=7 "); ++ ++ ++/* raid transport support */ ++struct raid_template *mpt3sas_raid_template_mpt2sas; ++struct raid_template *mpt2sas_raid_template_mpt2sas; ++ ++ ++/** ++ * struct sense_info - common structure for obtaining sense keys ++ * @skey: sense key ++ * @asc: additional sense code ++ * @ascq: additional sense code qualifier ++ */ ++struct sense_info { ++ u8 skey; ++ u8 asc; ++ u8 ascq; ++}; ++ ++#define MPT3SAS_PROCESS_TRIGGER_DIAG (0xFFFB) ++#define MPT3SAS_TURN_ON_PFA_LED (0xFFFC) ++#define MPT3SAS_PORT_ENABLE_COMPLETE (0xFFFD) ++#define MPT3SAS_ABRT_TASK_SET (0xFFFE) ++#define MPT3SAS_REMOVE_UNRESPONDING_DEVICES (0xFFFF) ++/** ++ * struct fw_event_work - firmware event struct ++ * @list: link list framework ++ * @work: work object (ioc->fault_reset_work_q) ++ * @ioc: per adapter object ++ * @device_handle: device handle ++ * @VF_ID: virtual function id ++ * @VP_ID: virtual port id ++ * @ignore: flag meaning this event has been marked to ignore ++ * @event: firmware event MPI2_EVENT_XXX defined in mpi2_ioc.h ++ * @refcount: kref for this event ++ * @event_data: reply event data payload follows ++ * ++ * This object stored on ioc->fw_event_list. ++ */ ++struct fw_event_work { ++ struct list_head list; ++ struct work_struct work; ++ ++ struct MPT3SAS_ADAPTER *ioc; ++ u16 device_handle; ++ u8 VF_ID; ++ u8 VP_ID; ++ u8 ignore; ++ u16 event; ++ struct kref refcount; ++ char event_data[0] __aligned(4); ++}; ++ ++static void fw_event_work_free(struct kref *r) ++{ ++ kfree(container_of(r, struct fw_event_work, refcount)); ++} ++ ++static void fw_event_work_get(struct fw_event_work *fw_work) ++{ ++ kref_get(&fw_work->refcount); ++} ++ ++static void fw_event_work_put(struct fw_event_work *fw_work) ++{ ++ kref_put(&fw_work->refcount, fw_event_work_free); ++} ++ ++static struct fw_event_work *alloc_fw_event_work(int len) ++{ ++ struct fw_event_work *fw_event; ++ ++ fw_event = kzalloc(sizeof(*fw_event) + len, GFP_ATOMIC); ++ if (!fw_event) ++ return NULL; ++ ++ kref_init(&fw_event->refcount); ++ return fw_event; ++} ++ ++/** ++ * struct _scsi_io_transfer - scsi io transfer ++ * @handle: sas device handle (assigned by firmware) ++ * @is_raid: flag set for hidden raid components ++ * @dir: DMA_TO_DEVICE, DMA_FROM_DEVICE, ++ * @data_length: data transfer length ++ * @data_dma: dma pointer to data ++ * @sense: sense data ++ * @lun: lun number ++ * @cdb_length: cdb length ++ * @cdb: cdb contents ++ * @timeout: timeout for this command ++ * @VF_ID: virtual function id ++ * @VP_ID: virtual port id ++ * @valid_reply: flag set for reply message ++ * @sense_length: sense length ++ * @ioc_status: ioc status ++ * @scsi_state: scsi state ++ * @scsi_status: scsi staus ++ * @log_info: log information ++ * @transfer_length: data length transfer when there is a reply message ++ * ++ * Used for sending internal scsi commands to devices within this module. ++ * Refer to _scsi_send_scsi_io(). ++ */ ++struct _scsi_io_transfer { ++ u16 handle; ++ u8 is_raid; ++ enum dma_data_direction dir; ++ u32 data_length; ++ dma_addr_t data_dma; ++ u8 sense[SCSI_SENSE_BUFFERSIZE]; ++ u32 lun; ++ u8 cdb_length; ++ u8 cdb[32]; ++ u8 timeout; ++ u8 VF_ID; ++ u8 VP_ID; ++ u8 valid_reply; ++ /* the following bits are only valid when 'valid_reply = 1' */ ++ u32 sense_length; ++ u16 ioc_status; ++ u8 scsi_state; ++ u8 scsi_status; ++ u32 log_info; ++ u32 transfer_length; ++}; ++ ++/** ++ * _scsih_set_debug_level - global setting of ioc->logging_level. ++ * ++ * Note: The logging levels are defined in mpt3sas_debug.h. ++ */ ++static int ++_scsih_set_debug_level(const char *val, struct kernel_param *kp) ++{ ++ int ret = param_set_int(val, kp); ++ struct MPT3SAS_ADAPTER *ioc; ++ ++ if (ret) ++ return ret; ++ ++ pr_info("setting logging_level(0x%08x)\n", logging_level); ++ spin_lock(&gioc_lock_mpt2sas); ++ list_for_each_entry(ioc, &mpt2sas_ioc_list, list) ++ ioc->logging_level = logging_level; ++ spin_unlock(&gioc_lock_mpt2sas); ++ return 0; ++} ++module_param_call(logging_level, _scsih_set_debug_level, param_get_int, ++ &logging_level, 0644); ++ ++/** ++ * _scsih_srch_boot_sas_address - search based on sas_address ++ * @sas_address: sas address ++ * @boot_device: boot device object from bios page 2 ++ * ++ * Returns 1 when there's a match, 0 means no match. ++ */ ++static inline int ++_scsih_srch_boot_sas_address(u64 sas_address, ++ Mpi2BootDeviceSasWwid_t *boot_device) ++{ ++ return (sas_address == le64_to_cpu(boot_device->SASAddress)) ? 1 : 0; ++} ++ ++/** ++ * _scsih_srch_boot_device_name - search based on device name ++ * @device_name: device name specified in INDENTIFY fram ++ * @boot_device: boot device object from bios page 2 ++ * ++ * Returns 1 when there's a match, 0 means no match. ++ */ ++static inline int ++_scsih_srch_boot_device_name(u64 device_name, ++ Mpi2BootDeviceDeviceName_t *boot_device) ++{ ++ return (device_name == le64_to_cpu(boot_device->DeviceName)) ? 1 : 0; ++} ++ ++/** ++ * _scsih_srch_boot_encl_slot - search based on enclosure_logical_id/slot ++ * @enclosure_logical_id: enclosure logical id ++ * @slot_number: slot number ++ * @boot_device: boot device object from bios page 2 ++ * ++ * Returns 1 when there's a match, 0 means no match. ++ */ ++static inline int ++_scsih_srch_boot_encl_slot(u64 enclosure_logical_id, u16 slot_number, ++ Mpi2BootDeviceEnclosureSlot_t *boot_device) ++{ ++ return (enclosure_logical_id == le64_to_cpu(boot_device-> ++ EnclosureLogicalID) && slot_number == le16_to_cpu(boot_device-> ++ SlotNumber)) ? 1 : 0; ++} ++ ++/** ++ * _scsih_is_boot_device - search for matching boot device. ++ * @sas_address: sas address ++ * @device_name: device name specified in INDENTIFY fram ++ * @enclosure_logical_id: enclosure logical id ++ * @slot_number: slot number ++ * @form: specifies boot device form ++ * @boot_device: boot device object from bios page 2 ++ * ++ * Returns 1 when there's a match, 0 means no match. ++ */ ++static int ++_scsih_is_boot_device(u64 sas_address, u64 device_name, ++ u64 enclosure_logical_id, u16 slot, u8 form, ++ Mpi2BiosPage2BootDevice_t *boot_device) ++{ ++ int rc = 0; ++ ++ switch (form) { ++ case MPI2_BIOSPAGE2_FORM_SAS_WWID: ++ if (!sas_address) ++ break; ++ rc = _scsih_srch_boot_sas_address( ++ sas_address, &boot_device->SasWwid); ++ break; ++ case MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT: ++ if (!enclosure_logical_id) ++ break; ++ rc = _scsih_srch_boot_encl_slot( ++ enclosure_logical_id, ++ slot, &boot_device->EnclosureSlot); ++ break; ++ case MPI2_BIOSPAGE2_FORM_DEVICE_NAME: ++ if (!device_name) ++ break; ++ rc = _scsih_srch_boot_device_name( ++ device_name, &boot_device->DeviceName); ++ break; ++ case MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED: ++ break; ++ } ++ ++ return rc; ++} ++ ++/** ++ * _scsih_get_sas_address - set the sas_address for given device handle ++ * @handle: device handle ++ * @sas_address: sas address ++ * ++ * Returns 0 success, non-zero when failure ++ */ ++static int ++_scsih_get_sas_address(struct MPT3SAS_ADAPTER *ioc, u16 handle, ++ u64 *sas_address) ++{ ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u32 ioc_status; ++ ++ *sas_address = 0; ++ ++ if (handle <= ioc->sas_hba.num_phys) { ++ *sas_address = ioc->sas_hba.sas_address; ++ return 0; ++ } ++ ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, ++ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__); ++ return -ENXIO; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; ++ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ++ *sas_address = le64_to_cpu(sas_device_pg0.SASAddress); ++ return 0; ++ } ++ ++ /* we hit this becuase the given parent handle doesn't exist */ ++ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) ++ return -ENXIO; ++ ++ /* else error case */ ++ pr_err(MPT3SAS_FMT ++ "handle(0x%04x), ioc_status(0x%04x), failure at %s:%d/%s()!\n", ++ ioc->name, handle, ioc_status, ++ __FILE__, __LINE__, __func__); ++ return -EIO; ++} ++ ++/** ++ * _scsih_determine_boot_device - determine boot device. ++ * @ioc: per adapter object ++ * @device: either sas_device or raid_device object ++ * @is_raid: [flag] 1 = raid object, 0 = sas object ++ * ++ * Determines whether this device should be first reported device to ++ * to scsi-ml or sas transport, this purpose is for persistent boot device. ++ * There are primary, alternate, and current entries in bios page 2. The order ++ * priority is primary, alternate, then current. This routine saves ++ * the corresponding device object and is_raid flag in the ioc object. ++ * The saved data to be used later in _scsih_probe_boot_devices(). ++ */ ++static void ++_scsih_determine_boot_device(struct MPT3SAS_ADAPTER *ioc, ++ void *device, u8 is_raid) ++{ ++ struct _sas_device *sas_device; ++ struct _raid_device *raid_device; ++ u64 sas_address; ++ u64 device_name; ++ u64 enclosure_logical_id; ++ u16 slot; ++ ++ /* only process this function when driver loads */ ++ if (!ioc->is_driver_loading) ++ return; ++ ++ /* no Bios, return immediately */ ++ if (!ioc->bios_pg3.BiosVersion) ++ return; ++ ++ if (!is_raid) { ++ sas_device = device; ++ sas_address = sas_device->sas_address; ++ device_name = sas_device->device_name; ++ enclosure_logical_id = sas_device->enclosure_logical_id; ++ slot = sas_device->slot; ++ } else { ++ raid_device = device; ++ sas_address = raid_device->wwid; ++ device_name = 0; ++ enclosure_logical_id = 0; ++ slot = 0; ++ } ++ ++ if (!ioc->req_boot_device.device) { ++ if (_scsih_is_boot_device(sas_address, device_name, ++ enclosure_logical_id, slot, ++ (ioc->bios_pg2.ReqBootDeviceForm & ++ MPI2_BIOSPAGE2_FORM_MASK), ++ &ioc->bios_pg2.RequestedBootDevice)) { ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: req_boot_device(0x%016llx)\n", ++ ioc->name, __func__, ++ (unsigned long long)sas_address)); ++ ioc->req_boot_device.device = device; ++ ioc->req_boot_device.is_raid = is_raid; ++ } ++ } ++ ++ if (!ioc->req_alt_boot_device.device) { ++ if (_scsih_is_boot_device(sas_address, device_name, ++ enclosure_logical_id, slot, ++ (ioc->bios_pg2.ReqAltBootDeviceForm & ++ MPI2_BIOSPAGE2_FORM_MASK), ++ &ioc->bios_pg2.RequestedAltBootDevice)) { ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: req_alt_boot_device(0x%016llx)\n", ++ ioc->name, __func__, ++ (unsigned long long)sas_address)); ++ ioc->req_alt_boot_device.device = device; ++ ioc->req_alt_boot_device.is_raid = is_raid; ++ } ++ } ++ ++ if (!ioc->current_boot_device.device) { ++ if (_scsih_is_boot_device(sas_address, device_name, ++ enclosure_logical_id, slot, ++ (ioc->bios_pg2.CurrentBootDeviceForm & ++ MPI2_BIOSPAGE2_FORM_MASK), ++ &ioc->bios_pg2.CurrentBootDevice)) { ++ dinitprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: current_boot_device(0x%016llx)\n", ++ ioc->name, __func__, ++ (unsigned long long)sas_address)); ++ ioc->current_boot_device.device = device; ++ ioc->current_boot_device.is_raid = is_raid; ++ } ++ } ++} ++ ++static struct _sas_device * ++__mpt2sas_get_sdev_from_target(struct MPT3SAS_ADAPTER *ioc, ++ struct MPT3SAS_TARGET *tgt_priv) ++{ ++ struct _sas_device *ret; ++ ++ assert_spin_locked(&ioc->sas_device_lock); ++ ++ ret = tgt_priv->sdev; ++ if (ret) ++ sas_device_get(ret); ++ ++ return ret; ++} ++ ++static struct _sas_device * ++mpt2sas_get_sdev_from_target(struct MPT3SAS_ADAPTER *ioc, ++ struct MPT3SAS_TARGET *tgt_priv) ++{ ++ struct _sas_device *ret; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ ret = __mpt2sas_get_sdev_from_target(ioc, tgt_priv); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ return ret; ++} ++ ++ ++struct _sas_device * ++__mpt2sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address) ++{ ++ struct _sas_device *sas_device; ++ ++ assert_spin_locked(&ioc->sas_device_lock); ++ ++ list_for_each_entry(sas_device, &ioc->sas_device_list, list) ++ if (sas_device->sas_address == sas_address) ++ goto found_device; ++ ++ list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) ++ if (sas_device->sas_address == sas_address) ++ goto found_device; ++ ++ return NULL; ++ ++found_device: ++ sas_device_get(sas_device); ++ return sas_device; ++} ++ ++/** ++ * mpt2sas_get_sdev_by_addr - sas device search ++ * @ioc: per adapter object ++ * @sas_address: sas address ++ * Context: Calling function should acquire ioc->sas_device_lock ++ * ++ * This searches for sas_device based on sas_address, then return sas_device ++ * object. ++ */ ++struct _sas_device * ++mpt2sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address) ++{ ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ sas_address); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ return sas_device; ++} ++ ++static struct _sas_device * ++__mpt2sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _sas_device *sas_device; ++ ++ assert_spin_locked(&ioc->sas_device_lock); ++ ++ list_for_each_entry(sas_device, &ioc->sas_device_list, list) ++ if (sas_device->handle == handle) ++ goto found_device; ++ ++ list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) ++ if (sas_device->handle == handle) ++ goto found_device; ++ ++ return NULL; ++ ++found_device: ++ sas_device_get(sas_device); ++ return sas_device; ++} ++ ++/** ++ * mpt2sas_get_sdev_by_handle - sas device search ++ * @ioc: per adapter object ++ * @handle: sas device handle (assigned by firmware) ++ * Context: Calling function should acquire ioc->sas_device_lock ++ * ++ * This searches for sas_device based on sas_address, then return sas_device ++ * object. ++ */ ++static struct _sas_device * ++mpt2sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ return sas_device; ++} ++ ++/** ++ * _scsih_sas_device_remove - remove sas_device from list. ++ * @ioc: per adapter object ++ * @sas_device: the sas_device object ++ * Context: This function will acquire ioc->sas_device_lock. ++ * ++ * If sas_device is on the list, remove it and decrement its reference count. ++ */ ++static void ++_scsih_sas_device_remove(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_device *sas_device) ++{ ++ unsigned long flags; ++ ++ if (!sas_device) ++ return; ++ pr_info(MPT3SAS_FMT ++ "removing handle(0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, sas_device->handle, ++ (unsigned long long) sas_device->sas_address); ++ ++ if (sas_device->enclosure_handle != 0) ++ pr_info(MPT3SAS_FMT ++ "removing enclosure logical id(0x%016llx), slot(%d)\n", ++ ioc->name, (unsigned long long) ++ sas_device->enclosure_logical_id, sas_device->slot); ++ ++ if (sas_device->connector_name[0] != '\0') ++ pr_info(MPT3SAS_FMT ++ "removing enclosure level(0x%04x), connector name( %s)\n", ++ ioc->name, sas_device->enclosure_level, ++ sas_device->connector_name); ++ ++ /* ++ * The lock serializes access to the list, but we still need to verify ++ * that nobody removed the entry while we were waiting on the lock. ++ */ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ if (!list_empty(&sas_device->list)) { ++ list_del_init(&sas_device->list); ++ sas_device_put(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++} ++ ++/** ++ * _scsih_device_remove_by_handle - removing device object by handle ++ * @ioc: per adapter object ++ * @handle: device handle ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ if (ioc->shost_recovery) ++ return; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (sas_device) { ++ list_del_init(&sas_device->list); ++ sas_device_put(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ if (sas_device) { ++ _scsih_remove_device(ioc, sas_device); ++ sas_device_put(sas_device); ++ } ++} ++ ++/** ++ * mpt2sas_device_remove_by_sas_address - removing device object by sas address ++ * @ioc: per adapter object ++ * @sas_address: device sas_address ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_device_remove_by_sas_address(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address) ++{ ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ if (ioc->shost_recovery) ++ return; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, sas_address); ++ if (sas_device) { ++ list_del_init(&sas_device->list); ++ sas_device_put(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ if (sas_device) { ++ _scsih_remove_device(ioc, sas_device); ++ sas_device_put(sas_device); ++ } ++} ++ ++/** ++ * _scsih_sas_device_add - insert sas_device to the list. ++ * @ioc: per adapter object ++ * @sas_device: the sas_device object ++ * Context: This function will acquire ioc->sas_device_lock. ++ * ++ * Adding new object to the ioc->sas_device_list. ++ */ ++static void ++_scsih_sas_device_add(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_device *sas_device) ++{ ++ unsigned long flags; ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: handle(0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, __func__, sas_device->handle, ++ (unsigned long long)sas_device->sas_address)); ++ ++ if (sas_device->enclosure_handle != 0) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enclosure logical id(0x%016llx), slot( %d)\n", ++ ioc->name, __func__, (unsigned long long) ++ sas_device->enclosure_logical_id, sas_device->slot)); ++ ++ if (sas_device->connector_name[0] != '\0') ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enclosure level(0x%04x), connector name( %s)\n", ++ ioc->name, __func__, ++ sas_device->enclosure_level, sas_device->connector_name)); ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device_get(sas_device); ++ list_add_tail(&sas_device->list, &ioc->sas_device_list); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ if (!mpt2sas_transport_port_add(ioc, sas_device->handle, ++ sas_device->sas_address_parent)) { ++ _scsih_sas_device_remove(ioc, sas_device); ++ } else if (!sas_device->starget) { ++ /* ++ * When asyn scanning is enabled, its not possible to remove ++ * devices while scanning is turned on due to an oops in ++ * scsi_sysfs_add_sdev()->add_device()->sysfs_addrm_start() ++ */ ++ if (!ioc->is_driver_loading) { ++ mpt2sas_transport_port_remove(ioc, ++ sas_device->sas_address, ++ sas_device->sas_address_parent); ++ _scsih_sas_device_remove(ioc, sas_device); ++ } ++ } ++} ++ ++/** ++ * _scsih_sas_device_init_add - insert sas_device to the list. ++ * @ioc: per adapter object ++ * @sas_device: the sas_device object ++ * Context: This function will acquire ioc->sas_device_lock. ++ * ++ * Adding new object at driver load time to the ioc->sas_device_init_list. ++ */ ++static void ++_scsih_sas_device_init_add(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_device *sas_device) ++{ ++ unsigned long flags; ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, ++ __func__, sas_device->handle, ++ (unsigned long long)sas_device->sas_address)); ++ ++ if (sas_device->enclosure_handle != 0) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enclosure logical id(0x%016llx), slot( %d)\n", ++ ioc->name, __func__, (unsigned long long) ++ sas_device->enclosure_logical_id, sas_device->slot)); ++ ++ if (sas_device->connector_name[0] != '\0') ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enclosure level(0x%04x), connector name( %s)\n", ++ ioc->name, __func__, sas_device->enclosure_level, ++ sas_device->connector_name)); ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device_get(sas_device); ++ list_add_tail(&sas_device->list, &ioc->sas_device_init_list); ++ _scsih_determine_boot_device(ioc, sas_device, 0); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++} ++ ++/** ++ * _scsih_raid_device_find_by_id - raid device search ++ * @ioc: per adapter object ++ * @id: sas device target id ++ * @channel: sas device channel ++ * Context: Calling function should acquire ioc->raid_device_lock ++ * ++ * This searches for raid_device based on target id, then return raid_device ++ * object. ++ */ ++static struct _raid_device * ++_scsih_raid_device_find_by_id(struct MPT3SAS_ADAPTER *ioc, int id, int channel) ++{ ++ struct _raid_device *raid_device, *r; ++ ++ r = NULL; ++ list_for_each_entry(raid_device, &ioc->raid_device_list, list) { ++ if (raid_device->id == id && raid_device->channel == channel) { ++ r = raid_device; ++ goto out; ++ } ++ } ++ ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_raid_device_find_by_handle - raid device search ++ * @ioc: per adapter object ++ * @handle: sas device handle (assigned by firmware) ++ * Context: Calling function should acquire ioc->raid_device_lock ++ * ++ * This searches for raid_device based on handle, then return raid_device ++ * object. ++ */ ++struct _raid_device * ++mpt2sas_raid_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _raid_device *raid_device, *r; ++ ++ r = NULL; ++ list_for_each_entry(raid_device, &ioc->raid_device_list, list) { ++ if (raid_device->handle != handle) ++ continue; ++ r = raid_device; ++ goto out; ++ } ++ ++ out: ++ return r; ++} ++ ++/** ++ * _scsih_raid_device_find_by_wwid - raid device search ++ * @ioc: per adapter object ++ * @handle: sas device handle (assigned by firmware) ++ * Context: Calling function should acquire ioc->raid_device_lock ++ * ++ * This searches for raid_device based on wwid, then return raid_device ++ * object. ++ */ ++static struct _raid_device * ++_scsih_raid_device_find_by_wwid(struct MPT3SAS_ADAPTER *ioc, u64 wwid) ++{ ++ struct _raid_device *raid_device, *r; ++ ++ r = NULL; ++ list_for_each_entry(raid_device, &ioc->raid_device_list, list) { ++ if (raid_device->wwid != wwid) ++ continue; ++ r = raid_device; ++ goto out; ++ } ++ ++ out: ++ return r; ++} ++ ++/** ++ * _scsih_raid_device_add - add raid_device object ++ * @ioc: per adapter object ++ * @raid_device: raid_device object ++ * ++ * This is added to the raid_device_list link list. ++ */ ++static void ++_scsih_raid_device_add(struct MPT3SAS_ADAPTER *ioc, ++ struct _raid_device *raid_device) ++{ ++ unsigned long flags; ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: handle(0x%04x), wwid(0x%016llx)\n", ioc->name, __func__, ++ raid_device->handle, (unsigned long long)raid_device->wwid)); ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ list_add_tail(&raid_device->list, &ioc->raid_device_list); ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++} ++ ++/** ++ * _scsih_raid_device_remove - delete raid_device object ++ * @ioc: per adapter object ++ * @raid_device: raid_device object ++ * ++ */ ++static void ++_scsih_raid_device_remove(struct MPT3SAS_ADAPTER *ioc, ++ struct _raid_device *raid_device) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ list_del(&raid_device->list); ++ kfree(raid_device); ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++} ++ ++/** ++ * mpt2sas_scsih_expander_find_by_handle - expander device search ++ * @ioc: per adapter object ++ * @handle: expander handle (assigned by firmware) ++ * Context: Calling function should acquire ioc->sas_device_lock ++ * ++ * This searches for expander device based on handle, then returns the ++ * sas_node object. ++ */ ++struct _sas_node * ++mpt2sas_scsih_expander_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _sas_node *sas_expander, *r; ++ ++ r = NULL; ++ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { ++ if (sas_expander->handle != handle) ++ continue; ++ r = sas_expander; ++ goto out; ++ } ++ out: ++ return r; ++} ++ ++/** ++ * mpt2sas_scsih_expander_find_by_sas_address - expander device search ++ * @ioc: per adapter object ++ * @sas_address: sas address ++ * Context: Calling function should acquire ioc->sas_node_lock. ++ * ++ * This searches for expander device based on sas_address, then returns the ++ * sas_node object. ++ */ ++struct _sas_node * ++mpt2sas_scsih_expander_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address) ++{ ++ struct _sas_node *sas_expander, *r; ++ ++ r = NULL; ++ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { ++ if (sas_expander->sas_address != sas_address) ++ continue; ++ r = sas_expander; ++ goto out; ++ } ++ out: ++ return r; ++} ++ ++/** ++ * _scsih_expander_node_add - insert expander device to the list. ++ * @ioc: per adapter object ++ * @sas_expander: the sas_device object ++ * Context: This function will acquire ioc->sas_node_lock. ++ * ++ * Adding new object to the ioc->sas_expander_list. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_expander_node_add(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_node *sas_expander) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ list_add_tail(&sas_expander->list, &ioc->sas_expander_list); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++} ++ ++/** ++ * _scsih_is_end_device - determines if device is an end device ++ * @device_info: bitfield providing information about the device. ++ * Context: none ++ * ++ * Returns 1 if end device. ++ */ ++static int ++_scsih_is_end_device(u32 device_info) ++{ ++ if (device_info & MPI2_SAS_DEVICE_INFO_END_DEVICE && ++ ((device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) | ++ (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) | ++ (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE))) ++ return 1; ++ else ++ return 0; ++} ++ ++/** ++ * _scsih_scsi_lookup_get - returns scmd entry ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Returns the smid stored scmd pointer. ++ */ ++static struct scsi_cmnd * ++_scsih_scsi_lookup_get(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ return ioc->scsi_lookup[smid - 1].scmd; ++} ++ ++/** ++ * _scsih_scsi_lookup_get_clear - returns scmd entry ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Returns the smid stored scmd pointer. ++ * Then will derefrence the stored scmd pointer. ++ */ ++static inline struct scsi_cmnd * ++_scsih_scsi_lookup_get_clear(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ unsigned long flags; ++ struct scsi_cmnd *scmd; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ scmd = ioc->scsi_lookup[smid - 1].scmd; ++ ioc->scsi_lookup[smid - 1].scmd = NULL; ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ ++ return scmd; ++} ++ ++/** ++ * _scsih_scsi_lookup_find_by_scmd - scmd lookup ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @scmd: pointer to scsi command object ++ * Context: This function will acquire ioc->scsi_lookup_lock. ++ * ++ * This will search for a scmd pointer in the scsi_lookup array, ++ * returning the revelent smid. A returned value of zero means invalid. ++ */ ++static u16 ++_scsih_scsi_lookup_find_by_scmd(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd ++ *scmd) ++{ ++ u16 smid; ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ smid = 0; ++ for (i = 0; i < ioc->scsiio_depth; i++) { ++ if (ioc->scsi_lookup[i].scmd == scmd) { ++ smid = ioc->scsi_lookup[i].smid; ++ goto out; ++ } ++ } ++ out: ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return smid; ++} ++ ++/** ++ * _scsih_scsi_lookup_find_by_target - search for matching channel:id ++ * @ioc: per adapter object ++ * @id: target id ++ * @channel: channel ++ * Context: This function will acquire ioc->scsi_lookup_lock. ++ * ++ * This will search for a matching channel:id in the scsi_lookup array, ++ * returning 1 if found. ++ */ ++static u8 ++_scsih_scsi_lookup_find_by_target(struct MPT3SAS_ADAPTER *ioc, int id, ++ int channel) ++{ ++ u8 found; ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ found = 0; ++ for (i = 0 ; i < ioc->scsiio_depth; i++) { ++ if (ioc->scsi_lookup[i].scmd && ++ (ioc->scsi_lookup[i].scmd->device->id == id && ++ ioc->scsi_lookup[i].scmd->device->channel == channel)) { ++ found = 1; ++ goto out; ++ } ++ } ++ out: ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return found; ++} ++ ++/** ++ * _scsih_scsi_lookup_find_by_lun - search for matching channel:id:lun ++ * @ioc: per adapter object ++ * @id: target id ++ * @lun: lun number ++ * @channel: channel ++ * Context: This function will acquire ioc->scsi_lookup_lock. ++ * ++ * This will search for a matching channel:id:lun in the scsi_lookup array, ++ * returning 1 if found. ++ */ ++static u8 ++_scsih_scsi_lookup_find_by_lun(struct MPT3SAS_ADAPTER *ioc, int id, ++ unsigned int lun, int channel) ++{ ++ u8 found; ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ found = 0; ++ for (i = 0 ; i < ioc->scsiio_depth; i++) { ++ if (ioc->scsi_lookup[i].scmd && ++ (ioc->scsi_lookup[i].scmd->device->id == id && ++ ioc->scsi_lookup[i].scmd->device->channel == channel && ++ ioc->scsi_lookup[i].scmd->device->lun == lun)) { ++ found = 1; ++ goto out; ++ } ++ } ++ out: ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ return found; ++} ++ ++static void ++_scsih_adjust_queue_depth(struct scsi_device *sdev, int qdepth) ++{ ++ struct Scsi_Host *shost = sdev->host; ++ int max_depth; ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ max_depth = shost->can_queue; ++ ++ /* limit max device queue for SATA to 32 */ ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data) ++ goto not_sata; ++ sas_target_priv_data = sas_device_priv_data->sas_target; ++ if (!sas_target_priv_data) ++ goto not_sata; ++ if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) ++ goto not_sata; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_from_target(ioc, sas_target_priv_data); ++ if (sas_device) { ++ if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE) ++ max_depth = MPT3SAS_SATA_QUEUE_DEPTH; ++ ++ sas_device_put(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ not_sata: ++ ++ if (!sdev->tagged_supported) ++ max_depth = 1; ++ if (qdepth > max_depth) ++ qdepth = max_depth; ++ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); ++} ++ ++/** ++ * scsih_change_queue_depth_mpt2sas - setting device queue depth ++ * @sdev: scsi device struct ++ * @qdepth: requested queue depth ++ * @reason: SCSI_QDEPTH_DEFAULT/SCSI_QDEPTH_QFULL/SCSI_QDEPTH_RAMP_UP ++ * (see include/scsi/scsi_host.h for definition) ++ * ++ * Returns queue depth. ++ */ ++int ++scsih_change_queue_depth_mpt2sas(struct scsi_device *sdev, int qdepth, int reason) ++{ ++ if (reason == SCSI_QDEPTH_DEFAULT || reason == SCSI_QDEPTH_RAMP_UP) ++ _scsih_adjust_queue_depth(sdev, qdepth); ++ else if (reason == SCSI_QDEPTH_QFULL) ++ scsi_track_queue_full(sdev, qdepth); ++ else ++ return -EOPNOTSUPP; ++ ++ if (sdev->inquiry_len > 7) ++ sdev_printk(KERN_INFO, sdev, "qdepth(%d), tagged(%d), " \ ++ "simple(%d), ordered(%d), scsi_level(%d), cmd_que(%d)\n", ++ sdev->queue_depth, sdev->tagged_supported, sdev->simple_tags, ++ sdev->ordered_tags, sdev->scsi_level, ++ (sdev->inquiry[7] & 2) >> 1); ++ ++ return sdev->queue_depth; ++} ++ ++/** ++ * _scsih_change_queue_type_mpt2sas - changing device queue tag type ++ * @sdev: scsi device struct ++ * @tag_type: requested tag type ++ * ++ * Returns queue tag type. ++ */ ++int ++_scsih_change_queue_type_mpt2sas(struct scsi_device *sdev, int tag_type) ++{ ++ if (sdev->tagged_supported) { ++ scsi_set_tag_type(sdev, tag_type); ++ if (tag_type) ++ scsi_activate_tcq(sdev, sdev->queue_depth); ++ else ++ scsi_deactivate_tcq(sdev, sdev->queue_depth); ++ } else ++ tag_type = 0; ++ ++ return tag_type; ++} ++ ++ ++/** ++ * scsih_target_alloc_mpt2sas - target add routine ++ * @starget: scsi target struct ++ * ++ * Returns 0 if ok. Any other return is assumed to be an error and ++ * the device is ignored. ++ */ ++int ++scsih_target_alloc_mpt2sas(struct scsi_target *starget) ++{ ++ struct Scsi_Host *shost = dev_to_shost(&starget->dev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct _sas_device *sas_device; ++ struct _raid_device *raid_device; ++ unsigned long flags; ++ struct sas_rphy *rphy; ++ ++ sas_target_priv_data = kzalloc(sizeof(*sas_target_priv_data), ++ GFP_KERNEL); ++ if (!sas_target_priv_data) ++ return -ENOMEM; ++ ++ starget->hostdata = sas_target_priv_data; ++ sas_target_priv_data->starget = starget; ++ sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE; ++ ++ /* RAID volumes */ ++ if (starget->channel == RAID_CHANNEL) { ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = _scsih_raid_device_find_by_id(ioc, starget->id, ++ starget->channel); ++ if (raid_device) { ++ sas_target_priv_data->handle = raid_device->handle; ++ sas_target_priv_data->sas_address = raid_device->wwid; ++ sas_target_priv_data->flags |= MPT_TARGET_FLAGS_VOLUME; ++ sas_target_priv_data->raid_device = raid_device; ++ if (ioc->is_warpdrive) ++ raid_device->starget = starget; ++ } ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ return 0; ++ } ++ ++ /* sas/sata devices */ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ rphy = dev_to_rphy(starget->dev.parent); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ rphy->identify.sas_address); ++ ++ if (sas_device) { ++ sas_target_priv_data->handle = sas_device->handle; ++ sas_target_priv_data->sas_address = sas_device->sas_address; ++ sas_target_priv_data->sdev = sas_device; ++ sas_device->starget = starget; ++ sas_device->id = starget->id; ++ sas_device->channel = starget->channel; ++ if (test_bit(sas_device->handle, ioc->pd_handles)) ++ sas_target_priv_data->flags |= ++ MPT_TARGET_FLAGS_RAID_COMPONENT; ++ if (sas_device->fast_path) ++ sas_target_priv_data->flags |= MPT_TARGET_FASTPATH_IO; ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ return 0; ++} ++ ++/** ++ * scsih_target_destroy_mpt2sas - target destroy routine ++ * @starget: scsi target struct ++ * ++ * Returns nothing. ++ */ ++void ++scsih_target_destroy_mpt2sas(struct scsi_target *starget) ++{ ++ struct Scsi_Host *shost = dev_to_shost(&starget->dev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct _sas_device *sas_device; ++ struct _raid_device *raid_device; ++ unsigned long flags; ++ struct sas_rphy *rphy; ++ ++ sas_target_priv_data = starget->hostdata; ++ if (!sas_target_priv_data) ++ return; ++ ++ if (starget->channel == RAID_CHANNEL) { ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = _scsih_raid_device_find_by_id(ioc, starget->id, ++ starget->channel); ++ if (raid_device) { ++ raid_device->starget = NULL; ++ raid_device->sdev = NULL; ++ } ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ goto out; ++ } ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ rphy = dev_to_rphy(starget->dev.parent); ++ sas_device = __mpt2sas_get_sdev_from_target(ioc, sas_target_priv_data); ++ if (sas_device && (sas_device->starget == starget) && ++ (sas_device->id == starget->id) && ++ (sas_device->channel == starget->channel)) ++ sas_device->starget = NULL; ++ ++ if (sas_device) { ++ /* ++ * Corresponding get() is in _scsih_target_alloc_mpt2sas() ++ */ ++ sas_target_priv_data->sdev = NULL; ++ sas_device_put(sas_device); ++ ++ sas_device_put(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ out: ++ kfree(sas_target_priv_data); ++ starget->hostdata = NULL; ++} ++ ++/** ++ * scsih_slave_alloc_mpt2sas - device add routine ++ * @sdev: scsi device struct ++ * ++ * Returns 0 if ok. Any other return is assumed to be an error and ++ * the device is ignored. ++ */ ++int ++scsih_slave_alloc_mpt2sas(struct scsi_device *sdev) ++{ ++ struct Scsi_Host *shost; ++ struct MPT3SAS_ADAPTER *ioc; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_target *starget; ++ struct _raid_device *raid_device; ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ sas_device_priv_data = kzalloc(sizeof(*sas_device_priv_data), ++ GFP_KERNEL); ++ if (!sas_device_priv_data) ++ return -ENOMEM; ++ ++ sas_device_priv_data->lun = sdev->lun; ++ sas_device_priv_data->flags = MPT_DEVICE_FLAGS_INIT; ++ ++ starget = scsi_target(sdev); ++ sas_target_priv_data = starget->hostdata; ++ sas_target_priv_data->num_luns++; ++ sas_device_priv_data->sas_target = sas_target_priv_data; ++ sdev->hostdata = sas_device_priv_data; ++ if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT)) ++ sdev->no_uld_attach = 1; ++ ++ shost = dev_to_shost(&starget->dev); ++ ioc = shost_priv(shost); ++ if (starget->channel == RAID_CHANNEL) { ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = _scsih_raid_device_find_by_id(ioc, ++ starget->id, starget->channel); ++ if (raid_device) ++ raid_device->sdev = sdev; /* raid is single lun */ ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ } ++ ++ if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ sas_target_priv_data->sas_address); ++ if (sas_device && (sas_device->starget == NULL)) { ++ sdev_printk(KERN_INFO, sdev, ++ "%s : sas_device->starget set to starget @ %d\n", ++ __func__, __LINE__); ++ sas_device->starget = starget; ++ } ++ ++ if (sas_device) ++ sas_device_put(sas_device); ++ ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ } ++ ++ return 0; ++} ++ ++/** ++ * scsih_slave_destroy_mpt2sas - device destroy routine ++ * @sdev: scsi device struct ++ * ++ * Returns nothing. ++ */ ++void ++scsih_slave_destroy_mpt2sas(struct scsi_device *sdev) ++{ ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct scsi_target *starget; ++ struct Scsi_Host *shost; ++ struct MPT3SAS_ADAPTER *ioc; ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ if (!sdev->hostdata) ++ return; ++ ++ starget = scsi_target(sdev); ++ sas_target_priv_data = starget->hostdata; ++ sas_target_priv_data->num_luns--; ++ ++ shost = dev_to_shost(&starget->dev); ++ ioc = shost_priv(shost); ++ ++ if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_from_target(ioc, ++ sas_target_priv_data); ++ if (sas_device && !sas_target_priv_data->num_luns) ++ sas_device->starget = NULL; ++ ++ if (sas_device) ++ sas_device_put(sas_device); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ } ++ ++ kfree(sdev->hostdata); ++ sdev->hostdata = NULL; ++} ++ ++/** ++ * _scsih_display_sata_capabilities - sata capabilities ++ * @ioc: per adapter object ++ * @handle: device handle ++ * @sdev: scsi device struct ++ */ ++static void ++_scsih_display_sata_capabilities(struct MPT3SAS_ADAPTER *ioc, ++ u16 handle, struct scsi_device *sdev) ++{ ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ u32 ioc_status; ++ u16 flags; ++ u32 device_info; ++ ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, ++ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ flags = le16_to_cpu(sas_device_pg0.Flags); ++ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); ++ ++ sdev_printk(KERN_INFO, sdev, ++ "atapi(%s), ncq(%s), asyn_notify(%s), smart(%s), fua(%s), " ++ "sw_preserve(%s)\n", ++ (device_info & MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE) ? "y" : "n", ++ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED) ? "y" : "n", ++ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY) ? "y" : ++ "n", ++ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED) ? "y" : "n", ++ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED) ? "y" : "n", ++ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE) ? "y" : "n"); ++} ++ ++/* ++ * raid transport support - ++ * Enabled for SLES11 and newer, in older kernels the driver will panic when ++ * unloading the driver followed by a load - I beleive that the subroutine ++ * raid_class_release() is not cleaning up properly. ++ */ ++ ++/** ++ * scsih_is_raid_mpt2sas - return boolean indicating device is raid volume ++ * @dev the device struct object ++ */ ++int ++scsih_is_raid_mpt2sas(struct device *dev) ++{ ++ struct scsi_device *sdev = to_scsi_device(dev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host); ++ ++ if (ioc->is_warpdrive) ++ return 0; ++ return (sdev->channel == RAID_CHANNEL) ? 1 : 0; ++} ++ ++/** ++ * scsih_get_resync_mpt2sas - get raid volume resync percent complete ++ * @dev the device struct object ++ */ ++void ++scsih_get_resync_mpt2sas(struct device *dev) ++{ ++ struct scsi_device *sdev = to_scsi_device(dev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host); ++ static struct _raid_device *raid_device; ++ unsigned long flags; ++ Mpi2RaidVolPage0_t vol_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u32 volume_status_flags; ++ u8 percent_complete; ++ u16 handle; ++ ++ percent_complete = 0; ++ handle = 0; ++ if (ioc->is_warpdrive) ++ goto out; ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id, ++ sdev->channel); ++ if (raid_device) { ++ handle = raid_device->handle; ++ percent_complete = raid_device->percent_complete; ++ } ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ ++ if (!handle) ++ goto out; ++ ++ if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0, ++ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, ++ sizeof(Mpi2RaidVolPage0_t))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ percent_complete = 0; ++ goto out; ++ } ++ ++ volume_status_flags = le32_to_cpu(vol_pg0.VolumeStatusFlags); ++ if (!(volume_status_flags & ++ MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS)) ++ percent_complete = 0; ++ ++ out: ++ ++ switch (ioc->hba_mpi_version_belonged) { ++ case MPI2_VERSION: ++ raid_set_resync(mpt2sas_raid_template_mpt2sas, dev, percent_complete); ++ break; ++ case MPI25_VERSION: ++ case MPI26_VERSION: ++ raid_set_resync(mpt3sas_raid_template_mpt2sas, dev, percent_complete); ++ break; ++ } ++} ++ ++/** ++ * scsih_get_state_mpt2sas - get raid volume level ++ * @dev the device struct object ++ */ ++void ++scsih_get_state_mpt2sas(struct device *dev) ++{ ++ struct scsi_device *sdev = to_scsi_device(dev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host); ++ static struct _raid_device *raid_device; ++ unsigned long flags; ++ Mpi2RaidVolPage0_t vol_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u32 volstate; ++ enum raid_state state = RAID_STATE_UNKNOWN; ++ u16 handle = 0; ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id, ++ sdev->channel); ++ if (raid_device) ++ handle = raid_device->handle; ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ ++ if (!raid_device) ++ goto out; ++ ++ if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0, ++ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, ++ sizeof(Mpi2RaidVolPage0_t))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ++ volstate = le32_to_cpu(vol_pg0.VolumeStatusFlags); ++ if (volstate & MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) { ++ state = RAID_STATE_RESYNCING; ++ goto out; ++ } ++ ++ switch (vol_pg0.VolumeState) { ++ case MPI2_RAID_VOL_STATE_OPTIMAL: ++ case MPI2_RAID_VOL_STATE_ONLINE: ++ state = RAID_STATE_ACTIVE; ++ break; ++ case MPI2_RAID_VOL_STATE_DEGRADED: ++ state = RAID_STATE_DEGRADED; ++ break; ++ case MPI2_RAID_VOL_STATE_FAILED: ++ case MPI2_RAID_VOL_STATE_MISSING: ++ state = RAID_STATE_OFFLINE; ++ break; ++ } ++ out: ++ switch (ioc->hba_mpi_version_belonged) { ++ case MPI2_VERSION: ++ raid_set_state(mpt2sas_raid_template_mpt2sas, dev, state); ++ break; ++ case MPI25_VERSION: ++ case MPI26_VERSION: ++ raid_set_state(mpt3sas_raid_template_mpt2sas, dev, state); ++ break; ++ } ++} ++ ++/** ++ * _scsih_set_level - set raid level ++ * @sdev: scsi device struct ++ * @volume_type: volume type ++ */ ++static void ++_scsih_set_level(struct MPT3SAS_ADAPTER *ioc, ++ struct scsi_device *sdev, u8 volume_type) ++{ ++ enum raid_level level = RAID_LEVEL_UNKNOWN; ++ ++ switch (volume_type) { ++ case MPI2_RAID_VOL_TYPE_RAID0: ++ level = RAID_LEVEL_0; ++ break; ++ case MPI2_RAID_VOL_TYPE_RAID10: ++ level = RAID_LEVEL_10; ++ break; ++ case MPI2_RAID_VOL_TYPE_RAID1E: ++ level = RAID_LEVEL_1E; ++ break; ++ case MPI2_RAID_VOL_TYPE_RAID1: ++ level = RAID_LEVEL_1; ++ break; ++ } ++ ++ switch (ioc->hba_mpi_version_belonged) { ++ case MPI2_VERSION: ++ raid_set_level(mpt2sas_raid_template_mpt2sas, ++ &sdev->sdev_gendev, level); ++ break; ++ case MPI25_VERSION: ++ case MPI26_VERSION: ++ raid_set_level(mpt3sas_raid_template_mpt2sas, ++ &sdev->sdev_gendev, level); ++ break; ++ } ++} ++ ++ ++/** ++ * _scsih_get_volume_capabilities - volume capabilities ++ * @ioc: per adapter object ++ * @sas_device: the raid_device object ++ * ++ * Returns 0 for success, else 1 ++ */ ++static int ++_scsih_get_volume_capabilities(struct MPT3SAS_ADAPTER *ioc, ++ struct _raid_device *raid_device) ++{ ++ Mpi2RaidVolPage0_t *vol_pg0; ++ Mpi2RaidPhysDiskPage0_t pd_pg0; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 sz; ++ u8 num_pds; ++ ++ if ((mpt2sas_config_get_number_pds(ioc, raid_device->handle, ++ &num_pds)) || !num_pds) { ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, ++ __func__)); ++ return 1; ++ } ++ ++ raid_device->num_pds = num_pds; ++ sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds * ++ sizeof(Mpi2RaidVol0PhysDisk_t)); ++ vol_pg0 = kzalloc(sz, GFP_KERNEL); ++ if (!vol_pg0) { ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, ++ __func__)); ++ return 1; ++ } ++ ++ if ((mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0, ++ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) { ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, ++ __func__)); ++ kfree(vol_pg0); ++ return 1; ++ } ++ ++ raid_device->volume_type = vol_pg0->VolumeType; ++ ++ /* figure out what the underlying devices are by ++ * obtaining the device_info bits for the 1st device ++ */ ++ if (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, ++ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM, ++ vol_pg0->PhysDisk[0].PhysDiskNum))) { ++ if (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, ++ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ++ le16_to_cpu(pd_pg0.DevHandle)))) { ++ raid_device->device_info = ++ le32_to_cpu(sas_device_pg0.DeviceInfo); ++ } ++ } ++ ++ kfree(vol_pg0); ++ return 0; ++} ++ ++/** ++ * _scsih_enable_tlr - setting TLR flags ++ * @ioc: per adapter object ++ * @sdev: scsi device struct ++ * ++ * Enabling Transaction Layer Retries for tape devices when ++ * vpd page 0x90 is present ++ * ++ */ ++static void ++_scsih_enable_tlr(struct MPT3SAS_ADAPTER *ioc, struct scsi_device *sdev) ++{ ++ ++ /* only for TAPE */ ++ if (sdev->type != TYPE_TAPE) ++ return; ++ ++ if (!(ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR)) ++ return; ++ ++ sas_enable_tlr(sdev); ++ sdev_printk(KERN_INFO, sdev, "TLR %s\n", ++ sas_is_tlr_enabled(sdev) ? "Enabled" : "Disabled"); ++ return; ++ ++} ++ ++/** ++ * scsih_slave_configure_mpt2sas - device configure routine. ++ * @sdev: scsi device struct ++ * ++ * Returns 0 if ok. Any other return is assumed to be an error and ++ * the device is ignored. ++ */ ++int ++scsih_slave_configure_mpt2sas(struct scsi_device *sdev) ++{ ++ struct Scsi_Host *shost = sdev->host; ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct _sas_device *sas_device; ++ struct _raid_device *raid_device; ++ unsigned long flags; ++ int qdepth; ++ u8 ssp_target = 0; ++ char *ds = ""; ++ char *r_level = ""; ++ u16 handle, volume_handle = 0; ++ u64 volume_wwid = 0; ++ ++ qdepth = 1; ++ sas_device_priv_data = sdev->hostdata; ++ sas_device_priv_data->configured_lun = 1; ++ sas_device_priv_data->flags &= ~MPT_DEVICE_FLAGS_INIT; ++ sas_target_priv_data = sas_device_priv_data->sas_target; ++ handle = sas_target_priv_data->handle; ++ ++ /* raid volume handling */ ++ if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME) { ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = mpt2sas_raid_device_find_by_handle(ioc, handle); ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ if (!raid_device) { ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, ++ __LINE__, __func__)); ++ return 1; ++ } ++ ++ if (_scsih_get_volume_capabilities(ioc, raid_device)) { ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, ++ __LINE__, __func__)); ++ return 1; ++ } ++ ++ /* ++ * WARPDRIVE: Initialize the required data for Direct IO ++ */ ++ mpt2sas_init_warpdrive_properties(ioc, raid_device); ++ ++ /* RAID Queue Depth Support ++ * IS volume = underlying qdepth of drive type, either ++ * MPT3SAS_SAS_QUEUE_DEPTH or MPT3SAS_SATA_QUEUE_DEPTH ++ * IM/IME/R10 = 128 (MPT3SAS_RAID_QUEUE_DEPTH) ++ */ ++ if (raid_device->device_info & ++ MPI2_SAS_DEVICE_INFO_SSP_TARGET) { ++ qdepth = MPT3SAS_SAS_QUEUE_DEPTH; ++ ds = "SSP"; ++ } else { ++ qdepth = MPT3SAS_SATA_QUEUE_DEPTH; ++ if (raid_device->device_info & ++ MPI2_SAS_DEVICE_INFO_SATA_DEVICE) ++ ds = "SATA"; ++ else ++ ds = "STP"; ++ } ++ ++ switch (raid_device->volume_type) { ++ case MPI2_RAID_VOL_TYPE_RAID0: ++ r_level = "RAID0"; ++ break; ++ case MPI2_RAID_VOL_TYPE_RAID1E: ++ qdepth = MPT3SAS_RAID_QUEUE_DEPTH; ++ if (ioc->manu_pg10.OEMIdentifier && ++ (le32_to_cpu(ioc->manu_pg10.GenericFlags0) & ++ MFG10_GF0_R10_DISPLAY) && ++ !(raid_device->num_pds % 2)) ++ r_level = "RAID10"; ++ else ++ r_level = "RAID1E"; ++ break; ++ case MPI2_RAID_VOL_TYPE_RAID1: ++ qdepth = MPT3SAS_RAID_QUEUE_DEPTH; ++ r_level = "RAID1"; ++ break; ++ case MPI2_RAID_VOL_TYPE_RAID10: ++ qdepth = MPT3SAS_RAID_QUEUE_DEPTH; ++ r_level = "RAID10"; ++ break; ++ case MPI2_RAID_VOL_TYPE_UNKNOWN: ++ default: ++ qdepth = MPT3SAS_RAID_QUEUE_DEPTH; ++ r_level = "RAIDX"; ++ break; ++ } ++ ++ if (!ioc->hide_ir_msg) ++ sdev_printk(KERN_INFO, sdev, ++ "%s: handle(0x%04x), wwid(0x%016llx)," ++ " pd_count(%d), type(%s)\n", ++ r_level, raid_device->handle, ++ (unsigned long long)raid_device->wwid, ++ raid_device->num_pds, ds); ++ ++ if (shost->max_sectors > MPT3SAS_RAID_MAX_SECTORS) { ++ blk_queue_max_hw_sectors(sdev->request_queue, ++ MPT3SAS_RAID_MAX_SECTORS); ++ sdev_printk(KERN_INFO, sdev, ++ "Set queue's max_sector to: %u\n", ++ MPT3SAS_RAID_MAX_SECTORS); ++ } ++ ++ scsih_change_queue_depth_mpt2sas(sdev, qdepth, SCSI_QDEPTH_DEFAULT); ++ ++ /* raid transport support */ ++ if (!ioc->is_warpdrive) ++ _scsih_set_level(ioc, sdev, raid_device->volume_type); ++ return 0; ++ } ++ ++ /* non-raid handling */ ++ if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) { ++ if (mpt2sas_config_get_volume_handle(ioc, handle, ++ &volume_handle)) { ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__)); ++ return 1; ++ } ++ if (volume_handle && mpt2sas_config_get_volume_wwid(ioc, ++ volume_handle, &volume_wwid)) { ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__)); ++ return 1; ++ } ++ } ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ sas_device_priv_data->sas_target->sas_address); ++ if (!sas_device) { ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ dfailprintk(ioc, pr_warn(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, ++ __func__)); ++ return 1; ++ } ++ ++ sas_device->volume_handle = volume_handle; ++ sas_device->volume_wwid = volume_wwid; ++ if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) { ++ qdepth = MPT3SAS_SAS_QUEUE_DEPTH; ++ ssp_target = 1; ++ if (sas_device->device_info & ++ MPI2_SAS_DEVICE_INFO_SEP) { ++ sdev_printk(KERN_WARNING, sdev, ++ "set ignore_delay_remove for handle(0x%04x)\n", ++ sas_device_priv_data->sas_target->handle); ++ sas_device_priv_data->ignore_delay_remove = 1; ++ ds = "SES"; ++ } else ++ ds = "SSP"; ++ } else { ++ qdepth = MPT3SAS_SATA_QUEUE_DEPTH; ++ if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) ++ ds = "STP"; ++ else if (sas_device->device_info & ++ MPI2_SAS_DEVICE_INFO_SATA_DEVICE) ++ ds = "SATA"; ++ } ++ ++ sdev_printk(KERN_INFO, sdev, "%s: handle(0x%04x), " \ ++ "sas_addr(0x%016llx), phy(%d), device_name(0x%016llx)\n", ++ ds, handle, (unsigned long long)sas_device->sas_address, ++ sas_device->phy, (unsigned long long)sas_device->device_name); ++ if (sas_device->enclosure_handle != 0) ++ sdev_printk(KERN_INFO, sdev, ++ "%s: enclosure_logical_id(0x%016llx), slot(%d)\n", ++ ds, (unsigned long long) ++ sas_device->enclosure_logical_id, sas_device->slot); ++ if (sas_device->connector_name[0] != '\0') ++ sdev_printk(KERN_INFO, sdev, ++ "%s: enclosure level(0x%04x), connector name( %s)\n", ++ ds, sas_device->enclosure_level, ++ sas_device->connector_name); ++ ++ sas_device_put(sas_device); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ if (!ssp_target) ++ _scsih_display_sata_capabilities(ioc, handle, sdev); ++ ++ ++ scsih_change_queue_depth_mpt2sas(sdev, qdepth, SCSI_QDEPTH_DEFAULT); ++ ++ if (ssp_target) { ++ sas_read_port_mode_page(sdev); ++ _scsih_enable_tlr(ioc, sdev); ++ } ++ ++ return 0; ++} ++ ++/** ++ * scsih_bios_param_mpt2sas - fetch head, sector, cylinder info for a disk ++ * @sdev: scsi device struct ++ * @bdev: pointer to block device context ++ * @capacity: device size (in 512 byte sectors) ++ * @params: three element array to place output: ++ * params[0] number of heads (max 255) ++ * params[1] number of sectors (max 63) ++ * params[2] number of cylinders ++ * ++ * Return nothing. ++ */ ++int ++scsih_bios_param_mpt2sas(struct scsi_device *sdev, struct block_device *bdev, ++ sector_t capacity, int params[]) ++{ ++ int heads; ++ int sectors; ++ sector_t cylinders; ++ ulong dummy; ++ ++ heads = 64; ++ sectors = 32; ++ ++ dummy = heads * sectors; ++ cylinders = capacity; ++ sector_div(cylinders, dummy); ++ ++ /* ++ * Handle extended translation size for logical drives ++ * > 1Gb ++ */ ++ if ((ulong)capacity >= 0x200000) { ++ heads = 255; ++ sectors = 63; ++ dummy = heads * sectors; ++ cylinders = capacity; ++ sector_div(cylinders, dummy); ++ } ++ ++ /* return result */ ++ params[0] = heads; ++ params[1] = sectors; ++ params[2] = cylinders; ++ ++ return 0; ++} ++ ++/** ++ * _scsih_response_code - translation of device response code ++ * @ioc: per adapter object ++ * @response_code: response code returned by the device ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_response_code(struct MPT3SAS_ADAPTER *ioc, u8 response_code) ++{ ++ char *desc; ++ ++ switch (response_code) { ++ case MPI2_SCSITASKMGMT_RSP_TM_COMPLETE: ++ desc = "task management request completed"; ++ break; ++ case MPI2_SCSITASKMGMT_RSP_INVALID_FRAME: ++ desc = "invalid frame"; ++ break; ++ case MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED: ++ desc = "task management request not supported"; ++ break; ++ case MPI2_SCSITASKMGMT_RSP_TM_FAILED: ++ desc = "task management request failed"; ++ break; ++ case MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED: ++ desc = "task management request succeeded"; ++ break; ++ case MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN: ++ desc = "invalid lun"; ++ break; ++ case 0xA: ++ desc = "overlapped tag attempted"; ++ break; ++ case MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC: ++ desc = "task queued, however not sent to target"; ++ break; ++ default: ++ desc = "unknown"; ++ break; ++ } ++ pr_warn(MPT3SAS_FMT "response_code(0x%01x): %s\n", ++ ioc->name, response_code, desc); ++} ++ ++/** ++ * _scsih_tm_done - tm completion routine ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: none. ++ * ++ * The callback handler when using scsih_issue_tm. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_scsih_tm_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ ++ if (ioc->tm_cmds.status == MPT3_CMD_NOT_USED) ++ return 1; ++ if (ioc->tm_cmds.smid != smid) ++ return 1; ++ ioc->tm_cmds.status |= MPT3_CMD_COMPLETE; ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (mpi_reply) { ++ memcpy(ioc->tm_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); ++ ioc->tm_cmds.status |= MPT3_CMD_REPLY_VALID; ++ } ++ ioc->tm_cmds.status &= ~MPT3_CMD_PENDING; ++ complete(&ioc->tm_cmds.done); ++ return 1; ++} ++ ++/** ++ * mpt2sas_scsih_set_tm_flag - set per target tm_busy ++ * @ioc: per adapter object ++ * @handle: device handle ++ * ++ * During taskmangement request, we need to freeze the device queue. ++ */ ++void ++mpt2sas_scsih_set_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_device *sdev; ++ u8 skip = 0; ++ ++ shost_for_each_device(sdev, ioc->shost) { ++ if (skip) ++ continue; ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data) ++ continue; ++ if (sas_device_priv_data->sas_target->handle == handle) { ++ sas_device_priv_data->sas_target->tm_busy = 1; ++ skip = 1; ++ ioc->ignore_loginfos = 1; ++ } ++ } ++} ++ ++/** ++ * mpt2sas_scsih_clear_tm_flag - clear per target tm_busy ++ * @ioc: per adapter object ++ * @handle: device handle ++ * ++ * During taskmangement request, we need to freeze the device queue. ++ */ ++void ++mpt2sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_device *sdev; ++ u8 skip = 0; ++ ++ shost_for_each_device(sdev, ioc->shost) { ++ if (skip) ++ continue; ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data) ++ continue; ++ if (sas_device_priv_data->sas_target->handle == handle) { ++ sas_device_priv_data->sas_target->tm_busy = 0; ++ skip = 1; ++ ioc->ignore_loginfos = 0; ++ } ++ } ++} ++ ++/** ++ * mpt2sas_scsih_issue_tm - main routine for sending tm requests ++ * @ioc: per adapter struct ++ * @device_handle: device handle ++ * @channel: the channel assigned by the OS ++ * @id: the id assigned by the OS ++ * @lun: lun number ++ * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h) ++ * @smid_task: smid assigned to the task ++ * @timeout: timeout in seconds ++ * @m_type: TM_MUTEX_ON or TM_MUTEX_OFF ++ * Context: user ++ * ++ * A generic API for sending task management requests to firmware. ++ * ++ * The callback index is set inside `ioc->tm_cb_idx`. ++ * ++ * Return SUCCESS or FAILED. ++ */ ++int ++mpt2sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, ++ uint id, uint lun, u8 type, u16 smid_task, ulong timeout, ++ enum mutex_type m_type) ++{ ++ Mpi2SCSITaskManagementRequest_t *mpi_request; ++ Mpi2SCSITaskManagementReply_t *mpi_reply; ++ u16 smid = 0; ++ u32 ioc_state; ++ unsigned long timeleft; ++ struct scsiio_tracker *scsi_lookup = NULL; ++ int rc; ++ u16 msix_task = 0; ++ ++ if (m_type == TM_MUTEX_ON) ++ mutex_lock(&ioc->tm_cmds.mutex); ++ if (ioc->tm_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_info(MPT3SAS_FMT "%s: tm_cmd busy!!!\n", ++ __func__, ioc->name); ++ rc = FAILED; ++ goto err_out; ++ } ++ ++ if (ioc->shost_recovery || ioc->remove_host || ++ ioc->pci_error_recovery) { ++ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", ++ __func__, ioc->name); ++ rc = FAILED; ++ goto err_out; ++ } ++ ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 0); ++ if (ioc_state & MPI2_DOORBELL_USED) { ++ dhsprintk(ioc, pr_info(MPT3SAS_FMT ++ "unexpected doorbell active!\n", ioc->name)); ++ rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ rc = (!rc) ? SUCCESS : FAILED; ++ goto err_out; ++ } ++ ++ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { ++ mpt2sas_base_fault_info(ioc, ioc_state & ++ MPI2_DOORBELL_DATA_MASK); ++ rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ rc = (!rc) ? SUCCESS : FAILED; ++ goto err_out; ++ } ++ ++ smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = FAILED; ++ goto err_out; ++ } ++ ++ if (type == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK) ++ scsi_lookup = &ioc->scsi_lookup[smid_task - 1]; ++ ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "sending tm: handle(0x%04x), task_type(0x%02x), smid(%d)\n", ++ ioc->name, handle, type, smid_task)); ++ ioc->tm_cmds.status = MPT3_CMD_PENDING; ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->tm_cmds.smid = smid; ++ memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); ++ memset(ioc->tm_cmds.reply, 0, sizeof(Mpi2SCSITaskManagementReply_t)); ++ mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; ++ mpi_request->DevHandle = cpu_to_le16(handle); ++ mpi_request->TaskType = type; ++ mpi_request->TaskMID = cpu_to_le16(smid_task); ++ int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN); ++ mpt2sas_scsih_set_tm_flag(ioc, handle); ++ init_completion(&ioc->tm_cmds.done); ++ if ((type == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK) && ++ (scsi_lookup->msix_io < ioc->reply_queue_count)) ++ msix_task = scsi_lookup->msix_io; ++ else ++ msix_task = 0; ++ mpt2sas_base_put_smid_hi_priority(ioc, smid, msix_task); ++ timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ); ++ if (!(ioc->tm_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SCSITaskManagementRequest_t)/4); ++ if (!(ioc->tm_cmds.status & MPT3_CMD_RESET)) { ++ rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ rc = (!rc) ? SUCCESS : FAILED; ++ ioc->tm_cmds.status = MPT3_CMD_NOT_USED; ++ mpt2sas_scsih_clear_tm_flag(ioc, handle); ++ goto err_out; ++ } ++ } ++ ++ /* sync IRQs in case those were busy during flush. */ ++ mpt2sas_base_sync_reply_irqs(ioc); ++ ++ if (ioc->tm_cmds.status & MPT3_CMD_REPLY_VALID) { ++ mpt2sas_trigger_master(ioc, MASTER_TRIGGER_TASK_MANAGMENT); ++ mpi_reply = ioc->tm_cmds.reply; ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT "complete tm: " \ ++ "ioc_status(0x%04x), loginfo(0x%08x), term_count(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply->IOCStatus), ++ le32_to_cpu(mpi_reply->IOCLogInfo), ++ le32_to_cpu(mpi_reply->TerminationCount))); ++ if (ioc->logging_level & MPT_DEBUG_TM) { ++ _scsih_response_code(ioc, mpi_reply->ResponseCode); ++ if (mpi_reply->IOCStatus) ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SCSITaskManagementRequest_t)/4); ++ } ++ } ++ ++ switch (type) { ++ case MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK: ++ rc = SUCCESS; ++ if (scsi_lookup->scmd == NULL) ++ break; ++ rc = FAILED; ++ break; ++ ++ case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET: ++ if (_scsih_scsi_lookup_find_by_target(ioc, id, channel)) ++ rc = FAILED; ++ else ++ rc = SUCCESS; ++ break; ++ case MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET: ++ case MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET: ++ if (_scsih_scsi_lookup_find_by_lun(ioc, id, lun, channel)) ++ rc = FAILED; ++ else ++ rc = SUCCESS; ++ break; ++ case MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK: ++ rc = SUCCESS; ++ break; ++ default: ++ rc = FAILED; ++ break; ++ } ++ ++ mpt2sas_scsih_clear_tm_flag(ioc, handle); ++ ioc->tm_cmds.status = MPT3_CMD_NOT_USED; ++ if (m_type == TM_MUTEX_ON) ++ mutex_unlock(&ioc->tm_cmds.mutex); ++ ++ return rc; ++ ++ err_out: ++ if (m_type == TM_MUTEX_ON) ++ mutex_unlock(&ioc->tm_cmds.mutex); ++ return rc; ++} ++ ++/** ++ * _scsih_tm_display_info - displays info about the device ++ * @ioc: per adapter struct ++ * @scmd: pointer to scsi command object ++ * ++ * Called by task management callback handlers. ++ */ ++static void ++_scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd) ++{ ++ struct scsi_target *starget = scmd->device->sdev_target; ++ struct MPT3SAS_TARGET *priv_target = starget->hostdata; ++ struct _sas_device *sas_device = NULL; ++ unsigned long flags; ++ char *device_str = NULL; ++ ++ if (!priv_target) ++ return; ++ if (ioc->hide_ir_msg) ++ device_str = "WarpDrive"; ++ else ++ device_str = "volume"; ++ ++ scsi_print_command(scmd); ++ if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) { ++ starget_printk(KERN_INFO, starget, ++ "%s handle(0x%04x), %s wwid(0x%016llx)\n", ++ device_str, priv_target->handle, ++ device_str, (unsigned long long)priv_target->sas_address); ++ } else { ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_from_target(ioc, priv_target); ++ if (sas_device) { ++ if (priv_target->flags & ++ MPT_TARGET_FLAGS_RAID_COMPONENT) { ++ starget_printk(KERN_INFO, starget, ++ "volume handle(0x%04x), " ++ "volume wwid(0x%016llx)\n", ++ sas_device->volume_handle, ++ (unsigned long long)sas_device->volume_wwid); ++ } ++ starget_printk(KERN_INFO, starget, ++ "handle(0x%04x), sas_address(0x%016llx), phy(%d)\n", ++ sas_device->handle, ++ (unsigned long long)sas_device->sas_address, ++ sas_device->phy); ++ if (sas_device->enclosure_handle != 0) ++ starget_printk(KERN_INFO, starget, ++ "enclosure_logical_id(0x%016llx), slot(%d)\n", ++ (unsigned long long) ++ sas_device->enclosure_logical_id, ++ sas_device->slot); ++ if (sas_device->connector_name[0] != '\0') ++ starget_printk(KERN_INFO, starget, ++ "enclosure level(0x%04x),connector name(%s)\n", ++ sas_device->enclosure_level, ++ sas_device->connector_name); ++ ++ sas_device_put(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ } ++} ++ ++/** ++ * scsih_abort_mpt2sas - eh threads main abort routine ++ * @scmd: pointer to scsi command object ++ * ++ * Returns SUCCESS if command aborted else FAILED ++ */ ++int ++scsih_abort_mpt2sas(struct scsi_cmnd *scmd) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ u16 smid; ++ u16 handle; ++ int r; ++ ++ sdev_printk(KERN_INFO, scmd->device, ++ "attempting task abort! scmd(%p)\n", scmd); ++ _scsih_tm_display_info(ioc, scmd); ++ ++ sas_device_priv_data = scmd->device->hostdata; ++ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { ++ sdev_printk(KERN_INFO, scmd->device, ++ "device been deleted! scmd(%p)\n", scmd); ++ scmd->result = DID_NO_CONNECT << 16; ++ scmd->scsi_done(scmd); ++ r = SUCCESS; ++ goto out; ++ } ++ ++ /* search for the command */ ++ smid = _scsih_scsi_lookup_find_by_scmd(ioc, scmd); ++ if (!smid) { ++ scmd->result = DID_RESET << 16; ++ r = SUCCESS; ++ goto out; ++ } ++ ++ /* for hidden raid components and volumes this is not supported */ ++ if (sas_device_priv_data->sas_target->flags & ++ MPT_TARGET_FLAGS_RAID_COMPONENT || ++ sas_device_priv_data->sas_target->flags & MPT_TARGET_FLAGS_VOLUME) { ++ scmd->result = DID_RESET << 16; ++ r = FAILED; ++ goto out; ++ } ++ ++ mpt2sas_halt_firmware(ioc); ++ ++ handle = sas_device_priv_data->sas_target->handle; ++ r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, ++ scmd->device->id, scmd->device->lun, ++ MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, TM_MUTEX_ON); ++ ++ out: ++ sdev_printk(KERN_INFO, scmd->device, "task abort: %s scmd(%p)\n", ++ ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); ++ return r; ++} ++ ++/** ++ * scsih_dev_reset_mpt2sas - eh threads main device reset routine ++ * @scmd: pointer to scsi command object ++ * ++ * Returns SUCCESS if command aborted else FAILED ++ */ ++int ++scsih_dev_reset_mpt2sas(struct scsi_cmnd *scmd) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct _sas_device *sas_device = NULL; ++ u16 handle; ++ int r; ++ ++ struct scsi_target *starget = scmd->device->sdev_target; ++ struct MPT3SAS_TARGET *target_priv_data = starget->hostdata; ++ ++ sdev_printk(KERN_INFO, scmd->device, ++ "attempting device reset! scmd(%p)\n", scmd); ++ _scsih_tm_display_info(ioc, scmd); ++ ++ sas_device_priv_data = scmd->device->hostdata; ++ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { ++ sdev_printk(KERN_INFO, scmd->device, ++ "device been deleted! scmd(%p)\n", scmd); ++ scmd->result = DID_NO_CONNECT << 16; ++ scmd->scsi_done(scmd); ++ r = SUCCESS; ++ goto out; ++ } ++ ++ /* for hidden raid components obtain the volume_handle */ ++ handle = 0; ++ if (sas_device_priv_data->sas_target->flags & ++ MPT_TARGET_FLAGS_RAID_COMPONENT) { ++ sas_device = mpt2sas_get_sdev_from_target(ioc, ++ target_priv_data); ++ if (sas_device) ++ handle = sas_device->volume_handle; ++ } else ++ handle = sas_device_priv_data->sas_target->handle; ++ ++ if (!handle) { ++ scmd->result = DID_RESET << 16; ++ r = FAILED; ++ goto out; ++ } ++ ++ r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, ++ scmd->device->id, scmd->device->lun, ++ MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 30, TM_MUTEX_ON); ++ ++ out: ++ sdev_printk(KERN_INFO, scmd->device, "device reset: %s scmd(%p)\n", ++ ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); ++ ++ if (sas_device) ++ sas_device_put(sas_device); ++ ++ return r; ++} ++ ++/** ++ * scsih_target_reset_mpt2sas - eh threads main target reset routine ++ * @scmd: pointer to scsi command object ++ * ++ * Returns SUCCESS if command aborted else FAILED ++ */ ++int ++scsih_target_reset_mpt2sas(struct scsi_cmnd *scmd) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct _sas_device *sas_device = NULL; ++ u16 handle; ++ int r; ++ struct scsi_target *starget = scmd->device->sdev_target; ++ struct MPT3SAS_TARGET *target_priv_data = starget->hostdata; ++ ++ starget_printk(KERN_INFO, starget, "attempting target reset! scmd(%p)\n", ++ scmd); ++ _scsih_tm_display_info(ioc, scmd); ++ ++ sas_device_priv_data = scmd->device->hostdata; ++ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { ++ starget_printk(KERN_INFO, starget, "target been deleted! scmd(%p)\n", ++ scmd); ++ scmd->result = DID_NO_CONNECT << 16; ++ scmd->scsi_done(scmd); ++ r = SUCCESS; ++ goto out; ++ } ++ ++ /* for hidden raid components obtain the volume_handle */ ++ handle = 0; ++ if (sas_device_priv_data->sas_target->flags & ++ MPT_TARGET_FLAGS_RAID_COMPONENT) { ++ sas_device = mpt2sas_get_sdev_from_target(ioc, ++ target_priv_data); ++ if (sas_device) ++ handle = sas_device->volume_handle; ++ } else ++ handle = sas_device_priv_data->sas_target->handle; ++ ++ if (!handle) { ++ scmd->result = DID_RESET << 16; ++ r = FAILED; ++ goto out; ++ } ++ ++ r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, ++ scmd->device->id, 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, ++ 30, TM_MUTEX_ON); ++ ++ out: ++ starget_printk(KERN_INFO, starget, "target reset: %s scmd(%p)\n", ++ ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); ++ ++ if (sas_device) ++ sas_device_put(sas_device); ++ ++ return r; ++} ++ ++ ++/** ++ * scsih_host_reset_mpt2sas - eh threads main host reset routine ++ * @scmd: pointer to scsi command object ++ * ++ * Returns SUCCESS if command aborted else FAILED ++ */ ++int ++scsih_host_reset_mpt2sas(struct scsi_cmnd *scmd) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); ++ int r, retval; ++ ++ pr_info(MPT3SAS_FMT "attempting host reset! scmd(%p)\n", ++ ioc->name, scmd); ++ scsi_print_command(scmd); ++ ++ if (ioc->is_driver_loading) { ++ pr_info(MPT3SAS_FMT "Blocking the host reset\n", ++ ioc->name); ++ r = FAILED; ++ goto out; ++ } ++ ++ retval = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ r = (retval < 0) ? FAILED : SUCCESS; ++out: ++ pr_info(MPT3SAS_FMT "host reset: %s scmd(%p)\n", ++ ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); ++ ++ return r; ++} ++ ++/** ++ * _scsih_fw_event_add - insert and queue up fw_event ++ * @ioc: per adapter object ++ * @fw_event: object describing the event ++ * Context: This function will acquire ioc->fw_event_lock. ++ * ++ * This adds the firmware event object into link list, then queues it up to ++ * be processed from user context. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_fw_event_add(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) ++{ ++ unsigned long flags; ++ ++ if (ioc->firmware_event_thread == NULL) ++ return; ++ ++ spin_lock_irqsave(&ioc->fw_event_lock, flags); ++ fw_event_work_get(fw_event); ++ INIT_LIST_HEAD(&fw_event->list); ++ list_add_tail(&fw_event->list, &ioc->fw_event_list); ++ INIT_WORK(&fw_event->work, _firmware_event_work); ++ fw_event_work_get(fw_event); ++ queue_work(ioc->firmware_event_thread, &fw_event->work); ++ spin_unlock_irqrestore(&ioc->fw_event_lock, flags); ++} ++ ++/** ++ * _scsih_fw_event_del_from_list - delete fw_event from the list ++ * @ioc: per adapter object ++ * @fw_event: object describing the event ++ * Context: This function will acquire ioc->fw_event_lock. ++ * ++ * If the fw_event is on the fw_event_list, remove it and do a put. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_fw_event_del_from_list(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work ++ *fw_event) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->fw_event_lock, flags); ++ if (!list_empty(&fw_event->list)) { ++ list_del_init(&fw_event->list); ++ fw_event_work_put(fw_event); ++ } ++ spin_unlock_irqrestore(&ioc->fw_event_lock, flags); ++} ++ ++ ++ /** ++ * mpt2sas_send_trigger_data_event - send event for processing trigger data ++ * @ioc: per adapter object ++ * @event_data: trigger event data ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_send_trigger_data_event(struct MPT3SAS_ADAPTER *ioc, ++ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data) ++{ ++ struct fw_event_work *fw_event; ++ u16 sz; ++ ++ if (ioc->is_driver_loading) ++ return; ++ sz = sizeof(*event_data); ++ fw_event = alloc_fw_event_work(sz); ++ if (!fw_event) ++ return; ++ fw_event->event = MPT3SAS_PROCESS_TRIGGER_DIAG; ++ fw_event->ioc = ioc; ++ memcpy(fw_event->event_data, event_data, sizeof(*event_data)); ++ _scsih_fw_event_add(ioc, fw_event); ++ fw_event_work_put(fw_event); ++} ++ ++/** ++ * _scsih_error_recovery_delete_devices - remove devices not responding ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_error_recovery_delete_devices(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct fw_event_work *fw_event; ++ ++ if (ioc->is_driver_loading) ++ return; ++ fw_event = alloc_fw_event_work(0); ++ if (!fw_event) ++ return; ++ fw_event->event = MPT3SAS_REMOVE_UNRESPONDING_DEVICES; ++ fw_event->ioc = ioc; ++ _scsih_fw_event_add(ioc, fw_event); ++ fw_event_work_put(fw_event); ++} ++ ++/** ++ * mpt2sas_port_enable_complete - port enable completed (fake event) ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct fw_event_work *fw_event; ++ ++ fw_event = alloc_fw_event_work(0); ++ if (!fw_event) ++ return; ++ fw_event->event = MPT3SAS_PORT_ENABLE_COMPLETE; ++ fw_event->ioc = ioc; ++ _scsih_fw_event_add(ioc, fw_event); ++ fw_event_work_put(fw_event); ++} ++ ++static struct fw_event_work *dequeue_next_fw_event(struct MPT3SAS_ADAPTER *ioc) ++{ ++ unsigned long flags; ++ struct fw_event_work *fw_event = NULL; ++ ++ spin_lock_irqsave(&ioc->fw_event_lock, flags); ++ if (!list_empty(&ioc->fw_event_list)) { ++ fw_event = list_first_entry(&ioc->fw_event_list, ++ struct fw_event_work, list); ++ list_del_init(&fw_event->list); ++ } ++ spin_unlock_irqrestore(&ioc->fw_event_lock, flags); ++ ++ return fw_event; ++} ++ ++/** ++ * _scsih_fw_event_cleanup_queue - cleanup event queue ++ * @ioc: per adapter object ++ * ++ * Walk the firmware event queue, either killing timers, or waiting ++ * for outstanding events to complete ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_fw_event_cleanup_queue(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct fw_event_work *fw_event; ++ ++ if (list_empty(&ioc->fw_event_list) || ++ !ioc->firmware_event_thread || in_interrupt()) ++ return; ++ ++ while ((fw_event = dequeue_next_fw_event(ioc))) { ++ /* ++ * Wait on the fw_event to complete. If this returns 1, then ++ * the event was never executed, and we need a put for the ++ * reference the work had on the fw_event. ++ * ++ * If it did execute, we wait for it to finish, and the put will ++ * happen from _firmware_event_work() ++ */ ++ if (cancel_work_sync(&fw_event->work)) ++ fw_event_work_put(fw_event); ++ ++ fw_event_work_put(fw_event); ++ } ++} ++ ++/** ++ * _scsih_internal_device_block - block the sdev device ++ * @sdev: per device object ++ * @sas_device_priv_data : per device driver private data ++ * ++ * make sure device is blocked without error, if not ++ * print an error ++ */ ++static void ++_scsih_internal_device_block(struct scsi_device *sdev, ++ struct MPT3SAS_DEVICE *sas_device_priv_data) ++{ ++ int r = 0; ++ ++ sdev_printk(KERN_INFO, sdev, "device_block, handle(0x%04x)\n", ++ sas_device_priv_data->sas_target->handle); ++ sas_device_priv_data->block = 1; ++ ++ r = scsi_internal_device_block(sdev); ++ if (r == -EINVAL) ++ sdev_printk(KERN_WARNING, sdev, ++ "device_block failed with return(%d) for handle(0x%04x)\n", ++ sas_device_priv_data->sas_target->handle, r); ++} ++ ++/** ++ * _scsih_internal_device_unblock - unblock the sdev device ++ * @sdev: per device object ++ * @sas_device_priv_data : per device driver private data ++ * make sure device is unblocked without error, if not retry ++ * by blocking and then unblocking ++ */ ++ ++static void ++_scsih_internal_device_unblock(struct scsi_device *sdev, ++ struct MPT3SAS_DEVICE *sas_device_priv_data) ++{ ++ int r = 0; ++ ++ sdev_printk(KERN_WARNING, sdev, "device_unblock and setting to running, " ++ "handle(0x%04x)\n", sas_device_priv_data->sas_target->handle); ++ sas_device_priv_data->block = 0; ++ r = scsi_internal_device_unblock(sdev, SDEV_RUNNING); ++ if (r == -EINVAL) { ++ /* The device has been set to SDEV_RUNNING by SD layer during ++ * device addition but the request queue is still stopped by ++ * our earlier block call. We need to perform a block again ++ * to get the device to SDEV_BLOCK and then to SDEV_RUNNING */ ++ ++ sdev_printk(KERN_WARNING, sdev, ++ "device_unblock failed with return(%d) for handle(0x%04x) " ++ "performing a block followed by an unblock\n", ++ sas_device_priv_data->sas_target->handle, r); ++ sas_device_priv_data->block = 1; ++ r = scsi_internal_device_block(sdev); ++ if (r) ++ sdev_printk(KERN_WARNING, sdev, "retried device_block " ++ "failed with return(%d) for handle(0x%04x)\n", ++ sas_device_priv_data->sas_target->handle, r); ++ ++ sas_device_priv_data->block = 0; ++ r = scsi_internal_device_unblock(sdev, SDEV_RUNNING); ++ if (r) ++ sdev_printk(KERN_WARNING, sdev, "retried device_unblock" ++ " failed with return(%d) for handle(0x%04x)\n", ++ sas_device_priv_data->sas_target->handle, r); ++ } ++} ++ ++/** ++ * _scsih_ublock_io_all_device - unblock every device ++ * @ioc: per adapter object ++ * ++ * change the device state from block to running ++ */ ++static void ++_scsih_ublock_io_all_device(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_device *sdev; ++ ++ shost_for_each_device(sdev, ioc->shost) { ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data) ++ continue; ++ if (!sas_device_priv_data->block) ++ continue; ++ ++ dewtprintk(ioc, sdev_printk(KERN_INFO, sdev, ++ "device_running, handle(0x%04x)\n", ++ sas_device_priv_data->sas_target->handle)); ++ _scsih_internal_device_unblock(sdev, sas_device_priv_data); ++ } ++} ++ ++ ++/** ++ * _scsih_ublock_io_device - prepare device to be deleted ++ * @ioc: per adapter object ++ * @sas_addr: sas address ++ * ++ * unblock then put device in offline state ++ */ ++static void ++_scsih_ublock_io_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address) ++{ ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_device *sdev; ++ ++ shost_for_each_device(sdev, ioc->shost) { ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data) ++ continue; ++ if (sas_device_priv_data->sas_target->sas_address ++ != sas_address) ++ continue; ++ if (sas_device_priv_data->block) ++ _scsih_internal_device_unblock(sdev, ++ sas_device_priv_data); ++ } ++} ++ ++/** ++ * _scsih_block_io_all_device - set the device state to SDEV_BLOCK ++ * @ioc: per adapter object ++ * @handle: device handle ++ * ++ * During device pull we need to appropiately set the sdev state. ++ */ ++static void ++_scsih_block_io_all_device(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_device *sdev; ++ ++ shost_for_each_device(sdev, ioc->shost) { ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data) ++ continue; ++ if (sas_device_priv_data->block) ++ continue; ++ if (sas_device_priv_data->ignore_delay_remove) { ++ sdev_printk(KERN_INFO, sdev, ++ "%s skip device_block for SES handle(0x%04x)\n", ++ __func__, sas_device_priv_data->sas_target->handle); ++ continue; ++ } ++ _scsih_internal_device_block(sdev, sas_device_priv_data); ++ } ++} ++ ++/** ++ * _scsih_block_io_device - set the device state to SDEV_BLOCK ++ * @ioc: per adapter object ++ * @handle: device handle ++ * ++ * During device pull we need to appropiately set the sdev state. ++ */ ++static void ++_scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_device *sdev; ++ struct _sas_device *sas_device; ++ ++ sas_device = mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (!sas_device) ++ return; ++ ++ shost_for_each_device(sdev, ioc->shost) { ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data) ++ continue; ++ if (sas_device_priv_data->sas_target->handle != handle) ++ continue; ++ if (sas_device_priv_data->block) ++ continue; ++ if (sas_device->pend_sas_rphy_add) ++ continue; ++ if (sas_device_priv_data->ignore_delay_remove) { ++ sdev_printk(KERN_INFO, sdev, ++ "%s skip device_block for SES handle(0x%04x)\n", ++ __func__, sas_device_priv_data->sas_target->handle); ++ continue; ++ } ++ _scsih_internal_device_block(sdev, sas_device_priv_data); ++ } ++ ++ sas_device_put(sas_device); ++} ++ ++/** ++ * _scsih_block_io_to_children_attached_to_ex ++ * @ioc: per adapter object ++ * @sas_expander: the sas_device object ++ * ++ * This routine set sdev state to SDEV_BLOCK for all devices ++ * attached to this expander. This function called when expander is ++ * pulled. ++ */ ++static void ++_scsih_block_io_to_children_attached_to_ex(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_node *sas_expander) ++{ ++ struct _sas_port *mpt2sas_port; ++ struct _sas_device *sas_device; ++ struct _sas_node *expander_sibling; ++ unsigned long flags; ++ ++ if (!sas_expander) ++ return; ++ ++ list_for_each_entry(mpt2sas_port, ++ &sas_expander->sas_port_list, port_list) { ++ if (mpt2sas_port->remote_identify.device_type == ++ SAS_END_DEVICE) { ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ mpt2sas_port->remote_identify.sas_address); ++ if (sas_device) { ++ set_bit(sas_device->handle, ++ ioc->blocking_handles); ++ sas_device_put(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ } ++ } ++ ++ list_for_each_entry(mpt2sas_port, ++ &sas_expander->sas_port_list, port_list) { ++ ++ if (mpt2sas_port->remote_identify.device_type == ++ SAS_EDGE_EXPANDER_DEVICE || ++ mpt2sas_port->remote_identify.device_type == ++ SAS_FANOUT_EXPANDER_DEVICE) { ++ expander_sibling = ++ mpt2sas_scsih_expander_find_by_sas_address( ++ ioc, mpt2sas_port->remote_identify.sas_address); ++ _scsih_block_io_to_children_attached_to_ex(ioc, ++ expander_sibling); ++ } ++ } ++} ++ ++/** ++ * _scsih_block_io_to_children_attached_directly ++ * @ioc: per adapter object ++ * @event_data: topology change event data ++ * ++ * This routine set sdev state to SDEV_BLOCK for all devices ++ * direct attached during device pull. ++ */ ++static void ++_scsih_block_io_to_children_attached_directly(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataSasTopologyChangeList_t *event_data) ++{ ++ int i; ++ u16 handle; ++ u16 reason_code; ++ ++ for (i = 0; i < event_data->NumEntries; i++) { ++ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); ++ if (!handle) ++ continue; ++ reason_code = event_data->PHY[i].PhyStatus & ++ MPI2_EVENT_SAS_TOPO_RC_MASK; ++ if (reason_code == MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING) ++ _scsih_block_io_device(ioc, handle); ++ } ++} ++ ++/** ++ * _scsih_tm_tr_send - send task management request ++ * @ioc: per adapter object ++ * @handle: device handle ++ * Context: interrupt time. ++ * ++ * This code is to initiate the device removal handshake protocol ++ * with controller firmware. This function will issue target reset ++ * using high priority request queue. It will send a sas iounit ++ * control request (MPI2_SAS_OP_REMOVE_DEVICE) from this completion. ++ * ++ * This is designed to send muliple task management request at the same ++ * time to the fifo. If the fifo is full, we will append the request, ++ * and process it in a future completion. ++ */ ++static void ++_scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ Mpi2SCSITaskManagementRequest_t *mpi_request; ++ u16 smid; ++ struct _sas_device *sas_device = NULL; ++ struct MPT3SAS_TARGET *sas_target_priv_data = NULL; ++ u64 sas_address = 0; ++ unsigned long flags; ++ struct _tr_list *delayed_tr; ++ u32 ioc_state; ++ ++ if (ioc->remove_host) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host has been removed: handle(0x%04x)\n", ++ __func__, ioc->name, handle)); ++ return; ++ } else if (ioc->pci_error_recovery) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host in pci error recovery: handle(0x%04x)\n", ++ __func__, ioc->name, ++ handle)); ++ return; ++ } ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host is not operational: handle(0x%04x)\n", ++ __func__, ioc->name, ++ handle)); ++ return; ++ } ++ ++ /* if PD, then return */ ++ if (test_bit(handle, ioc->pd_handles)) ++ return; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (sas_device && sas_device->starget && ++ sas_device->starget->hostdata) { ++ sas_target_priv_data = sas_device->starget->hostdata; ++ sas_target_priv_data->deleted = 1; ++ sas_address = sas_device->sas_address; ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ if (sas_target_priv_data) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "setting delete flag: handle(0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, handle, ++ (unsigned long long)sas_address)); ++ if (sas_device->enclosure_handle != 0) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "setting delete flag:enclosure logical id(0x%016llx)," ++ " slot(%d)\n", ioc->name, (unsigned long long) ++ sas_device->enclosure_logical_id, ++ sas_device->slot)); ++ if (sas_device->connector_name[0] != '\0') ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "setting delete flag: enclosure level(0x%04x)," ++ " connector name( %s)\n", ioc->name, ++ sas_device->enclosure_level, ++ sas_device->connector_name)); ++ _scsih_ublock_io_device(ioc, sas_address); ++ sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE; ++ } ++ ++ smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx); ++ if (!smid) { ++ delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); ++ if (!delayed_tr) ++ goto out; ++ INIT_LIST_HEAD(&delayed_tr->list); ++ delayed_tr->handle = handle; ++ list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list); ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "DELAYED:tr:handle(0x%04x), (open)\n", ++ ioc->name, handle)); ++ goto out; ++ } ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "tr_send:handle(0x%04x), (open), smid(%d), cb(%d)\n", ++ ioc->name, handle, smid, ++ ioc->tm_tr_cb_idx)); ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; ++ mpi_request->DevHandle = cpu_to_le16(handle); ++ mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; ++ mpt2sas_base_put_smid_hi_priority(ioc, smid, 0); ++ mpt2sas_trigger_master(ioc, MASTER_TRIGGER_DEVICE_REMOVAL); ++ ++out: ++ if (sas_device) ++ sas_device_put(sas_device); ++} ++ ++/** ++ * _scsih_tm_tr_complete - ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: interrupt time. ++ * ++ * This is the target reset completion routine. ++ * This code is part of the code to initiate the device removal ++ * handshake protocol with controller firmware. ++ * It will send a sas iounit control request (MPI2_SAS_OP_REMOVE_DEVICE) ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_scsih_tm_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply) ++{ ++ u16 handle; ++ Mpi2SCSITaskManagementRequest_t *mpi_request_tm; ++ Mpi2SCSITaskManagementReply_t *mpi_reply = ++ mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ Mpi2SasIoUnitControlRequest_t *mpi_request; ++ u16 smid_sas_ctrl; ++ u32 ioc_state; ++ struct _sc_list *delayed_sc; ++ ++ if (ioc->remove_host) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host has been removed\n", __func__, ioc->name)); ++ return 1; ++ } else if (ioc->pci_error_recovery) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host in pci error recovery\n", __func__, ++ ioc->name)); ++ return 1; ++ } ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host is not operational\n", __func__, ioc->name)); ++ return 1; ++ } ++ if (unlikely(!mpi_reply)) { ++ pr_err(MPT3SAS_FMT "mpi_reply not valid at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return 1; ++ } ++ mpi_request_tm = mpt2sas_base_get_msg_frame(ioc, smid); ++ handle = le16_to_cpu(mpi_request_tm->DevHandle); ++ if (handle != le16_to_cpu(mpi_reply->DevHandle)) { ++ dewtprintk(ioc, pr_err(MPT3SAS_FMT ++ "spurious interrupt: handle(0x%04x:0x%04x), smid(%d)!!!\n", ++ ioc->name, handle, ++ le16_to_cpu(mpi_reply->DevHandle), smid)); ++ return 0; ++ } ++ ++ mpt2sas_trigger_master(ioc, MASTER_TRIGGER_TASK_MANAGMENT); ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), " ++ "loginfo(0x%08x), completed(%d)\n", ioc->name, ++ handle, smid, le16_to_cpu(mpi_reply->IOCStatus), ++ le32_to_cpu(mpi_reply->IOCLogInfo), ++ le32_to_cpu(mpi_reply->TerminationCount))); ++ ++ smid_sas_ctrl = mpt2sas_base_get_smid(ioc, ioc->tm_sas_control_cb_idx); ++ if (!smid_sas_ctrl) { ++ delayed_sc = kzalloc(sizeof(*delayed_sc), GFP_ATOMIC); ++ if (!delayed_sc) ++ return _scsih_check_for_pending_tm(ioc, smid); ++ INIT_LIST_HEAD(&delayed_sc->list); ++ delayed_sc->handle = mpi_request_tm->DevHandle; ++ list_add_tail(&delayed_sc->list, &ioc->delayed_sc_list); ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "DELAYED:sc:handle(0x%04x), (open)\n", ++ ioc->name, handle)); ++ return _scsih_check_for_pending_tm(ioc, smid); ++ } ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "sc_send:handle(0x%04x), (open), smid(%d), cb(%d)\n", ++ ioc->name, handle, smid_sas_ctrl, ++ ioc->tm_sas_control_cb_idx)); ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid_sas_ctrl); ++ memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; ++ mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE; ++ mpi_request->DevHandle = mpi_request_tm->DevHandle; ++ mpt2sas_base_put_smid_default(ioc, smid_sas_ctrl); ++ ++ return _scsih_check_for_pending_tm(ioc, smid); ++} ++ ++ ++/** ++ * _scsih_sas_control_complete - completion routine ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: interrupt time. ++ * ++ * This is the sas iounit control completion routine. ++ * This code is part of the code to initiate the device removal ++ * handshake protocol with controller firmware. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_scsih_sas_control_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ u8 msix_index, u32 reply) ++{ ++ Mpi2SasIoUnitControlReply_t *mpi_reply = ++ mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ ++ if (likely(mpi_reply)) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "sc_complete:handle(0x%04x), (open) " ++ "smid(%d), ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply->DevHandle), smid, ++ le16_to_cpu(mpi_reply->IOCStatus), ++ le32_to_cpu(mpi_reply->IOCLogInfo))); ++ } else { ++ pr_err(MPT3SAS_FMT "mpi_reply not valid at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ } ++ return mpt2sas_check_for_pending_internal_cmds(ioc, smid); ++} ++ ++/** ++ * _scsih_tm_tr_volume_send - send target reset request for volumes ++ * @ioc: per adapter object ++ * @handle: device handle ++ * Context: interrupt time. ++ * ++ * This is designed to send muliple task management request at the same ++ * time to the fifo. If the fifo is full, we will append the request, ++ * and process it in a future completion. ++ */ ++static void ++_scsih_tm_tr_volume_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ Mpi2SCSITaskManagementRequest_t *mpi_request; ++ u16 smid; ++ struct _tr_list *delayed_tr; ++ ++ if (ioc->shost_recovery || ioc->remove_host || ++ ioc->pci_error_recovery) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host reset in progress!\n", ++ __func__, ioc->name)); ++ return; ++ } ++ ++ smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_volume_cb_idx); ++ if (!smid) { ++ delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); ++ if (!delayed_tr) ++ return; ++ INIT_LIST_HEAD(&delayed_tr->list); ++ delayed_tr->handle = handle; ++ list_add_tail(&delayed_tr->list, &ioc->delayed_tr_volume_list); ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "DELAYED:tr:handle(0x%04x), (open)\n", ++ ioc->name, handle)); ++ return; ++ } ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "tr_send:handle(0x%04x), (open), smid(%d), cb(%d)\n", ++ ioc->name, handle, smid, ++ ioc->tm_tr_volume_cb_idx)); ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; ++ mpi_request->DevHandle = cpu_to_le16(handle); ++ mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; ++ mpt2sas_base_put_smid_hi_priority(ioc, smid, 0); ++} ++ ++/** ++ * _scsih_tm_volume_tr_complete - target reset completion ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: interrupt time. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_scsih_tm_volume_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, ++ u8 msix_index, u32 reply) ++{ ++ u16 handle; ++ Mpi2SCSITaskManagementRequest_t *mpi_request_tm; ++ Mpi2SCSITaskManagementReply_t *mpi_reply = ++ mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ ++ if (ioc->shost_recovery || ioc->remove_host || ++ ioc->pci_error_recovery) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host reset in progress!\n", ++ __func__, ioc->name)); ++ return 1; ++ } ++ if (unlikely(!mpi_reply)) { ++ pr_err(MPT3SAS_FMT "mpi_reply not valid at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return 1; ++ } ++ ++ mpi_request_tm = mpt2sas_base_get_msg_frame(ioc, smid); ++ handle = le16_to_cpu(mpi_request_tm->DevHandle); ++ if (handle != le16_to_cpu(mpi_reply->DevHandle)) { ++ dewtprintk(ioc, pr_err(MPT3SAS_FMT ++ "spurious interrupt: handle(0x%04x:0x%04x), smid(%d)!!!\n", ++ ioc->name, handle, ++ le16_to_cpu(mpi_reply->DevHandle), smid)); ++ return 0; ++ } ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), " ++ "loginfo(0x%08x), completed(%d)\n", ioc->name, ++ handle, smid, le16_to_cpu(mpi_reply->IOCStatus), ++ le32_to_cpu(mpi_reply->IOCLogInfo), ++ le32_to_cpu(mpi_reply->TerminationCount))); ++ ++ return _scsih_check_for_pending_tm(ioc, smid); ++} ++ ++/** ++ * _scsih_issue_delayed_event_ack_mpt2sas - issue delayed Event ACK messages ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @event: Event ID ++ * @event_context: used to track events uniquely ++ * ++ * Context - processed in interrupt context. ++ */ ++void ++_scsih_issue_delayed_event_ack_mpt2sas(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 event, ++ u32 event_context) ++{ ++ Mpi2EventAckRequest_t *ack_request; ++ int i = smid - ioc->internal_smid; ++ unsigned long flags; ++ ++ /* Without releasing the smid just update the ++ * call back index and reuse the same smid for ++ * processing this delayed request ++ */ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ ioc->internal_lookup[i].cb_idx = ioc->base_cb_idx; ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "EVENT ACK: event(0x%04x), smid(%d), cb(%d)\n", ++ ioc->name, le16_to_cpu(event), smid, ++ ioc->base_cb_idx)); ++ ack_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ memset(ack_request, 0, sizeof(Mpi2EventAckRequest_t)); ++ ack_request->Function = MPI2_FUNCTION_EVENT_ACK; ++ ack_request->Event = event; ++ ack_request->EventContext = event_context; ++ ack_request->VF_ID = 0; /* TODO */ ++ ack_request->VP_ID = 0; ++ mpt2sas_base_put_smid_default(ioc, smid); ++} ++ ++/** ++ * _scsih_issue_delayed_sas_io_unit_ctrl_mpt2sas - issue delayed ++ * sas_io_unit_ctrl messages ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @handle: device handle ++ * ++ * Context - processed in interrupt context. ++ */ ++void ++_scsih_issue_delayed_sas_io_unit_ctrl_mpt2sas(struct MPT3SAS_ADAPTER *ioc, ++ u16 smid, u16 handle) ++ { ++ Mpi2SasIoUnitControlRequest_t *mpi_request; ++ u32 ioc_state; ++ int i = smid - ioc->internal_smid; ++ unsigned long flags; ++ ++ if (ioc->remove_host) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host has been removed\n", ++ __func__, ioc->name)); ++ return; ++ } else if (ioc->pci_error_recovery) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host in pci error recovery\n", ++ __func__, ioc->name)); ++ return; ++ } ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: host is not operational\n", ++ __func__, ioc->name)); ++ return; ++ } ++ ++ /* Without releasing the smid just update the ++ * call back index and reuse the same smid for ++ * processing this delayed request ++ */ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ ioc->internal_lookup[i].cb_idx = ioc->tm_sas_control_cb_idx; ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "sc_send:handle(0x%04x), (open), smid(%d), cb(%d)\n", ++ ioc->name, le16_to_cpu(handle), smid, ++ ioc->tm_sas_control_cb_idx)); ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; ++ mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE; ++ mpi_request->DevHandle = handle; ++ mpt2sas_base_put_smid_default(ioc, smid); ++} ++ ++/** ++ * _scsih_check_for_pending_internal_cmds - check for pending internal messages ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Context: Executed in interrupt context ++ * ++ * This will check delayed internal messages list, and process the ++ * next request. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_check_for_pending_internal_cmds(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ struct _sc_list *delayed_sc; ++ struct _event_ack_list *delayed_event_ack; ++ ++ if (!list_empty(&ioc->delayed_event_ack_list)) { ++ delayed_event_ack = list_entry(ioc->delayed_event_ack_list.next, ++ struct _event_ack_list, list); ++ _scsih_issue_delayed_event_ack_mpt2sas(ioc, smid, ++ delayed_event_ack->Event, delayed_event_ack->EventContext); ++ list_del(&delayed_event_ack->list); ++ kfree(delayed_event_ack); ++ return 0; ++ } ++ ++ if (!list_empty(&ioc->delayed_sc_list)) { ++ delayed_sc = list_entry(ioc->delayed_sc_list.next, ++ struct _sc_list, list); ++ _scsih_issue_delayed_sas_io_unit_ctrl_mpt2sas(ioc, smid, ++ delayed_sc->handle); ++ list_del(&delayed_sc->list); ++ kfree(delayed_sc); ++ return 0; ++ } ++ return 1; ++} ++ ++/** ++ * _scsih_check_for_pending_tm - check for pending task management ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * This will check delayed target reset list, and feed the ++ * next reqeust. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_scsih_check_for_pending_tm(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ struct _tr_list *delayed_tr; ++ ++ if (!list_empty(&ioc->delayed_tr_volume_list)) { ++ delayed_tr = list_entry(ioc->delayed_tr_volume_list.next, ++ struct _tr_list, list); ++ mpt2sas_base_free_smid(ioc, smid); ++ _scsih_tm_tr_volume_send(ioc, delayed_tr->handle); ++ list_del(&delayed_tr->list); ++ kfree(delayed_tr); ++ return 0; ++ } ++ ++ if (!list_empty(&ioc->delayed_tr_list)) { ++ delayed_tr = list_entry(ioc->delayed_tr_list.next, ++ struct _tr_list, list); ++ mpt2sas_base_free_smid(ioc, smid); ++ _scsih_tm_tr_send(ioc, delayed_tr->handle); ++ list_del(&delayed_tr->list); ++ kfree(delayed_tr); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++/** ++ * _scsih_check_topo_delete_events - sanity check on topo events ++ * @ioc: per adapter object ++ * @event_data: the event data payload ++ * ++ * This routine added to better handle cable breaker. ++ * ++ * This handles the case where driver receives multiple expander ++ * add and delete events in a single shot. When there is a delete event ++ * the routine will void any pending add events waiting in the event queue. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_check_topo_delete_events(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataSasTopologyChangeList_t *event_data) ++{ ++ struct fw_event_work *fw_event; ++ Mpi2EventDataSasTopologyChangeList_t *local_event_data; ++ u16 expander_handle; ++ struct _sas_node *sas_expander; ++ unsigned long flags; ++ int i, reason_code; ++ u16 handle; ++ ++ for (i = 0 ; i < event_data->NumEntries; i++) { ++ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); ++ if (!handle) ++ continue; ++ reason_code = event_data->PHY[i].PhyStatus & ++ MPI2_EVENT_SAS_TOPO_RC_MASK; ++ if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING) ++ _scsih_tm_tr_send(ioc, handle); ++ } ++ ++ expander_handle = le16_to_cpu(event_data->ExpanderDevHandle); ++ if (expander_handle < ioc->sas_hba.num_phys) { ++ _scsih_block_io_to_children_attached_directly(ioc, event_data); ++ return; ++ } ++ if (event_data->ExpStatus == ++ MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING) { ++ /* put expander attached devices into blocking state */ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, ++ expander_handle); ++ _scsih_block_io_to_children_attached_to_ex(ioc, sas_expander); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ do { ++ handle = find_first_bit(ioc->blocking_handles, ++ ioc->facts.MaxDevHandle); ++ if (handle < ioc->facts.MaxDevHandle) ++ _scsih_block_io_device(ioc, handle); ++ } while (test_and_clear_bit(handle, ioc->blocking_handles)); ++ } else if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_RESPONDING) ++ _scsih_block_io_to_children_attached_directly(ioc, event_data); ++ ++ if (event_data->ExpStatus != MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING) ++ return; ++ ++ /* mark ignore flag for pending events */ ++ spin_lock_irqsave(&ioc->fw_event_lock, flags); ++ list_for_each_entry(fw_event, &ioc->fw_event_list, list) { ++ if (fw_event->event != MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST || ++ fw_event->ignore) ++ continue; ++ local_event_data = (Mpi2EventDataSasTopologyChangeList_t *) ++ fw_event->event_data; ++ if (local_event_data->ExpStatus == ++ MPI2_EVENT_SAS_TOPO_ES_ADDED || ++ local_event_data->ExpStatus == ++ MPI2_EVENT_SAS_TOPO_ES_RESPONDING) { ++ if (le16_to_cpu(local_event_data->ExpanderDevHandle) == ++ expander_handle) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "setting ignoring flag\n", ioc->name)); ++ fw_event->ignore = 1; ++ } ++ } ++ } ++ spin_unlock_irqrestore(&ioc->fw_event_lock, flags); ++} ++ ++/** ++ * _scsih_set_volume_delete_flag - setting volume delete flag ++ * @ioc: per adapter object ++ * @handle: device handle ++ * ++ * This returns nothing. ++ */ ++static void ++_scsih_set_volume_delete_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _raid_device *raid_device; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = mpt2sas_raid_device_find_by_handle(ioc, handle); ++ if (raid_device && raid_device->starget && ++ raid_device->starget->hostdata) { ++ sas_target_priv_data = ++ raid_device->starget->hostdata; ++ sas_target_priv_data->deleted = 1; ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "setting delete flag: handle(0x%04x), " ++ "wwid(0x%016llx)\n", ioc->name, handle, ++ (unsigned long long) raid_device->wwid)); ++ } ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++} ++ ++/** ++ * _scsih_set_volume_handle_for_tr - set handle for target reset to volume ++ * @handle: input handle ++ * @a: handle for volume a ++ * @b: handle for volume b ++ * ++ * IR firmware only supports two raid volumes. The purpose of this ++ * routine is to set the volume handle in either a or b. When the given ++ * input handle is non-zero, or when a and b have not been set before. ++ */ ++static void ++_scsih_set_volume_handle_for_tr(u16 handle, u16 *a, u16 *b) ++{ ++ if (!handle || handle == *a || handle == *b) ++ return; ++ if (!*a) ++ *a = handle; ++ else if (!*b) ++ *b = handle; ++} ++ ++/** ++ * _scsih_check_ir_config_unhide_events - check for UNHIDE events ++ * @ioc: per adapter object ++ * @event_data: the event data payload ++ * Context: interrupt time. ++ * ++ * This routine will send target reset to volume, followed by target ++ * resets to the PDs. This is called when a PD has been removed, or ++ * volume has been deleted or removed. When the target reset is sent ++ * to volume, the PD target resets need to be queued to start upon ++ * completion of the volume target reset. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_check_ir_config_unhide_events(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataIrConfigChangeList_t *event_data) ++{ ++ Mpi2EventIrConfigElement_t *element; ++ int i; ++ u16 handle, volume_handle, a, b; ++ struct _tr_list *delayed_tr; ++ ++ a = 0; ++ b = 0; ++ ++ if (ioc->is_warpdrive) ++ return; ++ ++ /* Volume Resets for Deleted or Removed */ ++ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; ++ for (i = 0; i < event_data->NumElements; i++, element++) { ++ if (le32_to_cpu(event_data->Flags) & ++ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ++ continue; ++ if (element->ReasonCode == ++ MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED || ++ element->ReasonCode == ++ MPI2_EVENT_IR_CHANGE_RC_REMOVED) { ++ volume_handle = le16_to_cpu(element->VolDevHandle); ++ _scsih_set_volume_delete_flag(ioc, volume_handle); ++ _scsih_set_volume_handle_for_tr(volume_handle, &a, &b); ++ } ++ } ++ ++ /* Volume Resets for UNHIDE events */ ++ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; ++ for (i = 0; i < event_data->NumElements; i++, element++) { ++ if (le32_to_cpu(event_data->Flags) & ++ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ++ continue; ++ if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_UNHIDE) { ++ volume_handle = le16_to_cpu(element->VolDevHandle); ++ _scsih_set_volume_handle_for_tr(volume_handle, &a, &b); ++ } ++ } ++ ++ if (a) ++ _scsih_tm_tr_volume_send(ioc, a); ++ if (b) ++ _scsih_tm_tr_volume_send(ioc, b); ++ ++ /* PD target resets */ ++ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; ++ for (i = 0; i < event_data->NumElements; i++, element++) { ++ if (element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_UNHIDE) ++ continue; ++ handle = le16_to_cpu(element->PhysDiskDevHandle); ++ volume_handle = le16_to_cpu(element->VolDevHandle); ++ clear_bit(handle, ioc->pd_handles); ++ if (!volume_handle) ++ _scsih_tm_tr_send(ioc, handle); ++ else if (volume_handle == a || volume_handle == b) { ++ delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); ++ BUG_ON(!delayed_tr); ++ INIT_LIST_HEAD(&delayed_tr->list); ++ delayed_tr->handle = handle; ++ list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list); ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "DELAYED:tr:handle(0x%04x), (open)\n", ioc->name, ++ handle)); ++ } else ++ _scsih_tm_tr_send(ioc, handle); ++ } ++} ++ ++ ++/** ++ * _scsih_check_volume_delete_events - set delete flag for volumes ++ * @ioc: per adapter object ++ * @event_data: the event data payload ++ * Context: interrupt time. ++ * ++ * This will handle the case when the cable connected to entire volume is ++ * pulled. We will take care of setting the deleted flag so normal IO will ++ * not be sent. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_check_volume_delete_events(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataIrVolume_t *event_data) ++{ ++ u32 state; ++ ++ if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED) ++ return; ++ state = le32_to_cpu(event_data->NewValue); ++ if (state == MPI2_RAID_VOL_STATE_MISSING || state == ++ MPI2_RAID_VOL_STATE_FAILED) ++ _scsih_set_volume_delete_flag(ioc, ++ le16_to_cpu(event_data->VolDevHandle)); ++} ++ ++/** ++ * _scsih_temp_threshold_events - display temperature threshold exceeded events ++ * @ioc: per adapter object ++ * @event_data: the temp threshold event data ++ * Context: interrupt time. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_temp_threshold_events(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataTemperature_t *event_data) ++{ ++ if (ioc->temp_sensors_count >= event_data->SensorNum) { ++ pr_err(MPT3SAS_FMT "Temperature Threshold flags %s%s%s%s" ++ " exceeded for Sensor: %d !!!\n", ioc->name, ++ ((le16_to_cpu(event_data->Status) & 0x1) == 1) ? "0 " : " ", ++ ((le16_to_cpu(event_data->Status) & 0x2) == 2) ? "1 " : " ", ++ ((le16_to_cpu(event_data->Status) & 0x4) == 4) ? "2 " : " ", ++ ((le16_to_cpu(event_data->Status) & 0x8) == 8) ? "3 " : " ", ++ event_data->SensorNum); ++ pr_err(MPT3SAS_FMT "Current Temp In Celsius: %d\n", ++ ioc->name, event_data->CurrentTemperature); ++ } ++} ++ ++/** ++ * _scsih_flush_running_cmds - completing outstanding commands. ++ * @ioc: per adapter object ++ * ++ * The flushing out of all pending scmd commands following host reset, ++ * where all IO is dropped to the floor. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_flush_running_cmds(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct scsi_cmnd *scmd; ++ u16 smid; ++ u16 count = 0; ++ ++ for (smid = 1; smid <= ioc->scsiio_depth; smid++) { ++ scmd = _scsih_scsi_lookup_get_clear(ioc, smid); ++ if (!scmd) ++ continue; ++ count++; ++ mpt2sas_base_free_smid(ioc, smid); ++ scsi_dma_unmap(scmd); ++ if (ioc->pci_error_recovery) ++ scmd->result = DID_NO_CONNECT << 16; ++ else ++ scmd->result = DID_RESET << 16; ++ scmd->scsi_done(scmd); ++ } ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT "completing %d cmds\n", ++ ioc->name, count)); ++} ++ ++/** ++ * _scsih_setup_eedp - setup MPI request for EEDP transfer ++ * @ioc: per adapter object ++ * @scmd: pointer to scsi command object ++ * @mpi_request: pointer to the SCSI_IO reqest message frame ++ * ++ * Supporting protection 1 and 3. ++ * ++ * Returns nothing ++ */ ++static void ++_scsih_setup_eedp(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, ++ Mpi2SCSIIORequest_t *mpi_request) ++{ ++ u16 eedp_flags; ++ unsigned char prot_op = scsi_get_prot_op(scmd); ++ unsigned char prot_type = scsi_get_prot_type(scmd); ++ Mpi25SCSIIORequest_t *mpi_request_3v = ++ (Mpi25SCSIIORequest_t *)mpi_request; ++ ++ if (prot_type == SCSI_PROT_DIF_TYPE0 || prot_op == SCSI_PROT_NORMAL) ++ return; ++ ++ if (prot_op == SCSI_PROT_READ_STRIP) ++ eedp_flags = MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP; ++ else if (prot_op == SCSI_PROT_WRITE_INSERT) ++ eedp_flags = MPI2_SCSIIO_EEDPFLAGS_INSERT_OP; ++ else ++ return; ++ ++ switch (prot_type) { ++ case SCSI_PROT_DIF_TYPE1: ++ case SCSI_PROT_DIF_TYPE2: ++ ++ /* ++ * enable ref/guard checking ++ * auto increment ref tag ++ */ ++ eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG | ++ MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG | ++ MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; ++ mpi_request->CDB.EEDP32.PrimaryReferenceTag = ++ cpu_to_be32(scsi_get_lba(scmd)); ++ break; ++ ++ case SCSI_PROT_DIF_TYPE3: ++ ++ /* ++ * enable guard checking ++ */ ++ eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; ++ ++ break; ++ } ++ ++ mpi_request_3v->EEDPBlockSize = ++ cpu_to_le16(scmd->device->sector_size); ++ mpi_request->EEDPFlags = cpu_to_le16(eedp_flags); ++} ++ ++/** ++ * _scsih_eedp_error_handling - return sense code for EEDP errors ++ * @scmd: pointer to scsi command object ++ * @ioc_status: ioc status ++ * ++ * Returns nothing ++ */ ++static void ++_scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status) ++{ ++ u8 ascq; ++ ++ switch (ioc_status) { ++ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: ++ ascq = 0x01; ++ break; ++ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR: ++ ascq = 0x02; ++ break; ++ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR: ++ ascq = 0x03; ++ break; ++ default: ++ ascq = 0x00; ++ break; ++ } ++ scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST, 0x10, ++ ascq); ++ scmd->result = DRIVER_SENSE << 24 | (DID_ABORT << 16) | ++ SAM_STAT_CHECK_CONDITION; ++} ++ ++ ++ ++/** ++ * scsih_qcmd_mpt2sas - main scsi request entry point ++ * @scmd: pointer to scsi command object ++ * @done: function pointer to be invoked on completion ++ * ++ * The callback index is set inside `ioc->scsi_io_cb_idx`. ++ * ++ * Returns 0 on success. If there's a failure, return either: ++ * SCSI_MLQUEUE_DEVICE_BUSY if the device queue is full, or ++ * SCSI_MLQUEUE_HOST_BUSY if the entire host queue is full ++ */ ++int ++scsih_qcmd_mpt2sas(struct Scsi_Host *shost, struct scsi_cmnd *scmd) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct _raid_device *raid_device; ++ Mpi2SCSIIORequest_t *mpi_request; ++ u32 mpi_control; ++ u16 smid; ++ u16 handle; ++ ++ if (ioc->logging_level & MPT_DEBUG_SCSI) ++ scsi_print_command(scmd); ++ ++ sas_device_priv_data = scmd->device->hostdata; ++ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { ++ scmd->result = DID_NO_CONNECT << 16; ++ scmd->scsi_done(scmd); ++ return 0; ++ } ++ ++ if (ioc->pci_error_recovery || ioc->remove_host) { ++ scmd->result = DID_NO_CONNECT << 16; ++ scmd->scsi_done(scmd); ++ return 0; ++ } ++ ++ sas_target_priv_data = sas_device_priv_data->sas_target; ++ ++ /* invalid device handle */ ++ handle = sas_target_priv_data->handle; ++ if (handle == MPT3SAS_INVALID_DEVICE_HANDLE) { ++ scmd->result = DID_NO_CONNECT << 16; ++ scmd->scsi_done(scmd); ++ return 0; ++ } ++ ++ ++ /* host recovery or link resets sent via IOCTLs */ ++ if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress) ++ return SCSI_MLQUEUE_HOST_BUSY; ++ ++ /* device has been deleted */ ++ else if (sas_target_priv_data->deleted) { ++ scmd->result = DID_NO_CONNECT << 16; ++ scmd->scsi_done(scmd); ++ return 0; ++ /* device busy with task managment */ ++ } else if (sas_target_priv_data->tm_busy || ++ sas_device_priv_data->block) ++ return SCSI_MLQUEUE_DEVICE_BUSY; ++ ++ if (scmd->sc_data_direction == DMA_FROM_DEVICE) ++ mpi_control = MPI2_SCSIIO_CONTROL_READ; ++ else if (scmd->sc_data_direction == DMA_TO_DEVICE) ++ mpi_control = MPI2_SCSIIO_CONTROL_WRITE; ++ else ++ mpi_control = MPI2_SCSIIO_CONTROL_NODATATRANSFER; ++ ++ /* set tags */ ++ if (!(sas_device_priv_data->flags & MPT_DEVICE_FLAGS_INIT)) { ++ if (scmd->device->tagged_supported) { ++ if (scmd->device->ordered_tags) ++ mpi_control |= MPI2_SCSIIO_CONTROL_ORDEREDQ; ++ else ++ mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; ++ } else ++ mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; ++ } else ++ mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; ++ ++ /* Make sure Device is not raid volume. ++ * We do not expose raid functionality to upper layer for warpdrive. ++ */ ++ if (!ioc->is_warpdrive && !scsih_is_raid_mpt2sas(&scmd->device->sdev_gendev) ++ && sas_is_tlr_enabled(scmd->device) && scmd->cmd_len != 32) ++ mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON; ++ ++ smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->scsi_io_cb_idx, scmd); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ goto out; ++ } ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ memset(mpi_request, 0, sizeof(Mpi2SCSIIORequest_t)); ++ _scsih_setup_eedp(ioc, scmd, mpi_request); ++ ++ if (scmd->cmd_len == 32) ++ mpi_control |= 4 << MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT; ++ mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; ++ if (sas_device_priv_data->sas_target->flags & ++ MPT_TARGET_FLAGS_RAID_COMPONENT) ++ mpi_request->Function = MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH; ++ else ++ mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; ++ mpi_request->DevHandle = cpu_to_le16(handle); ++ mpi_request->DataLength = cpu_to_le32(scsi_bufflen(scmd)); ++ mpi_request->Control = cpu_to_le32(mpi_control); ++ mpi_request->IoFlags = cpu_to_le16(scmd->cmd_len); ++ mpi_request->MsgFlags = MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR; ++ mpi_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE; ++ mpi_request->SenseBufferLowAddress = ++ mpt2sas_base_get_sense_buffer_dma(ioc, smid); ++ mpi_request->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4; ++ int_to_scsilun(sas_device_priv_data->lun, (struct scsi_lun *) ++ mpi_request->LUN); ++ memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len); ++ ++ if (mpi_request->DataLength) { ++ if (ioc->build_sg_scmd(ioc, scmd, smid)) { ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ } else ++ ioc->build_zero_len_sge(ioc, &mpi_request->SGL); ++ ++ raid_device = sas_target_priv_data->raid_device; ++ if (raid_device && raid_device->direct_io_enabled) ++ mpt2sas_setup_direct_io(ioc, scmd, raid_device, mpi_request, ++ smid); ++ ++ if (likely(mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)) { ++ if (sas_target_priv_data->flags & MPT_TARGET_FASTPATH_IO) { ++ mpi_request->IoFlags = cpu_to_le16(scmd->cmd_len | ++ MPI25_SCSIIO_IOFLAGS_FAST_PATH); ++ mpt2sas_base_put_smid_fast_path(ioc, smid, handle); ++ } else ++ mpt2sas_base_put_smid_scsi_io(ioc, smid, ++ le16_to_cpu(mpi_request->DevHandle)); ++ } else ++ mpt2sas_base_put_smid_default(ioc, smid); ++ return 0; ++ ++ out: ++ return SCSI_MLQUEUE_HOST_BUSY; ++} ++ ++/** ++ * _scsih_normalize_sense - normalize descriptor and fixed format sense data ++ * @sense_buffer: sense data returned by target ++ * @data: normalized skey/asc/ascq ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_normalize_sense(char *sense_buffer, struct sense_info *data) ++{ ++ if ((sense_buffer[0] & 0x7F) >= 0x72) { ++ /* descriptor format */ ++ data->skey = sense_buffer[1] & 0x0F; ++ data->asc = sense_buffer[2]; ++ data->ascq = sense_buffer[3]; ++ } else { ++ /* fixed format */ ++ data->skey = sense_buffer[2] & 0x0F; ++ data->asc = sense_buffer[12]; ++ data->ascq = sense_buffer[13]; ++ } ++} ++ ++/** ++ * _scsih_scsi_ioc_info - translated non-succesfull SCSI_IO request ++ * @ioc: per adapter object ++ * @scmd: pointer to scsi command object ++ * @mpi_reply: reply mf payload returned from firmware ++ * ++ * scsi_status - SCSI Status code returned from target device ++ * scsi_state - state info associated with SCSI_IO determined by ioc ++ * ioc_status - ioc supplied status info ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, ++ Mpi2SCSIIOReply_t *mpi_reply, u16 smid) ++{ ++ u32 response_info; ++ u8 *response_bytes; ++ u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ u8 scsi_state = mpi_reply->SCSIState; ++ u8 scsi_status = mpi_reply->SCSIStatus; ++ char *desc_ioc_state = NULL; ++ char *desc_scsi_status = NULL; ++ char *desc_scsi_state = ioc->tmp_string; ++ u32 log_info = le32_to_cpu(mpi_reply->IOCLogInfo); ++ struct _sas_device *sas_device = NULL; ++ struct scsi_target *starget = scmd->device->sdev_target; ++ struct MPT3SAS_TARGET *priv_target = starget->hostdata; ++ char *device_str = NULL; ++ ++ if (!priv_target) ++ return; ++ if (ioc->hide_ir_msg) ++ device_str = "WarpDrive"; ++ else ++ device_str = "volume"; ++ ++ if (log_info == 0x31170000) ++ return; ++ ++ switch (ioc_status) { ++ case MPI2_IOCSTATUS_SUCCESS: ++ desc_ioc_state = "success"; ++ break; ++ case MPI2_IOCSTATUS_INVALID_FUNCTION: ++ desc_ioc_state = "invalid function"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: ++ desc_ioc_state = "scsi recovered error"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE: ++ desc_ioc_state = "scsi invalid dev handle"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: ++ desc_ioc_state = "scsi device not there"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: ++ desc_ioc_state = "scsi data overrun"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: ++ desc_ioc_state = "scsi data underrun"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: ++ desc_ioc_state = "scsi io data error"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: ++ desc_ioc_state = "scsi protocol error"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: ++ desc_ioc_state = "scsi task terminated"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: ++ desc_ioc_state = "scsi residual mismatch"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: ++ desc_ioc_state = "scsi task mgmt failed"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: ++ desc_ioc_state = "scsi ioc terminated"; ++ break; ++ case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: ++ desc_ioc_state = "scsi ext terminated"; ++ break; ++ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: ++ desc_ioc_state = "eedp guard error"; ++ break; ++ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR: ++ desc_ioc_state = "eedp ref tag error"; ++ break; ++ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR: ++ desc_ioc_state = "eedp app tag error"; ++ break; ++ case MPI2_IOCSTATUS_INSUFFICIENT_POWER: ++ desc_ioc_state = "insufficient power"; ++ break; ++ default: ++ desc_ioc_state = "unknown"; ++ break; ++ } ++ ++ switch (scsi_status) { ++ case MPI2_SCSI_STATUS_GOOD: ++ desc_scsi_status = "good"; ++ break; ++ case MPI2_SCSI_STATUS_CHECK_CONDITION: ++ desc_scsi_status = "check condition"; ++ break; ++ case MPI2_SCSI_STATUS_CONDITION_MET: ++ desc_scsi_status = "condition met"; ++ break; ++ case MPI2_SCSI_STATUS_BUSY: ++ desc_scsi_status = "busy"; ++ break; ++ case MPI2_SCSI_STATUS_INTERMEDIATE: ++ desc_scsi_status = "intermediate"; ++ break; ++ case MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET: ++ desc_scsi_status = "intermediate condmet"; ++ break; ++ case MPI2_SCSI_STATUS_RESERVATION_CONFLICT: ++ desc_scsi_status = "reservation conflict"; ++ break; ++ case MPI2_SCSI_STATUS_COMMAND_TERMINATED: ++ desc_scsi_status = "command terminated"; ++ break; ++ case MPI2_SCSI_STATUS_TASK_SET_FULL: ++ desc_scsi_status = "task set full"; ++ break; ++ case MPI2_SCSI_STATUS_ACA_ACTIVE: ++ desc_scsi_status = "aca active"; ++ break; ++ case MPI2_SCSI_STATUS_TASK_ABORTED: ++ desc_scsi_status = "task aborted"; ++ break; ++ default: ++ desc_scsi_status = "unknown"; ++ break; ++ } ++ ++ desc_scsi_state[0] = '\0'; ++ if (!scsi_state) ++ desc_scsi_state = " "; ++ if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) ++ strcat(desc_scsi_state, "response info "); ++ if (scsi_state & MPI2_SCSI_STATE_TERMINATED) ++ strcat(desc_scsi_state, "state terminated "); ++ if (scsi_state & MPI2_SCSI_STATE_NO_SCSI_STATUS) ++ strcat(desc_scsi_state, "no status "); ++ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_FAILED) ++ strcat(desc_scsi_state, "autosense failed "); ++ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) ++ strcat(desc_scsi_state, "autosense valid "); ++ ++ scsi_print_command(scmd); ++ ++ if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) { ++ pr_warn(MPT3SAS_FMT "\t%s wwid(0x%016llx)\n", ioc->name, ++ device_str, (unsigned long long)priv_target->sas_address); ++ } else { ++ sas_device = mpt2sas_get_sdev_from_target(ioc, priv_target); ++ if (sas_device) { ++ pr_warn(MPT3SAS_FMT ++ "\tsas_address(0x%016llx), phy(%d)\n", ++ ioc->name, (unsigned long long) ++ sas_device->sas_address, sas_device->phy); ++ if (sas_device->enclosure_handle != 0) ++ pr_warn(MPT3SAS_FMT ++ "\tenclosure_logical_id(0x%016llx)," ++ "slot(%d)\n", ioc->name, ++ (unsigned long long) ++ sas_device->enclosure_logical_id, ++ sas_device->slot); ++ if (sas_device->connector_name[0]) ++ pr_warn(MPT3SAS_FMT ++ "\tenclosure level(0x%04x)," ++ " connector name( %s)\n", ioc->name, ++ sas_device->enclosure_level, ++ sas_device->connector_name); ++ ++ sas_device_put(sas_device); ++ } ++ } ++ ++ pr_warn(MPT3SAS_FMT ++ "\thandle(0x%04x), ioc_status(%s)(0x%04x), smid(%d)\n", ++ ioc->name, le16_to_cpu(mpi_reply->DevHandle), ++ desc_ioc_state, ioc_status, smid); ++ pr_warn(MPT3SAS_FMT ++ "\trequest_len(%d), underflow(%d), resid(%d)\n", ++ ioc->name, scsi_bufflen(scmd), scmd->underflow, ++ scsi_get_resid(scmd)); ++ pr_warn(MPT3SAS_FMT ++ "\ttag(%d), transfer_count(%d), sc->result(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply->TaskTag), ++ le32_to_cpu(mpi_reply->TransferCount), scmd->result); ++ pr_warn(MPT3SAS_FMT ++ "\tscsi_status(%s)(0x%02x), scsi_state(%s)(0x%02x)\n", ++ ioc->name, desc_scsi_status, ++ scsi_status, desc_scsi_state, scsi_state); ++ ++ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) { ++ struct sense_info data; ++ _scsih_normalize_sense(scmd->sense_buffer, &data); ++ pr_warn(MPT3SAS_FMT ++ "\t[sense_key,asc,ascq]: [0x%02x,0x%02x,0x%02x], count(%d)\n", ++ ioc->name, data.skey, ++ data.asc, data.ascq, le32_to_cpu(mpi_reply->SenseCount)); ++ } ++ ++ if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) { ++ response_info = le32_to_cpu(mpi_reply->ResponseInfo); ++ response_bytes = (u8 *)&response_info; ++ _scsih_response_code(ioc, response_bytes[0]); ++ } ++} ++ ++/** ++ * _scsih_turn_on_pfa_led - illuminate PFA LED ++ * @ioc: per adapter object ++ * @handle: device handle ++ * Context: process ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_turn_on_pfa_led(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ Mpi2SepReply_t mpi_reply; ++ Mpi2SepRequest_t mpi_request; ++ struct _sas_device *sas_device; ++ ++ sas_device = mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (!sas_device) ++ return; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; ++ mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS; ++ mpi_request.SlotStatus = ++ cpu_to_le32(MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT); ++ mpi_request.DevHandle = cpu_to_le16(handle); ++ mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS; ++ if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, ++ &mpi_request)) != 0) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ sas_device->pfa_led_on = 1; ++ ++ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "enclosure_processor: ioc_status (0x%04x), loginfo(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply.IOCStatus), ++ le32_to_cpu(mpi_reply.IOCLogInfo))); ++ goto out; ++ } ++out: ++ sas_device_put(sas_device); ++} ++ ++/** ++ * _scsih_turn_off_pfa_led - turn off Fault LED ++ * @ioc: per adapter object ++ * @sas_device: sas device whose PFA LED has to turned off ++ * Context: process ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_turn_off_pfa_led(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_device *sas_device) ++{ ++ Mpi2SepReply_t mpi_reply; ++ Mpi2SepRequest_t mpi_request; ++ ++ memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; ++ mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS; ++ mpi_request.SlotStatus = 0; ++ mpi_request.Slot = cpu_to_le16(sas_device->slot); ++ mpi_request.DevHandle = 0; ++ mpi_request.EnclosureHandle = cpu_to_le16(sas_device->enclosure_handle); ++ mpi_request.Flags = MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS; ++ if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, ++ &mpi_request)) != 0) { ++ printk(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) { ++ dewtprintk(ioc, printk(MPT3SAS_FMT ++ "enclosure_processor: ioc_status (0x%04x), loginfo(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply.IOCStatus), ++ le32_to_cpu(mpi_reply.IOCLogInfo))); ++ return; ++ } ++} ++ ++/** ++ * _scsih_send_event_to_turn_on_pfa_led - fire delayed event ++ * @ioc: per adapter object ++ * @handle: device handle ++ * Context: interrupt. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_send_event_to_turn_on_pfa_led(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct fw_event_work *fw_event; ++ ++ fw_event = alloc_fw_event_work(0); ++ if (!fw_event) ++ return; ++ fw_event->event = MPT3SAS_TURN_ON_PFA_LED; ++ fw_event->device_handle = handle; ++ fw_event->ioc = ioc; ++ _scsih_fw_event_add(ioc, fw_event); ++ fw_event_work_put(fw_event); ++} ++ ++/** ++ * _scsih_smart_predicted_fault - process smart errors ++ * @ioc: per adapter object ++ * @handle: device handle ++ * Context: interrupt. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_smart_predicted_fault(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct scsi_target *starget; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ Mpi2EventNotificationReply_t *event_reply; ++ Mpi2EventDataSasDeviceStatusChange_t *event_data; ++ struct _sas_device *sas_device; ++ ssize_t sz; ++ unsigned long flags; ++ ++ /* only handle non-raid devices */ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (!sas_device) ++ goto out_unlock; ++ ++ starget = sas_device->starget; ++ sas_target_priv_data = starget->hostdata; ++ ++ if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) || ++ ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))) ++ goto out_unlock; ++ ++ if (sas_device->enclosure_handle != 0) ++ starget_printk(KERN_INFO, starget, "predicted fault, " ++ "enclosure logical id(0x%016llx), slot(%d)\n", ++ (unsigned long long)sas_device->enclosure_logical_id, ++ sas_device->slot); ++ if (sas_device->connector_name[0] != '\0') ++ starget_printk(KERN_WARNING, starget, "predicted fault, " ++ "enclosure level(0x%04x), connector name( %s)\n", ++ sas_device->enclosure_level, ++ sas_device->connector_name); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) ++ _scsih_send_event_to_turn_on_pfa_led(ioc, handle); ++ ++ /* insert into event log */ ++ sz = offsetof(Mpi2EventNotificationReply_t, EventData) + ++ sizeof(Mpi2EventDataSasDeviceStatusChange_t); ++ event_reply = kzalloc(sz, GFP_KERNEL); ++ if (!event_reply) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ++ event_reply->Function = MPI2_FUNCTION_EVENT_NOTIFICATION; ++ event_reply->Event = ++ cpu_to_le16(MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE); ++ event_reply->MsgLength = sz/4; ++ event_reply->EventDataLength = ++ cpu_to_le16(sizeof(Mpi2EventDataSasDeviceStatusChange_t)/4); ++ event_data = (Mpi2EventDataSasDeviceStatusChange_t *) ++ event_reply->EventData; ++ event_data->ReasonCode = MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA; ++ event_data->ASC = 0x5D; ++ event_data->DevHandle = cpu_to_le16(handle); ++ event_data->SASAddress = cpu_to_le64(sas_target_priv_data->sas_address); ++ mpt2sas_ctl_add_to_event_log(ioc, event_reply); ++ kfree(event_reply); ++out: ++ if (sas_device) ++ sas_device_put(sas_device); ++ return; ++ ++out_unlock: ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ goto out; ++} ++ ++/** ++ * _scsih_io_done - scsi request callback ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * ++ * Callback handler when using _scsih_qcmd_mpt2sas. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) ++{ ++ Mpi2SCSIIORequest_t *mpi_request; ++ Mpi2SCSIIOReply_t *mpi_reply; ++ struct scsi_cmnd *scmd; ++ u16 ioc_status; ++ u32 xfer_cnt; ++ u8 scsi_state; ++ u8 scsi_status; ++ u32 log_info; ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ u32 response_code = 0; ++ unsigned long flags; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ scmd = _scsih_scsi_lookup_get_clear(ioc, smid); ++ if (scmd == NULL) ++ return 1; ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ++ if (mpi_reply == NULL) { ++ scmd->result = DID_OK << 16; ++ goto out; ++ } ++ ++ sas_device_priv_data = scmd->device->hostdata; ++ if (!sas_device_priv_data || !sas_device_priv_data->sas_target || ++ sas_device_priv_data->sas_target->deleted) { ++ scmd->result = DID_NO_CONNECT << 16; ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus); ++ ++ /* ++ * WARPDRIVE: If direct_io is set then it is directIO, ++ * the failed direct I/O should be redirected to volume ++ */ ++ if (mpt2sas_scsi_direct_io_get(ioc, smid) && ++ ((ioc_status & MPI2_IOCSTATUS_MASK) ++ != MPI2_IOCSTATUS_SCSI_TASK_TERMINATED)) { ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ ioc->scsi_lookup[smid - 1].scmd = scmd; ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ mpt2sas_scsi_direct_io_set(ioc, smid, 0); ++ memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len); ++ mpi_request->DevHandle = ++ cpu_to_le16(sas_device_priv_data->sas_target->handle); ++ mpt2sas_base_put_smid_scsi_io(ioc, smid, ++ sas_device_priv_data->sas_target->handle); ++ return 0; ++ } ++ /* turning off TLR */ ++ scsi_state = mpi_reply->SCSIState; ++ if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) ++ response_code = ++ le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF; ++ if (!sas_device_priv_data->tlr_snoop_check) { ++ sas_device_priv_data->tlr_snoop_check++; ++ if (!ioc->is_warpdrive && ++ !scsih_is_raid_mpt2sas(&scmd->device->sdev_gendev) && ++ sas_is_tlr_enabled(scmd->device) && ++ response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) { ++ sas_disable_tlr(scmd->device); ++ sdev_printk(KERN_INFO, scmd->device, "TLR disabled\n"); ++ } ++ } ++ ++ xfer_cnt = le32_to_cpu(mpi_reply->TransferCount); ++ scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_cnt); ++ if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) ++ log_info = le32_to_cpu(mpi_reply->IOCLogInfo); ++ else ++ log_info = 0; ++ ioc_status &= MPI2_IOCSTATUS_MASK; ++ scsi_status = mpi_reply->SCSIStatus; ++ ++ if (ioc_status == MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 && ++ (scsi_status == MPI2_SCSI_STATUS_BUSY || ++ scsi_status == MPI2_SCSI_STATUS_RESERVATION_CONFLICT || ++ scsi_status == MPI2_SCSI_STATUS_TASK_SET_FULL)) { ++ ioc_status = MPI2_IOCSTATUS_SUCCESS; ++ } ++ ++ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) { ++ struct sense_info data; ++ const void *sense_data = mpt2sas_base_get_sense_buffer(ioc, ++ smid); ++ u32 sz = min_t(u32, SCSI_SENSE_BUFFERSIZE, ++ le32_to_cpu(mpi_reply->SenseCount)); ++ memcpy(scmd->sense_buffer, sense_data, sz); ++ _scsih_normalize_sense(scmd->sense_buffer, &data); ++ /* failure prediction threshold exceeded */ ++ if (data.asc == 0x5D) ++ _scsih_smart_predicted_fault(ioc, ++ le16_to_cpu(mpi_reply->DevHandle)); ++ mpt2sas_trigger_scsi(ioc, data.skey, data.asc, data.ascq); ++ ++ if (!(ioc->logging_level & MPT_DEBUG_REPLY) && ++ ((scmd->sense_buffer[2] == UNIT_ATTENTION) || ++ (scmd->sense_buffer[2] == MEDIUM_ERROR) || ++ (scmd->sense_buffer[2] == HARDWARE_ERROR))) ++ _scsih_scsi_ioc_info(ioc, scmd, mpi_reply, smid); ++ } ++ switch (ioc_status) { ++ case MPI2_IOCSTATUS_BUSY: ++ case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES: ++ scmd->result = SAM_STAT_BUSY; ++ break; ++ ++ case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: ++ scmd->result = DID_NO_CONNECT << 16; ++ break; ++ ++ case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: ++ if (sas_device_priv_data->block) { ++ scmd->result = DID_TRANSPORT_DISRUPTED << 16; ++ goto out; ++ } ++ if (log_info == 0x31110630) { ++ if (scmd->retries > 2) { ++ scmd->result = DID_NO_CONNECT << 16; ++ scsi_device_set_state(scmd->device, ++ SDEV_OFFLINE); ++ } else { ++ scmd->result = DID_SOFT_ERROR << 16; ++ scmd->device->expecting_cc_ua = 1; ++ } ++ break; ++ } else if (log_info == VIRTUAL_IO_FAILED_RETRY) { ++ scmd->result = DID_RESET << 16; ++ break; ++ } ++ scmd->result = DID_SOFT_ERROR << 16; ++ break; ++ case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: ++ case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: ++ scmd->result = DID_RESET << 16; ++ break; ++ ++ case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: ++ if ((xfer_cnt == 0) || (scmd->underflow > xfer_cnt)) ++ scmd->result = DID_SOFT_ERROR << 16; ++ else ++ scmd->result = (DID_OK << 16) | scsi_status; ++ break; ++ ++ case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: ++ scmd->result = (DID_OK << 16) | scsi_status; ++ ++ if ((scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID)) ++ break; ++ ++ if (xfer_cnt < scmd->underflow) { ++ if (scsi_status == SAM_STAT_BUSY) ++ scmd->result = SAM_STAT_BUSY; ++ else ++ scmd->result = DID_SOFT_ERROR << 16; ++ } else if (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED | ++ MPI2_SCSI_STATE_NO_SCSI_STATUS)) ++ scmd->result = DID_SOFT_ERROR << 16; ++ else if (scsi_state & MPI2_SCSI_STATE_TERMINATED) ++ scmd->result = DID_RESET << 16; ++ else if (!xfer_cnt && scmd->cmnd[0] == REPORT_LUNS) { ++ mpi_reply->SCSIState = MPI2_SCSI_STATE_AUTOSENSE_VALID; ++ mpi_reply->SCSIStatus = SAM_STAT_CHECK_CONDITION; ++ scmd->result = (DRIVER_SENSE << 24) | ++ SAM_STAT_CHECK_CONDITION; ++ scmd->sense_buffer[0] = 0x70; ++ scmd->sense_buffer[2] = ILLEGAL_REQUEST; ++ scmd->sense_buffer[12] = 0x20; ++ scmd->sense_buffer[13] = 0; ++ } ++ break; ++ ++ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: ++ scsi_set_resid(scmd, 0); ++ case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: ++ case MPI2_IOCSTATUS_SUCCESS: ++ scmd->result = (DID_OK << 16) | scsi_status; ++ if (response_code == ++ MPI2_SCSITASKMGMT_RSP_INVALID_FRAME || ++ (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED | ++ MPI2_SCSI_STATE_NO_SCSI_STATUS))) ++ scmd->result = DID_SOFT_ERROR << 16; ++ else if (scsi_state & MPI2_SCSI_STATE_TERMINATED) ++ scmd->result = DID_RESET << 16; ++ break; ++ ++ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: ++ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR: ++ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR: ++ _scsih_eedp_error_handling(scmd, ioc_status); ++ break; ++ ++ case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: ++ case MPI2_IOCSTATUS_INVALID_FUNCTION: ++ case MPI2_IOCSTATUS_INVALID_SGL: ++ case MPI2_IOCSTATUS_INTERNAL_ERROR: ++ case MPI2_IOCSTATUS_INVALID_FIELD: ++ case MPI2_IOCSTATUS_INVALID_STATE: ++ case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: ++ case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: ++ case MPI2_IOCSTATUS_INSUFFICIENT_POWER: ++ default: ++ scmd->result = DID_SOFT_ERROR << 16; ++ break; ++ ++ } ++ ++ if (scmd->result && (ioc->logging_level & MPT_DEBUG_REPLY)) ++ _scsih_scsi_ioc_info(ioc , scmd, mpi_reply, smid); ++ ++ out: ++ ++ scsi_dma_unmap(scmd); ++ ++ scmd->scsi_done(scmd); ++ return 1; ++} ++ ++/** ++ * _scsih_sas_host_refresh - refreshing sas host object contents ++ * @ioc: per adapter object ++ * Context: user ++ * ++ * During port enable, fw will send topology events for every device. Its ++ * possible that the handles may change from the previous setting, so this ++ * code keeping handles updating if changed. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_host_refresh(struct MPT3SAS_ADAPTER *ioc) ++{ ++ u16 sz; ++ u16 ioc_status; ++ int i; ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; ++ u16 attached_handle; ++ u8 link_rate; ++ ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "updating handles for sas_host(0x%016llx)\n", ++ ioc->name, (unsigned long long)ioc->sas_hba.sas_address)); ++ ++ sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys ++ * sizeof(Mpi2SasIOUnit0PhyData_t)); ++ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); ++ if (!sas_iounit_pg0) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, ++ sas_iounit_pg0, sz)) != 0) ++ goto out; ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ goto out; ++ for (i = 0; i < ioc->sas_hba.num_phys ; i++) { ++ link_rate = sas_iounit_pg0->PhyData[i].NegotiatedLinkRate >> 4; ++ if (i == 0) ++ ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0-> ++ PhyData[0].ControllerDevHandle); ++ ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle; ++ attached_handle = le16_to_cpu(sas_iounit_pg0->PhyData[i]. ++ AttachedDevHandle); ++ if (attached_handle && link_rate < MPI2_SAS_NEG_LINK_RATE_1_5) ++ link_rate = MPI2_SAS_NEG_LINK_RATE_1_5; ++ mpt2sas_transport_update_links(ioc, ioc->sas_hba.sas_address, ++ attached_handle, i, link_rate); ++ } ++ out: ++ kfree(sas_iounit_pg0); ++} ++ ++/** ++ * _scsih_sas_host_add - create sas host object ++ * @ioc: per adapter object ++ * ++ * Creating host side data object, stored in ioc->sas_hba ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_host_add(struct MPT3SAS_ADAPTER *ioc) ++{ ++ int i; ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; ++ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; ++ Mpi2SasPhyPage0_t phy_pg0; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ Mpi2SasEnclosurePage0_t enclosure_pg0; ++ u16 ioc_status; ++ u16 sz; ++ u8 device_missing_delay; ++ ++ mpt2sas_config_get_number_hba_phys(ioc, &ioc->sas_hba.num_phys); ++ if (!ioc->sas_hba.num_phys) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ /* sas_iounit page 0 */ ++ sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys * ++ sizeof(Mpi2SasIOUnit0PhyData_t)); ++ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); ++ if (!sas_iounit_pg0) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, ++ sas_iounit_pg0, sz))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ++ /* sas_iounit page 1 */ ++ sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * ++ sizeof(Mpi2SasIOUnit1PhyData_t)); ++ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); ++ if (!sas_iounit_pg1) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, ++ sas_iounit_pg1, sz))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ++ ioc->io_missing_delay = ++ sas_iounit_pg1->IODeviceMissingDelay; ++ device_missing_delay = ++ sas_iounit_pg1->ReportDeviceMissingDelay; ++ if (device_missing_delay & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16) ++ ioc->device_missing_delay = (device_missing_delay & ++ MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16; ++ else ++ ioc->device_missing_delay = device_missing_delay & ++ MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK; ++ ++ ioc->sas_hba.parent_dev = &ioc->shost->shost_gendev; ++ ioc->sas_hba.phy = kcalloc(ioc->sas_hba.num_phys, ++ sizeof(struct _sas_phy), GFP_KERNEL); ++ if (!ioc->sas_hba.phy) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ for (i = 0; i < ioc->sas_hba.num_phys ; i++) { ++ if ((mpt2sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0, ++ i))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ++ if (i == 0) ++ ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0-> ++ PhyData[0].ControllerDevHandle); ++ ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle; ++ ioc->sas_hba.phy[i].phy_id = i; ++ mpt2sas_transport_add_host_phy(ioc, &ioc->sas_hba.phy[i], ++ phy_pg0, ioc->sas_hba.parent_dev); ++ } ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, ++ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out; ++ } ++ ioc->sas_hba.enclosure_handle = ++ le16_to_cpu(sas_device_pg0.EnclosureHandle); ++ ioc->sas_hba.sas_address = le64_to_cpu(sas_device_pg0.SASAddress); ++ pr_info(MPT3SAS_FMT ++ "host_add: handle(0x%04x), sas_addr(0x%016llx), phys(%d)\n", ++ ioc->name, ioc->sas_hba.handle, ++ (unsigned long long) ioc->sas_hba.sas_address, ++ ioc->sas_hba.num_phys) ; ++ ++ if (ioc->sas_hba.enclosure_handle) { ++ if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply, ++ &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, ++ ioc->sas_hba.enclosure_handle))) ++ ioc->sas_hba.enclosure_logical_id = ++ le64_to_cpu(enclosure_pg0.EnclosureLogicalID); ++ } ++ ++ out: ++ kfree(sas_iounit_pg1); ++ kfree(sas_iounit_pg0); ++} ++ ++/** ++ * _scsih_expander_add - creating expander object ++ * @ioc: per adapter object ++ * @handle: expander handle ++ * ++ * Creating expander object, stored in ioc->sas_expander_list. ++ * ++ * Return 0 for success, else error. ++ */ ++static int ++_scsih_expander_add(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _sas_node *sas_expander; ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2ExpanderPage0_t expander_pg0; ++ Mpi2ExpanderPage1_t expander_pg1; ++ Mpi2SasEnclosurePage0_t enclosure_pg0; ++ u32 ioc_status; ++ u16 parent_handle; ++ u64 sas_address, sas_address_parent = 0; ++ int i; ++ unsigned long flags; ++ struct _sas_port *mpt2sas_port = NULL; ++ ++ int rc = 0; ++ ++ if (!handle) ++ return -1; ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery) ++ return -1; ++ ++ if ((mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, ++ MPI2_SAS_EXPAND_PGAD_FORM_HNDL, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ ++ /* handle out of order topology events */ ++ parent_handle = le16_to_cpu(expander_pg0.ParentDevHandle); ++ if (_scsih_get_sas_address(ioc, parent_handle, &sas_address_parent) ++ != 0) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ if (sas_address_parent != ioc->sas_hba.sas_address) { ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, ++ sas_address_parent); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ if (!sas_expander) { ++ rc = _scsih_expander_add(ioc, parent_handle); ++ if (rc != 0) ++ return rc; ++ } ++ } ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_address = le64_to_cpu(expander_pg0.SASAddress); ++ sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, ++ sas_address); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ if (sas_expander) ++ return 0; ++ ++ sas_expander = kzalloc(sizeof(struct _sas_node), ++ GFP_KERNEL); ++ if (!sas_expander) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ ++ sas_expander->handle = handle; ++ sas_expander->num_phys = expander_pg0.NumPhys; ++ sas_expander->sas_address_parent = sas_address_parent; ++ sas_expander->sas_address = sas_address; ++ ++ pr_info(MPT3SAS_FMT "expander_add: handle(0x%04x)," \ ++ " parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n", ioc->name, ++ handle, parent_handle, (unsigned long long) ++ sas_expander->sas_address, sas_expander->num_phys); ++ ++ if (!sas_expander->num_phys) ++ goto out_fail; ++ sas_expander->phy = kcalloc(sas_expander->num_phys, ++ sizeof(struct _sas_phy), GFP_KERNEL); ++ if (!sas_expander->phy) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -1; ++ goto out_fail; ++ } ++ ++ INIT_LIST_HEAD(&sas_expander->sas_port_list); ++ mpt2sas_port = mpt2sas_transport_port_add(ioc, handle, ++ sas_address_parent); ++ if (!mpt2sas_port) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -1; ++ goto out_fail; ++ } ++ sas_expander->parent_dev = &mpt2sas_port->rphy->dev; ++ ++ for (i = 0 ; i < sas_expander->num_phys ; i++) { ++ if ((mpt2sas_config_get_expander_pg1(ioc, &mpi_reply, ++ &expander_pg1, i, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -1; ++ goto out_fail; ++ } ++ sas_expander->phy[i].handle = handle; ++ sas_expander->phy[i].phy_id = i; ++ ++ if ((mpt2sas_transport_add_expander_phy(ioc, ++ &sas_expander->phy[i], expander_pg1, ++ sas_expander->parent_dev))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -1; ++ goto out_fail; ++ } ++ } ++ ++ if (sas_expander->enclosure_handle) { ++ if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply, ++ &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, ++ sas_expander->enclosure_handle))) ++ sas_expander->enclosure_logical_id = ++ le64_to_cpu(enclosure_pg0.EnclosureLogicalID); ++ } ++ ++ _scsih_expander_node_add(ioc, sas_expander); ++ return 0; ++ ++ out_fail: ++ ++ if (mpt2sas_port) ++ mpt2sas_transport_port_remove(ioc, sas_expander->sas_address, ++ sas_address_parent); ++ kfree(sas_expander); ++ return rc; ++} ++ ++/** ++ * mpt2sas_expander_remove - removing expander object ++ * @ioc: per adapter object ++ * @sas_address: expander sas_address ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address) ++{ ++ struct _sas_node *sas_expander; ++ unsigned long flags; ++ ++ if (ioc->shost_recovery) ++ return; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, ++ sas_address); ++ if (sas_expander) ++ list_del(&sas_expander->list); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ if (sas_expander) ++ _scsih_expander_node_remove(ioc, sas_expander); ++} ++ ++/** ++ * _scsih_done - internal SCSI_IO callback handler. ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * ++ * Callback handler when sending internal generated SCSI_IO. ++ * The callback index passed is `ioc->scsih_cb_idx` ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++static u8 ++_scsih_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (ioc->scsih_cmds.status == MPT3_CMD_NOT_USED) ++ return 1; ++ if (ioc->scsih_cmds.smid != smid) ++ return 1; ++ ioc->scsih_cmds.status |= MPT3_CMD_COMPLETE; ++ if (mpi_reply) { ++ memcpy(ioc->scsih_cmds.reply, mpi_reply, ++ mpi_reply->MsgLength*4); ++ ioc->scsih_cmds.status |= MPT3_CMD_REPLY_VALID; ++ } ++ ioc->scsih_cmds.status &= ~MPT3_CMD_PENDING; ++ complete(&ioc->scsih_cmds.done); ++ return 1; ++} ++ ++ ++ ++ ++#define MPT3_MAX_LUNS (255) ++ ++ ++/** ++ * _scsih_check_access_status - check access flags ++ * @ioc: per adapter object ++ * @sas_address: sas address ++ * @handle: sas device handle ++ * @access_flags: errors returned during discovery of the device ++ * ++ * Return 0 for success, else failure ++ */ ++static u8 ++_scsih_check_access_status(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, ++ u16 handle, u8 access_status) ++{ ++ u8 rc = 1; ++ char *desc = NULL; ++ ++ switch (access_status) { ++ case MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS: ++ case MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION: ++ rc = 0; ++ break; ++ case MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED: ++ desc = "sata capability failed"; ++ break; ++ case MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT: ++ desc = "sata affiliation conflict"; ++ break; ++ case MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE: ++ desc = "route not addressable"; ++ break; ++ case MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE: ++ desc = "smp error not addressable"; ++ break; ++ case MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED: ++ desc = "device blocked"; ++ break; ++ case MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE: ++ case MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX: ++ desc = "sata initialization failed"; ++ break; ++ default: ++ desc = "unknown"; ++ break; ++ } ++ ++ if (!rc) ++ return 0; ++ ++ pr_err(MPT3SAS_FMT ++ "discovery errors(%s): sas_address(0x%016llx), handle(0x%04x)\n", ++ ioc->name, desc, (unsigned long long)sas_address, handle); ++ return rc; ++} ++ ++/** ++ * _scsih_check_device - checking device responsiveness ++ * @ioc: per adapter object ++ * @parent_sas_address: sas address of parent expander or sas host ++ * @handle: attached device handle ++ * @phy_numberv: phy number ++ * @link_rate: new link rate ++ * ++ * Returns nothing. ++ */ ++static void ++_scsih_check_device(struct MPT3SAS_ADAPTER *ioc, ++ u64 parent_sas_address, u16 handle, u8 phy_number, u8 link_rate) ++{ ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ struct _sas_device *sas_device; ++ u32 ioc_status; ++ unsigned long flags; ++ u64 sas_address; ++ struct scsi_target *starget; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ u32 device_info; ++ ++ ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, ++ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) ++ return; ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ return; ++ ++ /* wide port handling ~ we need only handle device once for the phy that ++ * is matched in sas device page zero ++ */ ++ if (phy_number != sas_device_pg0.PhyNum) ++ return; ++ ++ /* check if this is end device */ ++ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); ++ if (!(_scsih_is_end_device(device_info))) ++ return; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_address = le64_to_cpu(sas_device_pg0.SASAddress); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ sas_address); ++ ++ if (!sas_device) ++ goto out_unlock; ++ ++ if (unlikely(sas_device->handle != handle)) { ++ starget = sas_device->starget; ++ sas_target_priv_data = starget->hostdata; ++ starget_printk(KERN_INFO, starget, ++ "handle changed from(0x%04x) to (0x%04x)!!!\n", ++ sas_device->handle, handle); ++ sas_target_priv_data->handle = handle; ++ sas_device->handle = handle; ++ if (sas_device_pg0.Flags & ++ MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { ++ sas_device->enclosure_level = ++ le16_to_cpu(sas_device_pg0.EnclosureLevel); ++ memcpy(&sas_device->connector_name[0], ++ &sas_device_pg0.ConnectorName[0], 4); ++ } else { ++ sas_device->enclosure_level = 0; ++ sas_device->connector_name[0] = '\0'; ++ } ++ } ++ ++ /* check if device is present */ ++ if (!(le16_to_cpu(sas_device_pg0.Flags) & ++ MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { ++ pr_err(MPT3SAS_FMT ++ "device is not present handle(0x%04x), flags!!!\n", ++ ioc->name, handle); ++ goto out_unlock; ++ } ++ ++ /* check if there were any issues with discovery */ ++ if (_scsih_check_access_status(ioc, sas_address, handle, ++ sas_device_pg0.AccessStatus)) ++ goto out_unlock; ++ ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ _scsih_ublock_io_device(ioc, sas_address); ++ ++ if (sas_device) ++ sas_device_put(sas_device); ++ return; ++ ++out_unlock: ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ if (sas_device) ++ sas_device_put(sas_device); ++} ++ ++/** ++ * _scsih_add_device - creating sas device object ++ * @ioc: per adapter object ++ * @handle: sas device handle ++ * @phy_num: phy number end device attached to ++ * @is_pd: is this hidden raid component ++ * ++ * Creating end device object, stored in ioc->sas_device_list. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, ++ u8 is_pd) ++{ ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ Mpi2SasEnclosurePage0_t enclosure_pg0; ++ struct _sas_device *sas_device; ++ u32 ioc_status; ++ u64 sas_address; ++ u32 device_info; ++ ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, ++ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ ++ /* check if this is end device */ ++ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); ++ if (!(_scsih_is_end_device(device_info))) ++ return -1; ++ sas_address = le64_to_cpu(sas_device_pg0.SASAddress); ++ ++ /* check if device is present */ ++ if (!(le16_to_cpu(sas_device_pg0.Flags) & ++ MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { ++ pr_err(MPT3SAS_FMT "device is not present handle(0x04%x)!!!\n", ++ ioc->name, handle); ++ return -1; ++ } ++ ++ /* check if there were any issues with discovery */ ++ if (_scsih_check_access_status(ioc, sas_address, handle, ++ sas_device_pg0.AccessStatus)) ++ return -1; ++ ++ sas_device = mpt2sas_get_sdev_by_addr(ioc, ++ sas_address); ++ if (sas_device) { ++ sas_device_put(sas_device); ++ return -1; ++ } ++ ++ sas_device = kzalloc(sizeof(struct _sas_device), ++ GFP_KERNEL); ++ if (!sas_device) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return 0; ++ } ++ ++ kref_init(&sas_device->refcount); ++ sas_device->handle = handle; ++ if (_scsih_get_sas_address(ioc, ++ le16_to_cpu(sas_device_pg0.ParentDevHandle), ++ &sas_device->sas_address_parent) != 0) ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ sas_device->enclosure_handle = ++ le16_to_cpu(sas_device_pg0.EnclosureHandle); ++ if (sas_device->enclosure_handle != 0) ++ sas_device->slot = ++ le16_to_cpu(sas_device_pg0.Slot); ++ sas_device->device_info = device_info; ++ sas_device->sas_address = sas_address; ++ sas_device->phy = sas_device_pg0.PhyNum; ++ sas_device->fast_path = (le16_to_cpu(sas_device_pg0.Flags) & ++ MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE) ? 1 : 0; ++ ++ if (sas_device_pg0.Flags & MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { ++ sas_device->enclosure_level = ++ le16_to_cpu(sas_device_pg0.EnclosureLevel); ++ memcpy(&sas_device->connector_name[0], ++ &sas_device_pg0.ConnectorName[0], 4); ++ } else { ++ sas_device->enclosure_level = 0; ++ sas_device->connector_name[0] = '\0'; ++ } ++ /* get enclosure_logical_id */ ++ if (sas_device->enclosure_handle && !(mpt2sas_config_get_enclosure_pg0( ++ ioc, &mpi_reply, &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, ++ sas_device->enclosure_handle))) ++ sas_device->enclosure_logical_id = ++ le64_to_cpu(enclosure_pg0.EnclosureLogicalID); ++ ++ /* get device name */ ++ sas_device->device_name = le64_to_cpu(sas_device_pg0.DeviceName); ++ ++ if (ioc->wait_for_discovery_to_complete) ++ _scsih_sas_device_init_add(ioc, sas_device); ++ else ++ _scsih_sas_device_add(ioc, sas_device); ++ ++ sas_device_put(sas_device); ++ return 0; ++} ++ ++/** ++ * _scsih_remove_device - removing sas device object ++ * @ioc: per adapter object ++ * @sas_device_delete: the sas_device object ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_remove_device(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_device *sas_device) ++{ ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ ++ if ((ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) && ++ (sas_device->pfa_led_on)) { ++ _scsih_turn_off_pfa_led(ioc, sas_device); ++ sas_device->pfa_led_on = 0; ++ } ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enter: handle(0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, __func__, ++ sas_device->handle, (unsigned long long) ++ sas_device->sas_address)); ++ if (sas_device->enclosure_handle != 0) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enter: enclosure logical id(0x%016llx), slot(%d)\n", ++ ioc->name, __func__, ++ (unsigned long long)sas_device->enclosure_logical_id, ++ sas_device->slot)); ++ if (sas_device->connector_name[0] != '\0') ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enter: enclosure level(0x%04x), connector name( %s)\n", ++ ioc->name, __func__, ++ sas_device->enclosure_level, ++ sas_device->connector_name)); ++ ++ if (sas_device->starget && sas_device->starget->hostdata) { ++ sas_target_priv_data = sas_device->starget->hostdata; ++ sas_target_priv_data->deleted = 1; ++ _scsih_ublock_io_device(ioc, sas_device->sas_address); ++ sas_target_priv_data->handle = ++ MPT3SAS_INVALID_DEVICE_HANDLE; ++ } ++ ++ if (!ioc->hide_drives) ++ mpt2sas_transport_port_remove(ioc, ++ sas_device->sas_address, ++ sas_device->sas_address_parent); ++ ++ pr_info(MPT3SAS_FMT ++ "removing handle(0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, sas_device->handle, ++ (unsigned long long) sas_device->sas_address); ++ if (sas_device->enclosure_handle != 0) ++ pr_info(MPT3SAS_FMT ++ "removing : enclosure logical id(0x%016llx), slot(%d)\n", ++ ioc->name, ++ (unsigned long long)sas_device->enclosure_logical_id, ++ sas_device->slot); ++ if (sas_device->connector_name[0] != '\0') ++ pr_info(MPT3SAS_FMT ++ "removing enclosure level(0x%04x), connector name( %s)\n", ++ ioc->name, sas_device->enclosure_level, ++ sas_device->connector_name); ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: exit: handle(0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, __func__, ++ sas_device->handle, (unsigned long long) ++ sas_device->sas_address)); ++ if (sas_device->enclosure_handle != 0) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: exit: enclosure logical id(0x%016llx), slot(%d)\n", ++ ioc->name, __func__, ++ (unsigned long long)sas_device->enclosure_logical_id, ++ sas_device->slot)); ++ if (sas_device->connector_name[0] != '\0') ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: exit: enclosure level(0x%04x), connector name(%s)\n", ++ ioc->name, __func__, sas_device->enclosure_level, ++ sas_device->connector_name)); ++} ++ ++/** ++ * _scsih_sas_topology_change_event_debug - debug for topology event ++ * @ioc: per adapter object ++ * @event_data: event data payload ++ * Context: user. ++ */ ++static void ++_scsih_sas_topology_change_event_debug(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataSasTopologyChangeList_t *event_data) ++{ ++ int i; ++ u16 handle; ++ u16 reason_code; ++ u8 phy_number; ++ char *status_str = NULL; ++ u8 link_rate, prev_link_rate; ++ ++ switch (event_data->ExpStatus) { ++ case MPI2_EVENT_SAS_TOPO_ES_ADDED: ++ status_str = "add"; ++ break; ++ case MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING: ++ status_str = "remove"; ++ break; ++ case MPI2_EVENT_SAS_TOPO_ES_RESPONDING: ++ case 0: ++ status_str = "responding"; ++ break; ++ case MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING: ++ status_str = "remove delay"; ++ break; ++ default: ++ status_str = "unknown status"; ++ break; ++ } ++ pr_info(MPT3SAS_FMT "sas topology change: (%s)\n", ++ ioc->name, status_str); ++ pr_info("\thandle(0x%04x), enclosure_handle(0x%04x) " \ ++ "start_phy(%02d), count(%d)\n", ++ le16_to_cpu(event_data->ExpanderDevHandle), ++ le16_to_cpu(event_data->EnclosureHandle), ++ event_data->StartPhyNum, event_data->NumEntries); ++ for (i = 0; i < event_data->NumEntries; i++) { ++ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); ++ if (!handle) ++ continue; ++ phy_number = event_data->StartPhyNum + i; ++ reason_code = event_data->PHY[i].PhyStatus & ++ MPI2_EVENT_SAS_TOPO_RC_MASK; ++ switch (reason_code) { ++ case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: ++ status_str = "target add"; ++ break; ++ case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING: ++ status_str = "target remove"; ++ break; ++ case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING: ++ status_str = "delay target remove"; ++ break; ++ case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED: ++ status_str = "link rate change"; ++ break; ++ case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE: ++ status_str = "target responding"; ++ break; ++ default: ++ status_str = "unknown"; ++ break; ++ } ++ link_rate = event_data->PHY[i].LinkRate >> 4; ++ prev_link_rate = event_data->PHY[i].LinkRate & 0xF; ++ pr_info("\tphy(%02d), attached_handle(0x%04x): %s:" \ ++ " link rate: new(0x%02x), old(0x%02x)\n", phy_number, ++ handle, status_str, link_rate, prev_link_rate); ++ ++ } ++} ++ ++/** ++ * _scsih_sas_topology_change_event - handle topology changes ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ */ ++static int ++_scsih_sas_topology_change_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ int i; ++ u16 parent_handle, handle; ++ u16 reason_code; ++ u8 phy_number, max_phys; ++ struct _sas_node *sas_expander; ++ u64 sas_address; ++ unsigned long flags; ++ u8 link_rate, prev_link_rate; ++ Mpi2EventDataSasTopologyChangeList_t *event_data = ++ (Mpi2EventDataSasTopologyChangeList_t *) ++ fw_event->event_data; ++ ++ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) ++ _scsih_sas_topology_change_event_debug(ioc, event_data); ++ ++ if (ioc->shost_recovery || ioc->remove_host || ioc->pci_error_recovery) ++ return 0; ++ ++ if (!ioc->sas_hba.num_phys) ++ _scsih_sas_host_add(ioc); ++ else ++ _scsih_sas_host_refresh(ioc); ++ ++ if (fw_event->ignore) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "ignoring expander event\n", ioc->name)); ++ return 0; ++ } ++ ++ parent_handle = le16_to_cpu(event_data->ExpanderDevHandle); ++ ++ /* handle expander add */ ++ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_ADDED) ++ if (_scsih_expander_add(ioc, parent_handle) != 0) ++ return 0; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, ++ parent_handle); ++ if (sas_expander) { ++ sas_address = sas_expander->sas_address; ++ max_phys = sas_expander->num_phys; ++ } else if (parent_handle < ioc->sas_hba.num_phys) { ++ sas_address = ioc->sas_hba.sas_address; ++ max_phys = ioc->sas_hba.num_phys; ++ } else { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return 0; ++ } ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ /* handle siblings events */ ++ for (i = 0; i < event_data->NumEntries; i++) { ++ if (fw_event->ignore) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "ignoring expander event\n", ioc->name)); ++ return 0; ++ } ++ if (ioc->remove_host || ioc->pci_error_recovery) ++ return 0; ++ phy_number = event_data->StartPhyNum + i; ++ if (phy_number >= max_phys) ++ continue; ++ reason_code = event_data->PHY[i].PhyStatus & ++ MPI2_EVENT_SAS_TOPO_RC_MASK; ++ if ((event_data->PHY[i].PhyStatus & ++ MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) && (reason_code != ++ MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING)) ++ continue; ++ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); ++ if (!handle) ++ continue; ++ link_rate = event_data->PHY[i].LinkRate >> 4; ++ prev_link_rate = event_data->PHY[i].LinkRate & 0xF; ++ switch (reason_code) { ++ case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED: ++ ++ if (ioc->shost_recovery) ++ break; ++ ++ if (link_rate == prev_link_rate) ++ break; ++ ++ mpt2sas_transport_update_links(ioc, sas_address, ++ handle, phy_number, link_rate); ++ ++ if (link_rate < MPI2_SAS_NEG_LINK_RATE_1_5) ++ break; ++ ++ _scsih_check_device(ioc, sas_address, handle, ++ phy_number, link_rate); ++ ++ ++ case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: ++ ++ if (ioc->shost_recovery) ++ break; ++ ++ mpt2sas_transport_update_links(ioc, sas_address, ++ handle, phy_number, link_rate); ++ ++ _scsih_add_device(ioc, handle, phy_number, 0); ++ ++ break; ++ case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING: ++ ++ _scsih_device_remove_by_handle(ioc, handle); ++ break; ++ } ++ } ++ ++ /* handle expander removal */ ++ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING && ++ sas_expander) ++ mpt2sas_expander_remove(ioc, sas_address); ++ ++ return 0; ++} ++ ++/** ++ * _scsih_sas_device_status_change_event_debug - debug for device event ++ * @event_data: event data payload ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_device_status_change_event_debug(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataSasDeviceStatusChange_t *event_data) ++{ ++ char *reason_str = NULL; ++ ++ switch (event_data->ReasonCode) { ++ case MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA: ++ reason_str = "smart data"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED: ++ reason_str = "unsupported device discovered"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET: ++ reason_str = "internal device reset"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL: ++ reason_str = "internal task abort"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL: ++ reason_str = "internal task abort set"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL: ++ reason_str = "internal clear task set"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL: ++ reason_str = "internal query task"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE: ++ reason_str = "sata init failure"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET: ++ reason_str = "internal device reset complete"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL: ++ reason_str = "internal task abort complete"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION: ++ reason_str = "internal async notification"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY: ++ reason_str = "expander reduced functionality"; ++ break; ++ case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY: ++ reason_str = "expander reduced functionality complete"; ++ break; ++ default: ++ reason_str = "unknown reason"; ++ break; ++ } ++ pr_info(MPT3SAS_FMT "device status change: (%s)\n" ++ "\thandle(0x%04x), sas address(0x%016llx), tag(%d)", ++ ioc->name, reason_str, le16_to_cpu(event_data->DevHandle), ++ (unsigned long long)le64_to_cpu(event_data->SASAddress), ++ le16_to_cpu(event_data->TaskTag)); ++ if (event_data->ReasonCode == MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA) ++ pr_info(MPT3SAS_FMT ", ASC(0x%x), ASCQ(0x%x)\n", ioc->name, ++ event_data->ASC, event_data->ASCQ); ++ pr_info("\n"); ++} ++ ++/** ++ * _scsih_sas_device_status_change_event - handle device status change ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_device_status_change_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ struct MPT3SAS_TARGET *target_priv_data; ++ struct _sas_device *sas_device; ++ u64 sas_address; ++ unsigned long flags; ++ Mpi2EventDataSasDeviceStatusChange_t *event_data = ++ (Mpi2EventDataSasDeviceStatusChange_t *) ++ fw_event->event_data; ++ ++ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) ++ _scsih_sas_device_status_change_event_debug(ioc, ++ event_data); ++ ++ /* In MPI Revision K (0xC), the internal device reset complete was ++ * implemented, so avoid setting tm_busy flag for older firmware. ++ */ ++ if ((ioc->facts.HeaderVersion >> 8) < 0xC) ++ return; ++ ++ if (event_data->ReasonCode != ++ MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET && ++ event_data->ReasonCode != ++ MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET) ++ return; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_address = le64_to_cpu(event_data->SASAddress); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ sas_address); ++ ++ if (!sas_device || !sas_device->starget) ++ goto out; ++ ++ target_priv_data = sas_device->starget->hostdata; ++ if (!target_priv_data) ++ goto out; ++ ++ if (event_data->ReasonCode == ++ MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET) ++ target_priv_data->tm_busy = 1; ++ else ++ target_priv_data->tm_busy = 0; ++ ++out: ++ if (sas_device) ++ sas_device_put(sas_device); ++ ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++} ++ ++/** ++ * _scsih_sas_enclosure_dev_status_change_event_debug - debug for enclosure ++ * event ++ * @ioc: per adapter object ++ * @event_data: event data payload ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_enclosure_dev_status_change_event_debug(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataSasEnclDevStatusChange_t *event_data) ++{ ++ char *reason_str = NULL; ++ ++ switch (event_data->ReasonCode) { ++ case MPI2_EVENT_SAS_ENCL_RC_ADDED: ++ reason_str = "enclosure add"; ++ break; ++ case MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING: ++ reason_str = "enclosure remove"; ++ break; ++ default: ++ reason_str = "unknown reason"; ++ break; ++ } ++ ++ pr_info(MPT3SAS_FMT "enclosure status change: (%s)\n" ++ "\thandle(0x%04x), enclosure logical id(0x%016llx)" ++ " number slots(%d)\n", ioc->name, reason_str, ++ le16_to_cpu(event_data->EnclosureHandle), ++ (unsigned long long)le64_to_cpu(event_data->EnclosureLogicalID), ++ le16_to_cpu(event_data->StartSlot)); ++} ++ ++/** ++ * _scsih_sas_enclosure_dev_status_change_event - handle enclosure events ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_enclosure_dev_status_change_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) ++ _scsih_sas_enclosure_dev_status_change_event_debug(ioc, ++ (Mpi2EventDataSasEnclDevStatusChange_t *) ++ fw_event->event_data); ++} ++ ++/** ++ * _scsih_sas_broadcast_primitive_event - handle broadcast events ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_broadcast_primitive_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ struct scsi_cmnd *scmd; ++ struct scsi_device *sdev; ++ u16 smid, handle; ++ u32 lun; ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ u32 termination_count; ++ u32 query_count; ++ Mpi2SCSITaskManagementReply_t *mpi_reply; ++ Mpi2EventDataSasBroadcastPrimitive_t *event_data = ++ (Mpi2EventDataSasBroadcastPrimitive_t *) ++ fw_event->event_data; ++ u16 ioc_status; ++ unsigned long flags; ++ int r; ++ u8 max_retries = 0; ++ u8 task_abort_retries; ++ ++ mutex_lock(&ioc->tm_cmds.mutex); ++ pr_info(MPT3SAS_FMT ++ "%s: enter: phy number(%d), width(%d)\n", ++ ioc->name, __func__, event_data->PhyNum, ++ event_data->PortWidth); ++ ++ _scsih_block_io_all_device(ioc); ++ ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ mpi_reply = ioc->tm_cmds.reply; ++ broadcast_aen_retry: ++ ++ /* sanity checks for retrying this loop */ ++ if (max_retries++ == 5) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT "%s: giving up\n", ++ ioc->name, __func__)); ++ goto out; ++ } else if (max_retries > 1) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT "%s: %d retry\n", ++ ioc->name, __func__, max_retries - 1)); ++ ++ termination_count = 0; ++ query_count = 0; ++ for (smid = 1; smid <= ioc->scsiio_depth; smid++) { ++ if (ioc->shost_recovery) ++ goto out; ++ scmd = _scsih_scsi_lookup_get(ioc, smid); ++ if (!scmd) ++ continue; ++ sdev = scmd->device; ++ sas_device_priv_data = sdev->hostdata; ++ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) ++ continue; ++ /* skip hidden raid components */ ++ if (sas_device_priv_data->sas_target->flags & ++ MPT_TARGET_FLAGS_RAID_COMPONENT) ++ continue; ++ /* skip volumes */ ++ if (sas_device_priv_data->sas_target->flags & ++ MPT_TARGET_FLAGS_VOLUME) ++ continue; ++ ++ handle = sas_device_priv_data->sas_target->handle; ++ lun = sas_device_priv_data->lun; ++ query_count++; ++ ++ if (ioc->shost_recovery) ++ goto out; ++ ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ r = mpt2sas_scsih_issue_tm(ioc, handle, 0, 0, lun, ++ MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30, ++ TM_MUTEX_OFF); ++ if (r == FAILED) { ++ sdev_printk(KERN_WARNING, sdev, ++ "mpt2sas_scsih_issue_tm: FAILED when sending " ++ "QUERY_TASK: scmd(%p)\n", scmd); ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ goto broadcast_aen_retry; ++ } ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) ++ & MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ sdev_printk(KERN_WARNING, sdev, ++ "query task: FAILED with IOCSTATUS(0x%04x), scmd(%p)\n", ++ ioc_status, scmd); ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ goto broadcast_aen_retry; ++ } ++ ++ /* see if IO is still owned by IOC and target */ ++ if (mpi_reply->ResponseCode == ++ MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED || ++ mpi_reply->ResponseCode == ++ MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC) { ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ continue; ++ } ++ task_abort_retries = 0; ++ tm_retry: ++ if (task_abort_retries++ == 60) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: ABORT_TASK: giving up\n", ioc->name, ++ __func__)); ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ goto broadcast_aen_retry; ++ } ++ ++ if (ioc->shost_recovery) ++ goto out_no_lock; ++ ++ r = mpt2sas_scsih_issue_tm(ioc, handle, sdev->channel, sdev->id, ++ sdev->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, ++ TM_MUTEX_OFF); ++ if (r == FAILED) { ++ sdev_printk(KERN_WARNING, sdev, ++ "mpt2sas_scsih_issue_tm: ABORT_TASK: FAILED : " ++ "scmd(%p)\n", scmd); ++ goto tm_retry; ++ } ++ ++ if (task_abort_retries > 1) ++ sdev_printk(KERN_WARNING, sdev, ++ "mpt2sas_scsih_issue_tm: ABORT_TASK: RETRIES (%d):" ++ " scmd(%p)\n", ++ task_abort_retries - 1, scmd); ++ ++ termination_count += le32_to_cpu(mpi_reply->TerminationCount); ++ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); ++ } ++ ++ if (ioc->broadcast_aen_pending) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: loop back due to pending AEN\n", ++ ioc->name, __func__)); ++ ioc->broadcast_aen_pending = 0; ++ goto broadcast_aen_retry; ++ } ++ ++ out: ++ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); ++ out_no_lock: ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s - exit, query_count = %d termination_count = %d\n", ++ ioc->name, __func__, query_count, termination_count)); ++ ++ ioc->broadcast_aen_busy = 0; ++ if (!ioc->shost_recovery) ++ _scsih_ublock_io_all_device(ioc); ++ mutex_unlock(&ioc->tm_cmds.mutex); ++} ++ ++/** ++ * _scsih_sas_discovery_event - handle discovery events ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_discovery_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ Mpi2EventDataSasDiscovery_t *event_data = ++ (Mpi2EventDataSasDiscovery_t *) fw_event->event_data; ++ ++ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) { ++ pr_info(MPT3SAS_FMT "discovery event: (%s)", ioc->name, ++ (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ? ++ "start" : "stop"); ++ if (event_data->DiscoveryStatus) ++ pr_info("discovery_status(0x%08x)", ++ le32_to_cpu(event_data->DiscoveryStatus)); ++ pr_info("\n"); ++ } ++ ++ if (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED && ++ !ioc->sas_hba.num_phys) { ++ if (disable_discovery > 0 && ioc->shost_recovery) { ++ /* Wait for the reset to complete */ ++ while (ioc->shost_recovery) ++ ssleep(1); ++ } ++ _scsih_sas_host_add(ioc); ++ } ++} ++ ++/** ++ * _scsih_ir_fastpath - turn on fastpath for IR physdisk ++ * @ioc: per adapter object ++ * @handle: device handle for physical disk ++ * @phys_disk_num: physical disk number ++ * ++ * Return 0 for success, else failure. ++ */ ++static int ++_scsih_ir_fastpath(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phys_disk_num) ++{ ++ Mpi2RaidActionRequest_t *mpi_request; ++ Mpi2RaidActionReply_t *mpi_reply; ++ u16 smid; ++ u8 issue_reset = 0; ++ int rc = 0; ++ u16 ioc_status; ++ u32 log_info; ++ ++ if (ioc->hba_mpi_version_belonged == MPI2_VERSION) ++ return rc; ++ ++ mutex_lock(&ioc->scsih_cmds.mutex); ++ ++ if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: scsih_cmd in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ioc->scsih_cmds.status = MPT3_CMD_PENDING; ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->scsih_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->scsih_cmds.smid = smid; ++ memset(mpi_request, 0, sizeof(Mpi2RaidActionRequest_t)); ++ ++ mpi_request->Function = MPI2_FUNCTION_RAID_ACTION; ++ mpi_request->Action = MPI2_RAID_ACTION_PHYSDISK_HIDDEN; ++ mpi_request->PhysDiskNum = phys_disk_num; ++ ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT "IR RAID_ACTION: turning fast "\ ++ "path on for handle(0x%04x), phys_disk_num (0x%02x)\n", ioc->name, ++ handle, phys_disk_num)); ++ ++ init_completion(&ioc->scsih_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ); ++ ++ if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ if (!(ioc->scsih_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ rc = -EFAULT; ++ goto out; ++ } ++ ++ if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) { ++ ++ mpi_reply = ioc->scsih_cmds.reply; ++ ioc_status = le16_to_cpu(mpi_reply->IOCStatus); ++ if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) ++ log_info = le32_to_cpu(mpi_reply->IOCLogInfo); ++ else ++ log_info = 0; ++ ioc_status &= MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "IR RAID_ACTION: failed: ioc_status(0x%04x), " ++ "loginfo(0x%08x)!!!\n", ioc->name, ioc_status, ++ log_info)); ++ rc = -EFAULT; ++ } else ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "IR RAID_ACTION: completed successfully\n", ++ ioc->name)); ++ } ++ ++ out: ++ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_unlock(&ioc->scsih_cmds.mutex); ++ ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ return rc; ++} ++ ++/** ++ * _scsih_reprobe_lun - reprobing lun ++ * @sdev: scsi device struct ++ * @no_uld_attach: sdev->no_uld_attach flag setting ++ * ++ **/ ++static void ++_scsih_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach) ++{ ++ int rc; ++ sdev->no_uld_attach = no_uld_attach ? 1 : 0; ++ sdev_printk(KERN_INFO, sdev, "%s raid component\n", ++ sdev->no_uld_attach ? "hidding" : "exposing"); ++ rc = scsi_device_reprobe(sdev); ++} ++ ++/** ++ * _scsih_sas_volume_add - add new volume ++ * @ioc: per adapter object ++ * @element: IR config element data ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_volume_add(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventIrConfigElement_t *element) ++{ ++ struct _raid_device *raid_device; ++ unsigned long flags; ++ u64 wwid; ++ u16 handle = le16_to_cpu(element->VolDevHandle); ++ int rc; ++ ++ mpt2sas_config_get_volume_wwid(ioc, handle, &wwid); ++ if (!wwid) { ++ pr_err(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = _scsih_raid_device_find_by_wwid(ioc, wwid); ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ ++ if (raid_device) ++ return; ++ ++ raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL); ++ if (!raid_device) { ++ pr_err(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ raid_device->id = ioc->sas_id++; ++ raid_device->channel = RAID_CHANNEL; ++ raid_device->handle = handle; ++ raid_device->wwid = wwid; ++ _scsih_raid_device_add(ioc, raid_device); ++ if (!ioc->wait_for_discovery_to_complete) { ++ rc = scsi_add_device(ioc->shost, RAID_CHANNEL, ++ raid_device->id, 0); ++ if (rc) ++ _scsih_raid_device_remove(ioc, raid_device); ++ } else { ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ _scsih_determine_boot_device(ioc, raid_device, 1); ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ } ++} ++ ++/** ++ * _scsih_sas_volume_delete - delete volume ++ * @ioc: per adapter object ++ * @handle: volume device handle ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_volume_delete(struct MPT3SAS_ADAPTER *ioc, u16 handle) ++{ ++ struct _raid_device *raid_device; ++ unsigned long flags; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct scsi_target *starget = NULL; ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = mpt2sas_raid_device_find_by_handle(ioc, handle); ++ if (raid_device) { ++ if (raid_device->starget) { ++ starget = raid_device->starget; ++ sas_target_priv_data = starget->hostdata; ++ sas_target_priv_data->deleted = 1; ++ } ++ pr_info(MPT3SAS_FMT "removing handle(0x%04x), wwid(0x%016llx)\n", ++ ioc->name, raid_device->handle, ++ (unsigned long long) raid_device->wwid); ++ list_del(&raid_device->list); ++ kfree(raid_device); ++ } ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ if (starget) ++ scsi_remove_target(&starget->dev); ++} ++ ++/** ++ * _scsih_sas_pd_expose - expose pd component to /dev/sdX ++ * @ioc: per adapter object ++ * @element: IR config element data ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_pd_expose(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventIrConfigElement_t *element) ++{ ++ struct _sas_device *sas_device; ++ struct scsi_target *starget = NULL; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ unsigned long flags; ++ u16 handle = le16_to_cpu(element->PhysDiskDevHandle); ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (sas_device) { ++ sas_device->volume_handle = 0; ++ sas_device->volume_wwid = 0; ++ clear_bit(handle, ioc->pd_handles); ++ if (sas_device->starget && sas_device->starget->hostdata) { ++ starget = sas_device->starget; ++ sas_target_priv_data = starget->hostdata; ++ sas_target_priv_data->flags &= ++ ~MPT_TARGET_FLAGS_RAID_COMPONENT; ++ } ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ if (!sas_device) ++ return; ++ ++ /* exposing raid component */ ++ if (starget) ++ starget_for_each_device(starget, NULL, _scsih_reprobe_lun); ++ ++ sas_device_put(sas_device); ++} ++ ++/** ++ * _scsih_sas_pd_hide - hide pd component from /dev/sdX ++ * @ioc: per adapter object ++ * @element: IR config element data ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_pd_hide(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventIrConfigElement_t *element) ++{ ++ struct _sas_device *sas_device; ++ struct scsi_target *starget = NULL; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ unsigned long flags; ++ u16 handle = le16_to_cpu(element->PhysDiskDevHandle); ++ u16 volume_handle = 0; ++ u64 volume_wwid = 0; ++ ++ mpt2sas_config_get_volume_handle(ioc, handle, &volume_handle); ++ if (volume_handle) ++ mpt2sas_config_get_volume_wwid(ioc, volume_handle, ++ &volume_wwid); ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (sas_device) { ++ set_bit(handle, ioc->pd_handles); ++ if (sas_device->starget && sas_device->starget->hostdata) { ++ starget = sas_device->starget; ++ sas_target_priv_data = starget->hostdata; ++ sas_target_priv_data->flags |= ++ MPT_TARGET_FLAGS_RAID_COMPONENT; ++ sas_device->volume_handle = volume_handle; ++ sas_device->volume_wwid = volume_wwid; ++ } ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ if (!sas_device) ++ return; ++ ++ /* hiding raid component */ ++ _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum); ++ ++ if (starget) ++ starget_for_each_device(starget, (void *)1, _scsih_reprobe_lun); ++ ++ sas_device_put(sas_device); ++} ++ ++/** ++ * _scsih_sas_pd_delete - delete pd component ++ * @ioc: per adapter object ++ * @element: IR config element data ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_pd_delete(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventIrConfigElement_t *element) ++{ ++ u16 handle = le16_to_cpu(element->PhysDiskDevHandle); ++ ++ _scsih_device_remove_by_handle(ioc, handle); ++} ++ ++/** ++ * _scsih_sas_pd_add - remove pd component ++ * @ioc: per adapter object ++ * @element: IR config element data ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_pd_add(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventIrConfigElement_t *element) ++{ ++ struct _sas_device *sas_device; ++ u16 handle = le16_to_cpu(element->PhysDiskDevHandle); ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ u32 ioc_status; ++ u64 sas_address; ++ u16 parent_handle; ++ ++ set_bit(handle, ioc->pd_handles); ++ ++ sas_device = mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (sas_device) { ++ _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum); ++ sas_device_put(sas_device); ++ return; ++ } ++ ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, ++ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); ++ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) ++ mpt2sas_transport_update_links(ioc, sas_address, handle, ++ sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); ++ ++ _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum); ++ _scsih_add_device(ioc, handle, 0, 1); ++} ++ ++/** ++ * _scsih_sas_ir_config_change_event_debug - debug for IR Config Change events ++ * @ioc: per adapter object ++ * @event_data: event data payload ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_ir_config_change_event_debug(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataIrConfigChangeList_t *event_data) ++{ ++ Mpi2EventIrConfigElement_t *element; ++ u8 element_type; ++ int i; ++ char *reason_str = NULL, *element_str = NULL; ++ ++ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; ++ ++ pr_info(MPT3SAS_FMT "raid config change: (%s), elements(%d)\n", ++ ioc->name, (le32_to_cpu(event_data->Flags) & ++ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? ++ "foreign" : "native", event_data->NumElements); ++ for (i = 0; i < event_data->NumElements; i++, element++) { ++ switch (element->ReasonCode) { ++ case MPI2_EVENT_IR_CHANGE_RC_ADDED: ++ reason_str = "add"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_REMOVED: ++ reason_str = "remove"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE: ++ reason_str = "no change"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_HIDE: ++ reason_str = "hide"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_UNHIDE: ++ reason_str = "unhide"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED: ++ reason_str = "volume_created"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED: ++ reason_str = "volume_deleted"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED: ++ reason_str = "pd_created"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED: ++ reason_str = "pd_deleted"; ++ break; ++ default: ++ reason_str = "unknown reason"; ++ break; ++ } ++ element_type = le16_to_cpu(element->ElementFlags) & ++ MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK; ++ switch (element_type) { ++ case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT: ++ element_str = "volume"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT: ++ element_str = "phys disk"; ++ break; ++ case MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT: ++ element_str = "hot spare"; ++ break; ++ default: ++ element_str = "unknown element"; ++ break; ++ } ++ pr_info("\t(%s:%s), vol handle(0x%04x), " \ ++ "pd handle(0x%04x), pd num(0x%02x)\n", element_str, ++ reason_str, le16_to_cpu(element->VolDevHandle), ++ le16_to_cpu(element->PhysDiskDevHandle), ++ element->PhysDiskNum); ++ } ++} ++ ++/** ++ * _scsih_sas_ir_config_change_event - handle ir configuration change events ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_ir_config_change_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ Mpi2EventIrConfigElement_t *element; ++ int i; ++ u8 foreign_config; ++ Mpi2EventDataIrConfigChangeList_t *event_data = ++ (Mpi2EventDataIrConfigChangeList_t *) ++ fw_event->event_data; ++ ++ if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) && ++ (!ioc->hide_ir_msg)) ++ _scsih_sas_ir_config_change_event_debug(ioc, event_data); ++ ++ foreign_config = (le32_to_cpu(event_data->Flags) & ++ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? 1 : 0; ++ ++ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; ++ if (ioc->shost_recovery && ++ ioc->hba_mpi_version_belonged != MPI2_VERSION) { ++ for (i = 0; i < event_data->NumElements; i++, element++) { ++ if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_HIDE) ++ _scsih_ir_fastpath(ioc, ++ le16_to_cpu(element->PhysDiskDevHandle), ++ element->PhysDiskNum); ++ } ++ return; ++ } ++ ++ for (i = 0; i < event_data->NumElements; i++, element++) { ++ ++ switch (element->ReasonCode) { ++ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED: ++ case MPI2_EVENT_IR_CHANGE_RC_ADDED: ++ if (!foreign_config) ++ _scsih_sas_volume_add(ioc, element); ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED: ++ case MPI2_EVENT_IR_CHANGE_RC_REMOVED: ++ if (!foreign_config) ++ _scsih_sas_volume_delete(ioc, ++ le16_to_cpu(element->VolDevHandle)); ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED: ++ if (!ioc->is_warpdrive) ++ _scsih_sas_pd_hide(ioc, element); ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED: ++ if (!ioc->is_warpdrive) ++ _scsih_sas_pd_expose(ioc, element); ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_HIDE: ++ if (!ioc->is_warpdrive) ++ _scsih_sas_pd_add(ioc, element); ++ break; ++ case MPI2_EVENT_IR_CHANGE_RC_UNHIDE: ++ if (!ioc->is_warpdrive) ++ _scsih_sas_pd_delete(ioc, element); ++ break; ++ } ++ } ++} ++ ++/** ++ * _scsih_sas_ir_volume_event - IR volume event ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_ir_volume_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ u64 wwid; ++ unsigned long flags; ++ struct _raid_device *raid_device; ++ u16 handle; ++ u32 state; ++ int rc; ++ Mpi2EventDataIrVolume_t *event_data = ++ (Mpi2EventDataIrVolume_t *) fw_event->event_data; ++ ++ if (ioc->shost_recovery) ++ return; ++ ++ if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED) ++ return; ++ ++ handle = le16_to_cpu(event_data->VolDevHandle); ++ state = le32_to_cpu(event_data->NewValue); ++ if (!ioc->hide_ir_msg) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: handle(0x%04x), old(0x%08x), new(0x%08x)\n", ++ ioc->name, __func__, handle, ++ le32_to_cpu(event_data->PreviousValue), state)); ++ switch (state) { ++ case MPI2_RAID_VOL_STATE_MISSING: ++ case MPI2_RAID_VOL_STATE_FAILED: ++ _scsih_sas_volume_delete(ioc, handle); ++ break; ++ ++ case MPI2_RAID_VOL_STATE_ONLINE: ++ case MPI2_RAID_VOL_STATE_DEGRADED: ++ case MPI2_RAID_VOL_STATE_OPTIMAL: ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = mpt2sas_raid_device_find_by_handle(ioc, handle); ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ ++ if (raid_device) ++ break; ++ ++ mpt2sas_config_get_volume_wwid(ioc, handle, &wwid); ++ if (!wwid) { ++ pr_err(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__); ++ break; ++ } ++ ++ raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL); ++ if (!raid_device) { ++ pr_err(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ioc->name, ++ __FILE__, __LINE__, __func__); ++ break; ++ } ++ ++ raid_device->id = ioc->sas_id++; ++ raid_device->channel = RAID_CHANNEL; ++ raid_device->handle = handle; ++ raid_device->wwid = wwid; ++ _scsih_raid_device_add(ioc, raid_device); ++ rc = scsi_add_device(ioc->shost, RAID_CHANNEL, ++ raid_device->id, 0); ++ if (rc) ++ _scsih_raid_device_remove(ioc, raid_device); ++ break; ++ ++ case MPI2_RAID_VOL_STATE_INITIALIZING: ++ default: ++ break; ++ } ++} ++ ++/** ++ * _scsih_sas_ir_physical_disk_event - PD event ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_ir_physical_disk_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ u16 handle, parent_handle; ++ u32 state; ++ struct _sas_device *sas_device; ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ u32 ioc_status; ++ Mpi2EventDataIrPhysicalDisk_t *event_data = ++ (Mpi2EventDataIrPhysicalDisk_t *) fw_event->event_data; ++ u64 sas_address; ++ ++ if (ioc->shost_recovery) ++ return; ++ ++ if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED) ++ return; ++ ++ handle = le16_to_cpu(event_data->PhysDiskDevHandle); ++ state = le32_to_cpu(event_data->NewValue); ++ ++ if (!ioc->hide_ir_msg) ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: handle(0x%04x), old(0x%08x), new(0x%08x)\n", ++ ioc->name, __func__, handle, ++ le32_to_cpu(event_data->PreviousValue), state)); ++ ++ switch (state) { ++ case MPI2_RAID_PD_STATE_ONLINE: ++ case MPI2_RAID_PD_STATE_DEGRADED: ++ case MPI2_RAID_PD_STATE_REBUILDING: ++ case MPI2_RAID_PD_STATE_OPTIMAL: ++ case MPI2_RAID_PD_STATE_HOT_SPARE: ++ ++ if (!ioc->is_warpdrive) ++ set_bit(handle, ioc->pd_handles); ++ ++ sas_device = mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (sas_device) { ++ sas_device_put(sas_device); ++ return; ++ } ++ ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, ++ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ++ handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); ++ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) ++ mpt2sas_transport_update_links(ioc, sas_address, handle, ++ sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); ++ ++ _scsih_add_device(ioc, handle, 0, 1); ++ ++ break; ++ ++ case MPI2_RAID_PD_STATE_OFFLINE: ++ case MPI2_RAID_PD_STATE_NOT_CONFIGURED: ++ case MPI2_RAID_PD_STATE_NOT_COMPATIBLE: ++ default: ++ break; ++ } ++} ++ ++/** ++ * _scsih_sas_ir_operation_status_event_debug - debug for IR op event ++ * @ioc: per adapter object ++ * @event_data: event data payload ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_ir_operation_status_event_debug(struct MPT3SAS_ADAPTER *ioc, ++ Mpi2EventDataIrOperationStatus_t *event_data) ++{ ++ char *reason_str = NULL; ++ ++ switch (event_data->RAIDOperation) { ++ case MPI2_EVENT_IR_RAIDOP_RESYNC: ++ reason_str = "resync"; ++ break; ++ case MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION: ++ reason_str = "online capacity expansion"; ++ break; ++ case MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK: ++ reason_str = "consistency check"; ++ break; ++ case MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT: ++ reason_str = "background init"; ++ break; ++ case MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT: ++ reason_str = "make data consistent"; ++ break; ++ } ++ ++ if (!reason_str) ++ return; ++ ++ pr_info(MPT3SAS_FMT "raid operational status: (%s)" \ ++ "\thandle(0x%04x), percent complete(%d)\n", ++ ioc->name, reason_str, ++ le16_to_cpu(event_data->VolDevHandle), ++ event_data->PercentComplete); ++} ++ ++/** ++ * _scsih_sas_ir_operation_status_event - handle RAID operation events ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_sas_ir_operation_status_event(struct MPT3SAS_ADAPTER *ioc, ++ struct fw_event_work *fw_event) ++{ ++ Mpi2EventDataIrOperationStatus_t *event_data = ++ (Mpi2EventDataIrOperationStatus_t *) ++ fw_event->event_data; ++ static struct _raid_device *raid_device; ++ unsigned long flags; ++ u16 handle; ++ ++ if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) && ++ (!ioc->hide_ir_msg)) ++ _scsih_sas_ir_operation_status_event_debug(ioc, ++ event_data); ++ ++ /* code added for raid transport support */ ++ if (event_data->RAIDOperation == MPI2_EVENT_IR_RAIDOP_RESYNC) { ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ handle = le16_to_cpu(event_data->VolDevHandle); ++ raid_device = mpt2sas_raid_device_find_by_handle(ioc, handle); ++ if (raid_device) ++ raid_device->percent_complete = ++ event_data->PercentComplete; ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ } ++} ++ ++/** ++ * _scsih_prep_device_scan - initialize parameters prior to device scan ++ * @ioc: per adapter object ++ * ++ * Set the deleted flag prior to device scan. If the device is found during ++ * the scan, then we clear the deleted flag. ++ */ ++static void ++_scsih_prep_device_scan(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct MPT3SAS_DEVICE *sas_device_priv_data; ++ struct scsi_device *sdev; ++ ++ shost_for_each_device(sdev, ioc->shost) { ++ sas_device_priv_data = sdev->hostdata; ++ if (sas_device_priv_data && sas_device_priv_data->sas_target) ++ sas_device_priv_data->sas_target->deleted = 1; ++ } ++} ++ ++/** ++ * _scsih_mark_responding_sas_device - mark a sas_devices as responding ++ * @ioc: per adapter object ++ * @sas_device_pg0: SAS Device page 0 ++ * ++ * After host reset, find out whether devices are still responding. ++ * Used in _scsih_remove_unresponsive_sas_devices. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, ++Mpi2SasDevicePage0_t *sas_device_pg0) ++{ ++ struct MPT3SAS_TARGET *sas_target_priv_data = NULL; ++ struct scsi_target *starget; ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ list_for_each_entry(sas_device, &ioc->sas_device_list, list) { ++ if ((sas_device->sas_address == sas_device_pg0->SASAddress) && ++ (sas_device->slot == sas_device_pg0->Slot)) { ++ sas_device->responding = 1; ++ starget = sas_device->starget; ++ if (starget && starget->hostdata) { ++ sas_target_priv_data = starget->hostdata; ++ sas_target_priv_data->tm_busy = 0; ++ sas_target_priv_data->deleted = 0; ++ } else ++ sas_target_priv_data = NULL; ++ if (starget) { ++ starget_printk(KERN_INFO, starget, ++ "handle(0x%04x), sas_addr(0x%016llx)\n", ++ sas_device_pg0->DevHandle, ++ (unsigned long long) ++ sas_device->sas_address); ++ ++ if (sas_device->enclosure_handle != 0) ++ starget_printk(KERN_INFO, starget, ++ "enclosure logical id(0x%016llx)," ++ " slot(%d)\n", ++ (unsigned long long) ++ sas_device->enclosure_logical_id, ++ sas_device->slot); ++ } ++ if (sas_device_pg0->Flags & ++ MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { ++ sas_device->enclosure_level = ++ le16_to_cpu(sas_device_pg0->EnclosureLevel); ++ memcpy(&sas_device->connector_name[0], ++ &sas_device_pg0->ConnectorName[0], 4); ++ } else { ++ sas_device->enclosure_level = 0; ++ sas_device->connector_name[0] = '\0'; ++ } ++ ++ if (sas_device->handle == sas_device_pg0->DevHandle) ++ goto out; ++ pr_info("\thandle changed from(0x%04x)!!!\n", ++ sas_device->handle); ++ sas_device->handle = sas_device_pg0->DevHandle; ++ if (sas_target_priv_data) ++ sas_target_priv_data->handle = ++ sas_device_pg0->DevHandle; ++ goto out; ++ } ++ } ++ out: ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++} ++ ++/** ++ * _scsih_search_responding_sas_devices - ++ * @ioc: per adapter object ++ * ++ * After host reset, find out whether devices are still responding. ++ * If not remove. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_search_responding_sas_devices(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 ioc_status; ++ u16 handle; ++ u32 device_info; ++ ++ pr_info(MPT3SAS_FMT "search for end-devices: start\n", ioc->name); ++ ++ if (list_empty(&ioc->sas_device_list)) ++ goto out; ++ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, ++ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, ++ handle))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ break; ++ handle = sas_device_pg0.DevHandle = ++ le16_to_cpu(sas_device_pg0.DevHandle); ++ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); ++ if (!(_scsih_is_end_device(device_info))) ++ continue; ++ sas_device_pg0.SASAddress = ++ le64_to_cpu(sas_device_pg0.SASAddress); ++ sas_device_pg0.Slot = le16_to_cpu(sas_device_pg0.Slot); ++ _scsih_mark_responding_sas_device(ioc, &sas_device_pg0); ++ } ++ ++ out: ++ pr_info(MPT3SAS_FMT "search for end-devices: complete\n", ++ ioc->name); ++} ++ ++/** ++ * _scsih_mark_responding_raid_device - mark a raid_device as responding ++ * @ioc: per adapter object ++ * @wwid: world wide identifier for raid volume ++ * @handle: device handle ++ * ++ * After host reset, find out whether devices are still responding. ++ * Used in _scsih_remove_unresponsive_raid_devices. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_mark_responding_raid_device(struct MPT3SAS_ADAPTER *ioc, u64 wwid, ++ u16 handle) ++{ ++ struct MPT3SAS_TARGET *sas_target_priv_data = NULL; ++ struct scsi_target *starget; ++ struct _raid_device *raid_device; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ list_for_each_entry(raid_device, &ioc->raid_device_list, list) { ++ if (raid_device->wwid == wwid && raid_device->starget) { ++ starget = raid_device->starget; ++ if (starget && starget->hostdata) { ++ sas_target_priv_data = starget->hostdata; ++ sas_target_priv_data->deleted = 0; ++ } else ++ sas_target_priv_data = NULL; ++ raid_device->responding = 1; ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ starget_printk(KERN_INFO, raid_device->starget, ++ "handle(0x%04x), wwid(0x%016llx)\n", handle, ++ (unsigned long long)raid_device->wwid); ++ ++ /* ++ * WARPDRIVE: The handles of the PDs might have changed ++ * across the host reset so re-initialize the ++ * required data for Direct IO ++ */ ++ mpt2sas_init_warpdrive_properties(ioc, raid_device); ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ if (raid_device->handle == handle) { ++ spin_unlock_irqrestore(&ioc->raid_device_lock, ++ flags); ++ return; ++ } ++ pr_info("\thandle changed from(0x%04x)!!!\n", ++ raid_device->handle); ++ raid_device->handle = handle; ++ if (sas_target_priv_data) ++ sas_target_priv_data->handle = handle; ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ return; ++ } ++ } ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++} ++ ++/** ++ * _scsih_search_responding_raid_devices - ++ * @ioc: per adapter object ++ * ++ * After host reset, find out whether devices are still responding. ++ * If not remove. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_search_responding_raid_devices(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2RaidVolPage1_t volume_pg1; ++ Mpi2RaidVolPage0_t volume_pg0; ++ Mpi2RaidPhysDiskPage0_t pd_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 ioc_status; ++ u16 handle; ++ u8 phys_disk_num; ++ ++ if (!ioc->ir_firmware) ++ return; ++ ++ pr_info(MPT3SAS_FMT "search for raid volumes: start\n", ++ ioc->name); ++ ++ if (list_empty(&ioc->raid_device_list)) ++ goto out; ++ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, ++ &volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ break; ++ handle = le16_to_cpu(volume_pg1.DevHandle); ++ ++ if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, ++ &volume_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, ++ sizeof(Mpi2RaidVolPage0_t))) ++ continue; ++ ++ if (volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_OPTIMAL || ++ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_ONLINE || ++ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_DEGRADED) ++ _scsih_mark_responding_raid_device(ioc, ++ le64_to_cpu(volume_pg1.WWID), handle); ++ } ++ ++ /* refresh the pd_handles */ ++ if (!ioc->is_warpdrive) { ++ phys_disk_num = 0xFF; ++ memset(ioc->pd_handles, 0, ioc->pd_handles_sz); ++ while (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, ++ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM, ++ phys_disk_num))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ break; ++ phys_disk_num = pd_pg0.PhysDiskNum; ++ handle = le16_to_cpu(pd_pg0.DevHandle); ++ set_bit(handle, ioc->pd_handles); ++ } ++ } ++ out: ++ pr_info(MPT3SAS_FMT "search for responding raid volumes: complete\n", ++ ioc->name); ++} ++ ++/** ++ * _scsih_mark_responding_expander - mark a expander as responding ++ * @ioc: per adapter object ++ * @sas_address: sas address ++ * @handle: ++ * ++ * After host reset, find out whether devices are still responding. ++ * Used in _scsih_remove_unresponsive_expanders. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_mark_responding_expander(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, ++ u16 handle) ++{ ++ struct _sas_node *sas_expander; ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { ++ if (sas_expander->sas_address != sas_address) ++ continue; ++ sas_expander->responding = 1; ++ if (sas_expander->handle == handle) ++ goto out; ++ pr_info("\texpander(0x%016llx): handle changed" \ ++ " from(0x%04x) to (0x%04x)!!!\n", ++ (unsigned long long)sas_expander->sas_address, ++ sas_expander->handle, handle); ++ sas_expander->handle = handle; ++ for (i = 0 ; i < sas_expander->num_phys ; i++) ++ sas_expander->phy[i].handle = handle; ++ goto out; ++ } ++ out: ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++} ++ ++/** ++ * _scsih_search_responding_expanders - ++ * @ioc: per adapter object ++ * ++ * After host reset, find out whether devices are still responding. ++ * If not remove. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_search_responding_expanders(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2ExpanderPage0_t expander_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 ioc_status; ++ u64 sas_address; ++ u16 handle; ++ ++ pr_info(MPT3SAS_FMT "search for expanders: start\n", ioc->name); ++ ++ if (list_empty(&ioc->sas_expander_list)) ++ goto out; ++ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, ++ MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) { ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ++ break; ++ ++ handle = le16_to_cpu(expander_pg0.DevHandle); ++ sas_address = le64_to_cpu(expander_pg0.SASAddress); ++ pr_info("\texpander present: handle(0x%04x), sas_addr(0x%016llx)\n", ++ handle, ++ (unsigned long long)sas_address); ++ _scsih_mark_responding_expander(ioc, sas_address, handle); ++ } ++ ++ out: ++ pr_info(MPT3SAS_FMT "search for expanders: complete\n", ioc->name); ++} ++ ++/** ++ * _scsih_remove_unresponding_sas_devices - removing unresponding devices ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_remove_unresponding_sas_devices(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct _sas_device *sas_device, *sas_device_next; ++ struct _sas_node *sas_expander, *sas_expander_next; ++ struct _raid_device *raid_device, *raid_device_next; ++ struct list_head tmp_list; ++ unsigned long flags; ++ LIST_HEAD(head); ++ ++ pr_info(MPT3SAS_FMT "removing unresponding devices: start\n", ++ ioc->name); ++ ++ /* removing unresponding end devices */ ++ pr_info(MPT3SAS_FMT "removing unresponding devices: end-devices\n", ++ ioc->name); ++ /* ++ * Iterate, pulling off devices marked as non-responding. We become the ++ * owner for the reference the list had on any object we prune. ++ */ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ list_for_each_entry_safe(sas_device, sas_device_next, ++ &ioc->sas_device_list, list) { ++ if (!sas_device->responding) ++ list_move_tail(&sas_device->list, &head); ++ else ++ sas_device->responding = 0; ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ /* ++ * Now, uninitialize and remove the unresponding devices we pruned. ++ */ ++ list_for_each_entry_safe(sas_device, sas_device_next, &head, list) { ++ _scsih_remove_device(ioc, sas_device); ++ list_del_init(&sas_device->list); ++ sas_device_put(sas_device); ++ } ++ ++ /* removing unresponding volumes */ ++ if (ioc->ir_firmware) { ++ pr_info(MPT3SAS_FMT "removing unresponding devices: volumes\n", ++ ioc->name); ++ list_for_each_entry_safe(raid_device, raid_device_next, ++ &ioc->raid_device_list, list) { ++ if (!raid_device->responding) ++ _scsih_sas_volume_delete(ioc, ++ raid_device->handle); ++ else ++ raid_device->responding = 0; ++ } ++ } ++ ++ /* removing unresponding expanders */ ++ pr_info(MPT3SAS_FMT "removing unresponding devices: expanders\n", ++ ioc->name); ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ INIT_LIST_HEAD(&tmp_list); ++ list_for_each_entry_safe(sas_expander, sas_expander_next, ++ &ioc->sas_expander_list, list) { ++ if (!sas_expander->responding) ++ list_move_tail(&sas_expander->list, &tmp_list); ++ else ++ sas_expander->responding = 0; ++ } ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ list_for_each_entry_safe(sas_expander, sas_expander_next, &tmp_list, ++ list) { ++ list_del(&sas_expander->list); ++ _scsih_expander_node_remove(ioc, sas_expander); ++ } ++ ++ pr_info(MPT3SAS_FMT "removing unresponding devices: complete\n", ++ ioc->name); ++ ++ /* unblock devices */ ++ _scsih_ublock_io_all_device(ioc); ++} ++ ++static void ++_scsih_refresh_expander_links(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_node *sas_expander, u16 handle) ++{ ++ Mpi2ExpanderPage1_t expander_pg1; ++ Mpi2ConfigReply_t mpi_reply; ++ int i; ++ ++ for (i = 0 ; i < sas_expander->num_phys ; i++) { ++ if ((mpt2sas_config_get_expander_pg1(ioc, &mpi_reply, ++ &expander_pg1, i, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return; ++ } ++ ++ mpt2sas_transport_update_links(ioc, sas_expander->sas_address, ++ le16_to_cpu(expander_pg1.AttachedDevHandle), i, ++ expander_pg1.NegotiatedLinkRate >> 4); ++ } ++} ++ ++/** ++ * _scsih_scan_for_devices_after_reset - scan for devices after host reset ++ * @ioc: per adapter object ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2ExpanderPage0_t expander_pg0; ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ Mpi2RaidVolPage1_t volume_pg1; ++ Mpi2RaidVolPage0_t volume_pg0; ++ Mpi2RaidPhysDiskPage0_t pd_pg0; ++ Mpi2EventIrConfigElement_t element; ++ Mpi2ConfigReply_t mpi_reply; ++ u8 phys_disk_num; ++ u16 ioc_status; ++ u16 handle, parent_handle; ++ u64 sas_address; ++ struct _sas_device *sas_device; ++ struct _sas_node *expander_device; ++ static struct _raid_device *raid_device; ++ u8 retry_count; ++ unsigned long flags; ++ ++ pr_info(MPT3SAS_FMT "scan devices: start\n", ioc->name); ++ ++ _scsih_sas_host_refresh(ioc); ++ ++ pr_info(MPT3SAS_FMT "\tscan devices: expanders start\n", ioc->name); ++ ++ /* expanders */ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, ++ MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_info(MPT3SAS_FMT "\tbreak from expander scan: " \ ++ "ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, ioc_status, ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ break; ++ } ++ handle = le16_to_cpu(expander_pg0.DevHandle); ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ expander_device = mpt2sas_scsih_expander_find_by_sas_address( ++ ioc, le64_to_cpu(expander_pg0.SASAddress)); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ if (expander_device) ++ _scsih_refresh_expander_links(ioc, expander_device, ++ handle); ++ else { ++ pr_info(MPT3SAS_FMT "\tBEFORE adding expander: " \ ++ "handle (0x%04x), sas_addr(0x%016llx)\n", ioc->name, ++ handle, (unsigned long long) ++ le64_to_cpu(expander_pg0.SASAddress)); ++ _scsih_expander_add(ioc, handle); ++ pr_info(MPT3SAS_FMT "\tAFTER adding expander: " \ ++ "handle (0x%04x), sas_addr(0x%016llx)\n", ioc->name, ++ handle, (unsigned long long) ++ le64_to_cpu(expander_pg0.SASAddress)); ++ } ++ } ++ ++ pr_info(MPT3SAS_FMT "\tscan devices: expanders complete\n", ++ ioc->name); ++ ++ if (!ioc->ir_firmware) ++ goto skip_to_sas; ++ ++ pr_info(MPT3SAS_FMT "\tscan devices: phys disk start\n", ioc->name); ++ ++ /* phys disk */ ++ phys_disk_num = 0xFF; ++ while (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, ++ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM, ++ phys_disk_num))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_info(MPT3SAS_FMT "\tbreak from phys disk scan: "\ ++ "ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, ioc_status, ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ break; ++ } ++ phys_disk_num = pd_pg0.PhysDiskNum; ++ handle = le16_to_cpu(pd_pg0.DevHandle); ++ sas_device = mpt2sas_get_sdev_by_handle(ioc, handle); ++ if (sas_device) { ++ sas_device_put(sas_device); ++ continue; ++ } ++ if (mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, ++ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ++ handle) != 0) ++ continue; ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_info(MPT3SAS_FMT "\tbreak from phys disk scan " \ ++ "ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, ioc_status, ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ break; ++ } ++ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); ++ if (!_scsih_get_sas_address(ioc, parent_handle, ++ &sas_address)) { ++ pr_info(MPT3SAS_FMT "\tBEFORE adding phys disk: " \ ++ " handle (0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, handle, (unsigned long long) ++ le64_to_cpu(sas_device_pg0.SASAddress)); ++ mpt2sas_transport_update_links(ioc, sas_address, ++ handle, sas_device_pg0.PhyNum, ++ MPI2_SAS_NEG_LINK_RATE_1_5); ++ set_bit(handle, ioc->pd_handles); ++ retry_count = 0; ++ /* This will retry adding the end device. ++ * _scsih_add_device() will decide on retries and ++ * return "1" when it should be retried ++ */ ++ while (_scsih_add_device(ioc, handle, retry_count++, ++ 1)) { ++ ssleep(1); ++ } ++ pr_info(MPT3SAS_FMT "\tAFTER adding phys disk: " \ ++ " handle (0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, handle, (unsigned long long) ++ le64_to_cpu(sas_device_pg0.SASAddress)); ++ } ++ } ++ ++ pr_info(MPT3SAS_FMT "\tscan devices: phys disk complete\n", ++ ioc->name); ++ ++ pr_info(MPT3SAS_FMT "\tscan devices: volumes start\n", ioc->name); ++ ++ /* volumes */ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, ++ &volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_info(MPT3SAS_FMT "\tbreak from volume scan: " \ ++ "ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, ioc_status, ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ break; ++ } ++ handle = le16_to_cpu(volume_pg1.DevHandle); ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = _scsih_raid_device_find_by_wwid(ioc, ++ le64_to_cpu(volume_pg1.WWID)); ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ if (raid_device) ++ continue; ++ if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, ++ &volume_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, ++ sizeof(Mpi2RaidVolPage0_t))) ++ continue; ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_info(MPT3SAS_FMT "\tbreak from volume scan: " \ ++ "ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, ioc_status, ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ break; ++ } ++ if (volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_OPTIMAL || ++ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_ONLINE || ++ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_DEGRADED) { ++ memset(&element, 0, sizeof(Mpi2EventIrConfigElement_t)); ++ element.ReasonCode = MPI2_EVENT_IR_CHANGE_RC_ADDED; ++ element.VolDevHandle = volume_pg1.DevHandle; ++ pr_info(MPT3SAS_FMT ++ "\tBEFORE adding volume: handle (0x%04x)\n", ++ ioc->name, volume_pg1.DevHandle); ++ _scsih_sas_volume_add(ioc, &element); ++ pr_info(MPT3SAS_FMT ++ "\tAFTER adding volume: handle (0x%04x)\n", ++ ioc->name, volume_pg1.DevHandle); ++ } ++ } ++ ++ pr_info(MPT3SAS_FMT "\tscan devices: volumes complete\n", ++ ioc->name); ++ ++ skip_to_sas: ++ ++ pr_info(MPT3SAS_FMT "\tscan devices: end devices start\n", ++ ioc->name); ++ ++ /* sas devices */ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, ++ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, ++ handle))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_info(MPT3SAS_FMT "\tbreak from end device scan:"\ ++ " ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, ioc_status, ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ break; ++ } ++ handle = le16_to_cpu(sas_device_pg0.DevHandle); ++ if (!(_scsih_is_end_device( ++ le32_to_cpu(sas_device_pg0.DeviceInfo)))) ++ continue; ++ sas_device = mpt2sas_get_sdev_by_addr(ioc, ++ le64_to_cpu(sas_device_pg0.SASAddress)); ++ if (sas_device) { ++ sas_device_put(sas_device); ++ continue; ++ } ++ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); ++ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) { ++ pr_info(MPT3SAS_FMT "\tBEFORE adding end device: " \ ++ "handle (0x%04x), sas_addr(0x%016llx)\n", ioc->name, ++ handle, (unsigned long long) ++ le64_to_cpu(sas_device_pg0.SASAddress)); ++ mpt2sas_transport_update_links(ioc, sas_address, handle, ++ sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); ++ retry_count = 0; ++ /* This will retry adding the end device. ++ * _scsih_add_device() will decide on retries and ++ * return "1" when it should be retried ++ */ ++ while (_scsih_add_device(ioc, handle, retry_count++, ++ 0)) { ++ ssleep(1); ++ } ++ pr_info(MPT3SAS_FMT "\tAFTER adding end device: " \ ++ "handle (0x%04x), sas_addr(0x%016llx)\n", ioc->name, ++ handle, (unsigned long long) ++ le64_to_cpu(sas_device_pg0.SASAddress)); ++ } ++ } ++ pr_info(MPT3SAS_FMT "\tscan devices: end devices complete\n", ++ ioc->name); ++ ++ pr_info(MPT3SAS_FMT "scan devices: complete\n", ioc->name); ++} ++/** ++ * mpt2sas_scsih_reset_handler - reset callback handler (for scsih) ++ * @ioc: per adapter object ++ * @reset_phase: phase ++ * ++ * The handler for doing any required cleanup or initialization. ++ * ++ * The reset phase can be MPT3_IOC_PRE_RESET, MPT3_IOC_AFTER_RESET, ++ * MPT3_IOC_DONE_RESET ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_scsih_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase) ++{ ++ switch (reset_phase) { ++ case MPT3_IOC_PRE_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_PRE_RESET\n", ioc->name, __func__)); ++ break; ++ case MPT3_IOC_AFTER_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_AFTER_RESET\n", ioc->name, __func__)); ++ if (ioc->scsih_cmds.status & MPT3_CMD_PENDING) { ++ ioc->scsih_cmds.status |= MPT3_CMD_RESET; ++ mpt2sas_base_free_smid(ioc, ioc->scsih_cmds.smid); ++ complete(&ioc->scsih_cmds.done); ++ } ++ if (ioc->tm_cmds.status & MPT3_CMD_PENDING) { ++ ioc->tm_cmds.status |= MPT3_CMD_RESET; ++ mpt2sas_base_free_smid(ioc, ioc->tm_cmds.smid); ++ complete(&ioc->tm_cmds.done); ++ } ++ ++ _scsih_fw_event_cleanup_queue(ioc); ++ _scsih_flush_running_cmds(ioc); ++ break; ++ case MPT3_IOC_DONE_RESET: ++ dtmprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: MPT3_IOC_DONE_RESET\n", ioc->name, __func__)); ++ if ((!ioc->is_driver_loading) && !(disable_discovery > 0 && ++ !ioc->sas_hba.num_phys)) { ++ _scsih_prep_device_scan(ioc); ++ _scsih_search_responding_sas_devices(ioc); ++ _scsih_search_responding_raid_devices(ioc); ++ _scsih_search_responding_expanders(ioc); ++ _scsih_error_recovery_delete_devices(ioc); ++ } ++ break; ++ } ++} ++ ++/** ++ * _mpt2sas_fw_work - delayed task for processing firmware events ++ * @ioc: per adapter object ++ * @fw_event: The fw_event_work object ++ * Context: user. ++ * ++ * Return nothing. ++ */ ++static void ++_mpt2sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) ++{ ++ _scsih_fw_event_del_from_list(ioc, fw_event); ++ ++ /* the queue is being flushed so ignore this event */ ++ if (ioc->remove_host || ioc->pci_error_recovery) { ++ fw_event_work_put(fw_event); ++ return; ++ } ++ ++ switch (fw_event->event) { ++ case MPT3SAS_PROCESS_TRIGGER_DIAG: ++ mpt2sas_process_trigger_data(ioc, ++ (struct SL_WH_TRIGGERS_EVENT_DATA_T *) ++ fw_event->event_data); ++ break; ++ case MPT3SAS_REMOVE_UNRESPONDING_DEVICES: ++ while (scsi_host_in_recovery(ioc->shost) || ++ ioc->shost_recovery) { ++ /* ++ * If we're unloading, bail. Otherwise, this can become ++ * an infinite loop. ++ */ ++ if (ioc->remove_host) ++ goto out; ++ ssleep(1); ++ } ++ _scsih_remove_unresponding_sas_devices(ioc); ++ _scsih_scan_for_devices_after_reset(ioc); ++ break; ++ case MPT3SAS_PORT_ENABLE_COMPLETE: ++ ioc->start_scan = 0; ++ if (missing_delay[0] != -1 && missing_delay[1] != -1) ++ mpt2sas_base_update_missing_delay(ioc, missing_delay[0], ++ missing_delay[1]); ++ dewtprintk(ioc, pr_info(MPT3SAS_FMT ++ "port enable: complete from worker thread\n", ++ ioc->name)); ++ break; ++ case MPT3SAS_TURN_ON_PFA_LED: ++ _scsih_turn_on_pfa_led(ioc, fw_event->device_handle); ++ break; ++ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: ++ _scsih_sas_topology_change_event(ioc, fw_event); ++ break; ++ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: ++ _scsih_sas_device_status_change_event(ioc, fw_event); ++ break; ++ case MPI2_EVENT_SAS_DISCOVERY: ++ _scsih_sas_discovery_event(ioc, fw_event); ++ break; ++ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: ++ _scsih_sas_broadcast_primitive_event(ioc, fw_event); ++ break; ++ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: ++ _scsih_sas_enclosure_dev_status_change_event(ioc, ++ fw_event); ++ break; ++ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: ++ _scsih_sas_ir_config_change_event(ioc, fw_event); ++ break; ++ case MPI2_EVENT_IR_VOLUME: ++ _scsih_sas_ir_volume_event(ioc, fw_event); ++ break; ++ case MPI2_EVENT_IR_PHYSICAL_DISK: ++ _scsih_sas_ir_physical_disk_event(ioc, fw_event); ++ break; ++ case MPI2_EVENT_IR_OPERATION_STATUS: ++ _scsih_sas_ir_operation_status_event(ioc, fw_event); ++ break; ++ } ++out: ++ fw_event_work_put(fw_event); ++} ++ ++/** ++ * _firmware_event_work ++ * @ioc: per adapter object ++ * @work: The fw_event_work object ++ * Context: user. ++ * ++ * wrappers for the work thread handling firmware events ++ * ++ * Return nothing. ++ */ ++ ++static void ++_firmware_event_work(struct work_struct *work) ++{ ++ struct fw_event_work *fw_event = container_of(work, ++ struct fw_event_work, work); ++ ++ _mpt2sas_fw_work(fw_event->ioc, fw_event); ++} ++ ++/** ++ * mpt2sas_scsih_event_callback - firmware event handler (called at ISR time) ++ * @ioc: per adapter object ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * Context: interrupt. ++ * ++ * This function merely adds a new work task into ioc->firmware_event_thread. ++ * The tasks are worked from _firmware_event_work in user context. ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, ++ u32 reply) ++{ ++ struct fw_event_work *fw_event; ++ Mpi2EventNotificationReply_t *mpi_reply; ++ u16 event; ++ u16 sz; ++ Mpi26EventDataActiveCableExcept_t *ActiveCableEventData; ++ ++ /* events turned off due to host reset or driver unloading */ ++ if (ioc->remove_host || ioc->pci_error_recovery) ++ return 1; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ ++ if (unlikely(!mpi_reply)) { ++ pr_err(MPT3SAS_FMT "mpi_reply not valid at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return 1; ++ } ++ ++ event = le16_to_cpu(mpi_reply->Event); ++ ++ if (event != MPI2_EVENT_LOG_ENTRY_ADDED) ++ mpt2sas_trigger_event(ioc, event, 0); ++ ++ switch (event) { ++ /* handle these */ ++ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: ++ { ++ Mpi2EventDataSasBroadcastPrimitive_t *baen_data = ++ (Mpi2EventDataSasBroadcastPrimitive_t *) ++ mpi_reply->EventData; ++ ++ if (baen_data->Primitive != ++ MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT) ++ return 1; ++ ++ if (ioc->broadcast_aen_busy) { ++ ioc->broadcast_aen_pending++; ++ return 1; ++ } else ++ ioc->broadcast_aen_busy = 1; ++ break; ++ } ++ ++ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: ++ _scsih_check_topo_delete_events(ioc, ++ (Mpi2EventDataSasTopologyChangeList_t *) ++ mpi_reply->EventData); ++ break; ++ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: ++ _scsih_check_ir_config_unhide_events(ioc, ++ (Mpi2EventDataIrConfigChangeList_t *) ++ mpi_reply->EventData); ++ break; ++ case MPI2_EVENT_IR_VOLUME: ++ _scsih_check_volume_delete_events(ioc, ++ (Mpi2EventDataIrVolume_t *) ++ mpi_reply->EventData); ++ break; ++ case MPI2_EVENT_LOG_ENTRY_ADDED: ++ { ++ Mpi2EventDataLogEntryAdded_t *log_entry; ++ u32 *log_code; ++ ++ if (!ioc->is_warpdrive) ++ break; ++ ++ log_entry = (Mpi2EventDataLogEntryAdded_t *) ++ mpi_reply->EventData; ++ log_code = (u32 *)log_entry->LogData; ++ ++ if (le16_to_cpu(log_entry->LogEntryQualifier) ++ != MPT2_WARPDRIVE_LOGENTRY) ++ break; ++ ++ switch (le32_to_cpu(*log_code)) { ++ case MPT2_WARPDRIVE_LC_SSDT: ++ pr_warn(MPT3SAS_FMT "WarpDrive Warning: " ++ "IO Throttling has occurred in the WarpDrive " ++ "subsystem. Check WarpDrive documentation for " ++ "additional details.\n", ioc->name); ++ break; ++ case MPT2_WARPDRIVE_LC_SSDLW: ++ pr_warn(MPT3SAS_FMT "WarpDrive Warning: " ++ "Program/Erase Cycles for the WarpDrive subsystem " ++ "in degraded range. Check WarpDrive documentation " ++ "for additional details.\n", ioc->name); ++ break; ++ case MPT2_WARPDRIVE_LC_SSDLF: ++ pr_err(MPT3SAS_FMT "WarpDrive Fatal Error: " ++ "There are no Program/Erase Cycles for the " ++ "WarpDrive subsystem. The storage device will be " ++ "in read-only mode. Check WarpDrive documentation " ++ "for additional details.\n", ioc->name); ++ break; ++ case MPT2_WARPDRIVE_LC_BRMF: ++ pr_err(MPT3SAS_FMT "WarpDrive Fatal Error: " ++ "The Backup Rail Monitor has failed on the " ++ "WarpDrive subsystem. Check WarpDrive " ++ "documentation for additional details.\n", ++ ioc->name); ++ break; ++ } ++ ++ break; ++ } ++ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: ++ case MPI2_EVENT_IR_OPERATION_STATUS: ++ case MPI2_EVENT_SAS_DISCOVERY: ++ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: ++ case MPI2_EVENT_IR_PHYSICAL_DISK: ++ break; ++ ++ case MPI2_EVENT_TEMP_THRESHOLD: ++ _scsih_temp_threshold_events(ioc, ++ (Mpi2EventDataTemperature_t *) ++ mpi_reply->EventData); ++ break; ++ case MPI2_EVENT_ACTIVE_CABLE_EXCEPTION: ++ ActiveCableEventData = ++ (Mpi26EventDataActiveCableExcept_t *) mpi_reply->EventData; ++ if (ActiveCableEventData->ReasonCode == ++ MPI26_EVENT_ACTIVE_CABLE_INSUFFICIENT_POWER) ++ pr_info(MPT3SAS_FMT "Currently an active cable with ReceptacleID %d", ++ ioc->name, ActiveCableEventData->ReceptacleID); ++ pr_info("cannot be powered and devices connected to this active cable"); ++ pr_info("will not be seen. This active cable"); ++ pr_info("requires %d mW of power", ++ ActiveCableEventData->ActiveCablePowerRequirement); ++ break; ++ ++ default: /* ignore the rest */ ++ return 1; ++ } ++ ++ sz = le16_to_cpu(mpi_reply->EventDataLength) * 4; ++ fw_event = alloc_fw_event_work(sz); ++ if (!fw_event) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return 1; ++ } ++ ++ memcpy(fw_event->event_data, mpi_reply->EventData, sz); ++ fw_event->ioc = ioc; ++ fw_event->VF_ID = mpi_reply->VF_ID; ++ fw_event->VP_ID = mpi_reply->VP_ID; ++ fw_event->event = event; ++ _scsih_fw_event_add(ioc, fw_event); ++ fw_event_work_put(fw_event); ++ return 1; ++} ++ ++/** ++ * _scsih_expander_node_remove - removing expander device from list. ++ * @ioc: per adapter object ++ * @sas_expander: the sas_device object ++ * Context: Calling function should acquire ioc->sas_node_lock. ++ * ++ * Removing object and freeing associated memory from the ++ * ioc->sas_expander_list. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_node *sas_expander) ++{ ++ struct _sas_port *mpt2sas_port, *next; ++ ++ /* remove sibling ports attached to this expander */ ++ list_for_each_entry_safe(mpt2sas_port, next, ++ &sas_expander->sas_port_list, port_list) { ++ if (ioc->shost_recovery) ++ return; ++ if (mpt2sas_port->remote_identify.device_type == ++ SAS_END_DEVICE) ++ mpt2sas_device_remove_by_sas_address(ioc, ++ mpt2sas_port->remote_identify.sas_address); ++ else if (mpt2sas_port->remote_identify.device_type == ++ SAS_EDGE_EXPANDER_DEVICE || ++ mpt2sas_port->remote_identify.device_type == ++ SAS_FANOUT_EXPANDER_DEVICE) ++ mpt2sas_expander_remove(ioc, ++ mpt2sas_port->remote_identify.sas_address); ++ } ++ ++ mpt2sas_transport_port_remove(ioc, sas_expander->sas_address, ++ sas_expander->sas_address_parent); ++ ++ pr_info(MPT3SAS_FMT ++ "expander_remove: handle(0x%04x), sas_addr(0x%016llx)\n", ++ ioc->name, ++ sas_expander->handle, (unsigned long long) ++ sas_expander->sas_address); ++ ++ kfree(sas_expander->phy); ++ kfree(sas_expander); ++} ++ ++/** ++ * _scsih_ir_shutdown - IR shutdown notification ++ * @ioc: per adapter object ++ * ++ * Sending RAID Action to alert the Integrated RAID subsystem of the IOC that ++ * the host system is shutting down. ++ * ++ * Return nothing. ++ */ ++static void ++_scsih_ir_shutdown(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2RaidActionRequest_t *mpi_request; ++ Mpi2RaidActionReply_t *mpi_reply; ++ u16 smid; ++ ++ /* is IR firmware build loaded ? */ ++ if (!ioc->ir_firmware) ++ return; ++ ++ /* are there any volumes ? */ ++ if (list_empty(&ioc->raid_device_list)) ++ return; ++ ++ mutex_lock(&ioc->scsih_cmds.mutex); ++ ++ if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: scsih_cmd in use\n", ++ ioc->name, __func__); ++ goto out; ++ } ++ ioc->scsih_cmds.status = MPT3_CMD_PENDING; ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->scsih_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; ++ goto out; ++ } ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->scsih_cmds.smid = smid; ++ memset(mpi_request, 0, sizeof(Mpi2RaidActionRequest_t)); ++ ++ mpi_request->Function = MPI2_FUNCTION_RAID_ACTION; ++ mpi_request->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED; ++ ++ if (!ioc->hide_ir_msg) ++ pr_info(MPT3SAS_FMT "IR shutdown (sending)\n", ioc->name); ++ init_completion(&ioc->scsih_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ); ++ ++ if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ goto out; ++ } ++ ++ if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) { ++ mpi_reply = ioc->scsih_cmds.reply; ++ if (!ioc->hide_ir_msg) ++ pr_info(MPT3SAS_FMT "IR shutdown " ++ "(complete): ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, le16_to_cpu(mpi_reply->IOCStatus), ++ le32_to_cpu(mpi_reply->IOCLogInfo)); ++ } ++ ++ out: ++ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_unlock(&ioc->scsih_cmds.mutex); ++} ++ ++/** ++ * scsih_remove_mpt2sas - detach and remove add host ++ * @pdev: PCI device struct ++ * ++ * Routine called when unloading the driver. ++ * Return nothing. ++ */ ++void scsih_remove_mpt2sas(struct pci_dev *pdev) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ struct _sas_port *mpt2sas_port, *next_port; ++ struct _raid_device *raid_device, *next; ++ struct MPT3SAS_TARGET *sas_target_priv_data; ++ struct workqueue_struct *wq; ++ unsigned long flags; ++ ++ ioc->remove_host = 1; ++ _scsih_fw_event_cleanup_queue(ioc); ++ ++ spin_lock_irqsave(&ioc->fw_event_lock, flags); ++ wq = ioc->firmware_event_thread; ++ ioc->firmware_event_thread = NULL; ++ spin_unlock_irqrestore(&ioc->fw_event_lock, flags); ++ if (wq) ++ destroy_workqueue(wq); ++ ++ /* release all the volumes */ ++ _scsih_ir_shutdown(ioc); ++ list_for_each_entry_safe(raid_device, next, &ioc->raid_device_list, ++ list) { ++ if (raid_device->starget) { ++ sas_target_priv_data = ++ raid_device->starget->hostdata; ++ sas_target_priv_data->deleted = 1; ++ scsi_remove_target(&raid_device->starget->dev); ++ } ++ pr_info(MPT3SAS_FMT "removing handle(0x%04x), wwid(0x%016llx)\n", ++ ioc->name, raid_device->handle, ++ (unsigned long long) raid_device->wwid); ++ _scsih_raid_device_remove(ioc, raid_device); ++ } ++ ++ /* free ports attached to the sas_host */ ++ list_for_each_entry_safe(mpt2sas_port, next_port, ++ &ioc->sas_hba.sas_port_list, port_list) { ++ if (mpt2sas_port->remote_identify.device_type == ++ SAS_END_DEVICE) ++ mpt2sas_device_remove_by_sas_address(ioc, ++ mpt2sas_port->remote_identify.sas_address); ++ else if (mpt2sas_port->remote_identify.device_type == ++ SAS_EDGE_EXPANDER_DEVICE || ++ mpt2sas_port->remote_identify.device_type == ++ SAS_FANOUT_EXPANDER_DEVICE) ++ mpt2sas_expander_remove(ioc, ++ mpt2sas_port->remote_identify.sas_address); ++ } ++ ++ /* free phys attached to the sas_host */ ++ if (ioc->sas_hba.num_phys) { ++ kfree(ioc->sas_hba.phy); ++ ioc->sas_hba.phy = NULL; ++ ioc->sas_hba.num_phys = 0; ++ } ++ ++ sas_remove_host(shost); ++ scsi_remove_host(shost); ++ mpt2sas_base_detach(ioc); ++ spin_lock(&gioc_lock_mpt2sas); ++ list_del(&ioc->list); ++ spin_unlock(&gioc_lock_mpt2sas); ++ scsi_host_put(shost); ++} ++ ++/** ++ * scsih_shutdown_mpt2sas - routine call during system shutdown ++ * @pdev: PCI device struct ++ * ++ * Return nothing. ++ */ ++void ++scsih_shutdown_mpt2sas(struct pci_dev *pdev) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ struct workqueue_struct *wq; ++ unsigned long flags; ++ ++ ioc->remove_host = 1; ++ _scsih_fw_event_cleanup_queue(ioc); ++ ++ spin_lock_irqsave(&ioc->fw_event_lock, flags); ++ wq = ioc->firmware_event_thread; ++ ioc->firmware_event_thread = NULL; ++ spin_unlock_irqrestore(&ioc->fw_event_lock, flags); ++ if (wq) ++ destroy_workqueue(wq); ++ ++ _scsih_ir_shutdown(ioc); ++ mpt2sas_base_detach(ioc); ++} ++ ++ ++/** ++ * _scsih_probe_boot_devices - reports 1st device ++ * @ioc: per adapter object ++ * ++ * If specified in bios page 2, this routine reports the 1st ++ * device scsi-ml or sas transport for persistent boot device ++ * purposes. Please refer to function _scsih_determine_boot_device() ++ */ ++static void ++_scsih_probe_boot_devices(struct MPT3SAS_ADAPTER *ioc) ++{ ++ u8 is_raid; ++ void *device; ++ struct _sas_device *sas_device; ++ struct _raid_device *raid_device; ++ u16 handle; ++ u64 sas_address_parent; ++ u64 sas_address; ++ unsigned long flags; ++ int rc; ++ ++ /* no Bios, return immediately */ ++ if (!ioc->bios_pg3.BiosVersion) ++ return; ++ ++ device = NULL; ++ is_raid = 0; ++ if (ioc->req_boot_device.device) { ++ device = ioc->req_boot_device.device; ++ is_raid = ioc->req_boot_device.is_raid; ++ } else if (ioc->req_alt_boot_device.device) { ++ device = ioc->req_alt_boot_device.device; ++ is_raid = ioc->req_alt_boot_device.is_raid; ++ } else if (ioc->current_boot_device.device) { ++ device = ioc->current_boot_device.device; ++ is_raid = ioc->current_boot_device.is_raid; ++ } ++ ++ if (!device) ++ return; ++ ++ if (is_raid) { ++ raid_device = device; ++ rc = scsi_add_device(ioc->shost, RAID_CHANNEL, ++ raid_device->id, 0); ++ if (rc) ++ _scsih_raid_device_remove(ioc, raid_device); ++ } else { ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = device; ++ handle = sas_device->handle; ++ sas_address_parent = sas_device->sas_address_parent; ++ sas_address = sas_device->sas_address; ++ list_move_tail(&sas_device->list, &ioc->sas_device_list); ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ if (ioc->hide_drives) ++ return; ++ if (!mpt2sas_transport_port_add(ioc, handle, ++ sas_address_parent)) { ++ _scsih_sas_device_remove(ioc, sas_device); ++ } else if (!sas_device->starget) { ++ if (!ioc->is_driver_loading) { ++ mpt2sas_transport_port_remove(ioc, ++ sas_address, ++ sas_address_parent); ++ _scsih_sas_device_remove(ioc, sas_device); ++ } ++ } ++ } ++} ++ ++/** ++ * _scsih_probe_raid - reporting raid volumes to scsi-ml ++ * @ioc: per adapter object ++ * ++ * Called during initial loading of the driver. ++ */ ++static void ++_scsih_probe_raid(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct _raid_device *raid_device, *raid_next; ++ int rc; ++ ++ list_for_each_entry_safe(raid_device, raid_next, ++ &ioc->raid_device_list, list) { ++ if (raid_device->starget) ++ continue; ++ rc = scsi_add_device(ioc->shost, RAID_CHANNEL, ++ raid_device->id, 0); ++ if (rc) ++ _scsih_raid_device_remove(ioc, raid_device); ++ } ++} ++ ++static struct _sas_device *get_next_sas_device(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct _sas_device *sas_device = NULL; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ if (!list_empty(&ioc->sas_device_init_list)) { ++ sas_device = list_first_entry(&ioc->sas_device_init_list, ++ struct _sas_device, list); ++ sas_device_get(sas_device); ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ ++ return sas_device; ++} ++ ++static void sas_device_make_active(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_device *sas_device) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ ++ /* ++ * Since we dropped the lock during the call to port_add(), we need to ++ * be careful here that somebody else didn't move or delete this item ++ * while we were busy with other things. ++ * ++ * If it was on the list, we need a put() for the reference the list ++ * had. Either way, we need a get() for the destination list. ++ */ ++ if (!list_empty(&sas_device->list)) { ++ list_del_init(&sas_device->list); ++ sas_device_put(sas_device); ++ } ++ ++ sas_device_get(sas_device); ++ list_add_tail(&sas_device->list, &ioc->sas_device_list); ++ ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++} ++ ++/** ++ * _scsih_probe_sas - reporting sas devices to sas transport ++ * @ioc: per adapter object ++ * ++ * Called during initial loading of the driver. ++ */ ++static void ++_scsih_probe_sas(struct MPT3SAS_ADAPTER *ioc) ++{ ++ struct _sas_device *sas_device; ++ ++ if (ioc->hide_drives) ++ return; ++ ++ while ((sas_device = get_next_sas_device(ioc))) { ++ if (!mpt2sas_transport_port_add(ioc, sas_device->handle, ++ sas_device->sas_address_parent)) { ++ _scsih_sas_device_remove(ioc, sas_device); ++ sas_device_put(sas_device); ++ continue; ++ } else if (!sas_device->starget) { ++ /* ++ * When asyn scanning is enabled, its not possible to ++ * remove devices while scanning is turned on due to an ++ * oops in scsi_sysfs_add_sdev()->add_device()-> ++ * sysfs_addrm_start() ++ */ ++ if (!ioc->is_driver_loading) { ++ mpt2sas_transport_port_remove(ioc, ++ sas_device->sas_address, ++ sas_device->sas_address_parent); ++ _scsih_sas_device_remove(ioc, sas_device); ++ sas_device_put(sas_device); ++ continue; ++ } ++ } ++ sas_device_make_active(ioc, sas_device); ++ sas_device_put(sas_device); ++ } ++} ++ ++/** ++ * _scsih_probe_devices - probing for devices ++ * @ioc: per adapter object ++ * ++ * Called during initial loading of the driver. ++ */ ++static void ++_scsih_probe_devices(struct MPT3SAS_ADAPTER *ioc) ++{ ++ u16 volume_mapping_flags; ++ ++ if (!(ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR)) ++ return; /* return when IOC doesn't support initiator mode */ ++ ++ _scsih_probe_boot_devices(ioc); ++ ++ if (ioc->ir_firmware) { ++ volume_mapping_flags = ++ le16_to_cpu(ioc->ioc_pg8.IRVolumeMappingFlags) & ++ MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE; ++ if (volume_mapping_flags == ++ MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING) { ++ _scsih_probe_raid(ioc); ++ _scsih_probe_sas(ioc); ++ } else { ++ _scsih_probe_sas(ioc); ++ _scsih_probe_raid(ioc); ++ } ++ } else ++ _scsih_probe_sas(ioc); ++} ++ ++/** ++ * scsih_scan_start_mpt2sas - scsi lld callback for .scan_start ++ * @shost: SCSI host pointer ++ * ++ * The shost has the ability to discover targets on its own instead ++ * of scanning the entire bus. In our implemention, we will kick off ++ * firmware discovery. ++ */ ++void ++scsih_scan_start_mpt2sas(struct Scsi_Host *shost) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ int rc; ++ if (diag_buffer_enable != -1 && diag_buffer_enable != 0) ++ mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable); ++ ++ if (disable_discovery > 0) ++ return; ++ ++ ioc->start_scan = 1; ++ rc = mpt2sas_port_enable(ioc); ++ ++ if (rc != 0) ++ pr_info(MPT3SAS_FMT "port enable: FAILED\n", ioc->name); ++} ++ ++/** ++ * scsih_scan_finished_mpt2sas - scsi lld callback for .scan_finished ++ * @shost: SCSI host pointer ++ * @time: elapsed time of the scan in jiffies ++ * ++ * This function will be called periodicallyn until it returns 1 with the ++ * scsi_host and the elapsed time of the scan in jiffies. In our implemention, ++ * we wait for firmware discovery to complete, then return 1. ++ */ ++int ++scsih_scan_finished_mpt2sas(struct Scsi_Host *shost, unsigned long time) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ if (disable_discovery > 0) { ++ ioc->is_driver_loading = 0; ++ ioc->wait_for_discovery_to_complete = 0; ++ return 1; ++ } ++ ++ if (time >= (300 * HZ)) { ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ pr_info(MPT3SAS_FMT ++ "port enable: FAILED with timeout (timeout=300s)\n", ++ ioc->name); ++ ioc->is_driver_loading = 0; ++ return 1; ++ } ++ ++ if (ioc->start_scan) ++ return 0; ++ ++ if (ioc->start_scan_failed) { ++ pr_info(MPT3SAS_FMT ++ "port enable: FAILED with (ioc_status=0x%08x)\n", ++ ioc->name, ioc->start_scan_failed); ++ ioc->is_driver_loading = 0; ++ ioc->wait_for_discovery_to_complete = 0; ++ ioc->remove_host = 1; ++ return 1; ++ } ++ ++ pr_info(MPT3SAS_FMT "port enable: SUCCESS\n", ioc->name); ++ ioc->base_cmds.status = MPT3_CMD_NOT_USED; ++ ++ if (ioc->wait_for_discovery_to_complete) { ++ ioc->wait_for_discovery_to_complete = 0; ++ _scsih_probe_devices(ioc); ++ } ++ mpt2sas_base_start_watchdog(ioc); ++ ioc->is_driver_loading = 0; ++ return 1; ++} ++ ++/* shost template for SAS 2.0 HBA devices */ ++static struct scsi_host_template mpt2sas_driver_template_mpt2sas = { ++ .module = THIS_MODULE, ++ .name = "Fusion MPT SAS Host", ++ .proc_name = MPT2SAS_DRIVER_NAME, ++ .queuecommand = scsih_qcmd_mpt2sas, ++ .target_alloc = scsih_target_alloc_mpt2sas, ++ .slave_alloc = scsih_slave_alloc_mpt2sas, ++ .slave_configure = scsih_slave_configure_mpt2sas, ++ .target_destroy = scsih_target_destroy_mpt2sas, ++ .slave_destroy = scsih_slave_destroy_mpt2sas, ++ .scan_finished = scsih_scan_finished_mpt2sas, ++ .scan_start = scsih_scan_start_mpt2sas, ++ .change_queue_depth = scsih_change_queue_depth_mpt2sas, ++ .change_queue_type = _scsih_change_queue_type_mpt2sas, ++ .eh_abort_handler = scsih_abort_mpt2sas, ++ .eh_device_reset_handler = scsih_dev_reset_mpt2sas, ++ .eh_target_reset_handler = scsih_target_reset_mpt2sas, ++ .eh_host_reset_handler = scsih_host_reset_mpt2sas, ++ .bios_param = scsih_bios_param_mpt2sas, ++ .can_queue = 1, ++ .this_id = -1, ++ .sg_tablesize = MPT2SAS_SG_DEPTH, ++ .max_sectors = 32767, ++ .cmd_per_lun = 7, ++ .use_clustering = ENABLE_CLUSTERING, ++ .shost_attrs = mpt2sas_host_attrs, ++ .sdev_attrs = mpt2sas_dev_attrs, ++}; ++ ++#ifdef MPT2SAS_SCSI ++/* raid transport support for SAS 2.0 HBA devices */ ++static struct raid_function_template mpt2sas_raid_functions = { ++ .cookie = &mpt2sas_driver_template_mpt2sas, ++ .is_raid = scsih_is_raid_mpt2sas, ++ .get_resync = scsih_get_resync_mpt2sas, ++ .get_state = scsih_get_state_mpt2sas, ++}; ++#endif /* MPT2SAS_SCSI */ ++ ++/* shost template for SAS 3.0 HBA devices */ ++static struct scsi_host_template mpt3sas_driver_template_mpt2sas = { ++ .module = THIS_MODULE, ++ .name = "Fusion MPT SAS Host", ++ .proc_name = MPT3SAS_DRIVER_NAME, ++ .queuecommand = scsih_qcmd_mpt2sas, ++ .target_alloc = scsih_target_alloc_mpt2sas, ++ .slave_alloc = scsih_slave_alloc_mpt2sas, ++ .slave_configure = scsih_slave_configure_mpt2sas, ++ .target_destroy = scsih_target_destroy_mpt2sas, ++ .slave_destroy = scsih_slave_destroy_mpt2sas, ++ .scan_finished = scsih_scan_finished_mpt2sas, ++ .scan_start = scsih_scan_start_mpt2sas, ++ .change_queue_depth = scsih_change_queue_depth_mpt2sas, ++ .change_queue_type = _scsih_change_queue_type_mpt2sas, ++ .eh_abort_handler = scsih_abort_mpt2sas, ++ .eh_device_reset_handler = scsih_dev_reset_mpt2sas, ++ .eh_target_reset_handler = scsih_target_reset_mpt2sas, ++ .eh_host_reset_handler = scsih_host_reset_mpt2sas, ++ .bios_param = scsih_bios_param_mpt2sas, ++ .can_queue = 1, ++ .this_id = -1, ++ .sg_tablesize = MPT3SAS_SG_DEPTH, ++ .max_sectors = 32767, ++ .cmd_per_lun = 7, ++ .use_clustering = ENABLE_CLUSTERING, ++ .shost_attrs = mpt2sas_host_attrs, ++ .sdev_attrs = mpt2sas_dev_attrs, ++}; ++ ++#ifndef MPT2SAS_SCSI ++/* raid transport support for SAS 3.0 HBA devices */ ++static struct raid_function_template mpt3sas_raid_functions = { ++ .cookie = &mpt3sas_driver_template_mpt2sas, ++ .is_raid = scsih_is_raid_mpt2sas, ++ .get_resync = scsih_get_resync_mpt2sas, ++ .get_state = scsih_get_state_mpt2sas, ++}; ++#endif /* MPT2SAS_SCSI */ ++ ++/** ++ * _scsih_determine_hba_mpi_version_mpt2sas - determine in which MPI version class ++ * this device belongs to. ++ * @pdev: PCI device struct ++ * ++ * return MPI2_VERSION for SAS 2.0 HBA devices, ++ * MPI25_VERSION for SAS 3.0 HBA devices, and ++ * MPI26 VERSION for Cutlass & Invader SAS 3.0 HBA devices ++ */ ++u16 ++_scsih_determine_hba_mpi_version_mpt2sas(struct pci_dev *pdev) ++{ ++ ++ switch (pdev->device) { ++ case MPI2_MFGPAGE_DEVID_SSS6200: ++ case MPI2_MFGPAGE_DEVID_SAS2004: ++ case MPI2_MFGPAGE_DEVID_SAS2008: ++ case MPI2_MFGPAGE_DEVID_SAS2108_1: ++ case MPI2_MFGPAGE_DEVID_SAS2108_2: ++ case MPI2_MFGPAGE_DEVID_SAS2108_3: ++ case MPI2_MFGPAGE_DEVID_SAS2116_1: ++ case MPI2_MFGPAGE_DEVID_SAS2116_2: ++ case MPI2_MFGPAGE_DEVID_SAS2208_1: ++ case MPI2_MFGPAGE_DEVID_SAS2208_2: ++ case MPI2_MFGPAGE_DEVID_SAS2208_3: ++ case MPI2_MFGPAGE_DEVID_SAS2208_4: ++ case MPI2_MFGPAGE_DEVID_SAS2208_5: ++ case MPI2_MFGPAGE_DEVID_SAS2208_6: ++ case MPI2_MFGPAGE_DEVID_SAS2308_1: ++ case MPI2_MFGPAGE_DEVID_SAS2308_2: ++ case MPI2_MFGPAGE_DEVID_SAS2308_3: ++ return MPI2_VERSION; ++ case MPI25_MFGPAGE_DEVID_SAS3004: ++ case MPI25_MFGPAGE_DEVID_SAS3008: ++ case MPI25_MFGPAGE_DEVID_SAS3108_1: ++ case MPI25_MFGPAGE_DEVID_SAS3108_2: ++ case MPI25_MFGPAGE_DEVID_SAS3108_5: ++ case MPI25_MFGPAGE_DEVID_SAS3108_6: ++ return MPI25_VERSION; ++ case MPI26_MFGPAGE_DEVID_SAS3216: ++ case MPI26_MFGPAGE_DEVID_SAS3224: ++ case MPI26_MFGPAGE_DEVID_SAS3316_1: ++ case MPI26_MFGPAGE_DEVID_SAS3316_2: ++ case MPI26_MFGPAGE_DEVID_SAS3316_3: ++ case MPI26_MFGPAGE_DEVID_SAS3316_4: ++ case MPI26_MFGPAGE_DEVID_SAS3324_1: ++ case MPI26_MFGPAGE_DEVID_SAS3324_2: ++ case MPI26_MFGPAGE_DEVID_SAS3324_3: ++ case MPI26_MFGPAGE_DEVID_SAS3324_4: ++ return MPI26_VERSION; ++ } ++ return 0; ++} ++ ++/** ++ * _scsih_probe_mpt2sas - attach and add scsi host ++ * @pdev: PCI device struct ++ * @id: pci device id ++ * ++ * Returns 0 success, anything else error. ++ */ ++int ++_scsih_probe_mpt2sas(struct pci_dev *pdev, const struct pci_device_id *id) ++{ ++ struct MPT3SAS_ADAPTER *ioc; ++ struct Scsi_Host *shost = NULL; ++ int rv; ++ u16 hba_mpi_version; ++ ++ /* Determine in which MPI version class this pci device belongs */ ++ hba_mpi_version = _scsih_determine_hba_mpi_version_mpt2sas(pdev); ++ if (hba_mpi_version == 0) ++ return -ENODEV; ++ ++ switch (hba_mpi_version) { ++ case MPI2_VERSION: ++ /* Use mpt2sas driver host template for SAS 2.0 HBA's */ ++ shost = scsi_host_alloc(&mpt2sas_driver_template_mpt2sas, ++ sizeof(struct MPT3SAS_ADAPTER)); ++ if (!shost) ++ return -ENODEV; ++ ioc = shost_priv(shost); ++ memset(ioc, 0, sizeof(struct MPT3SAS_ADAPTER)); ++ ioc->hba_mpi_version_belonged = hba_mpi_version; ++ ioc->id = mpt2_ids++; ++ sprintf(ioc->driver_name, "%s", MPT2SAS_DRIVER_NAME); ++ if (pdev->device == MPI2_MFGPAGE_DEVID_SSS6200) { ++ ioc->is_warpdrive = 1; ++ ioc->hide_ir_msg = 1; ++ } else ++ ioc->mfg_pg10_hide_flag = MFG_PAGE10_EXPOSE_ALL_DISKS; ++ break; ++ case MPI25_VERSION: ++ case MPI26_VERSION: ++ /* Use mpt3sas driver host template for SAS 3.0 HBA's */ ++ shost = scsi_host_alloc(&mpt3sas_driver_template_mpt2sas, ++ sizeof(struct MPT3SAS_ADAPTER)); ++ if (!shost) ++ return -ENODEV; ++ ioc = shost_priv(shost); ++ memset(ioc, 0, sizeof(struct MPT3SAS_ADAPTER)); ++ ioc->hba_mpi_version_belonged = hba_mpi_version; ++ ioc->id = mpt3_ids++; ++ sprintf(ioc->driver_name, "%s", MPT3SAS_DRIVER_NAME); ++ if ((ioc->hba_mpi_version_belonged == MPI25_VERSION && ++ pdev->revision >= SAS3_PCI_DEVICE_C0_REVISION) || ++ (ioc->hba_mpi_version_belonged == MPI26_VERSION)) ++ ioc->msix96_vector = 1; ++ break; ++ default: ++ return -ENODEV; ++ } ++ ++ INIT_LIST_HEAD(&ioc->list); ++ spin_lock(&gioc_lock_mpt2sas); ++ list_add_tail(&ioc->list, &mpt2sas_ioc_list); ++ spin_unlock(&gioc_lock_mpt2sas); ++ ioc->shost = shost; ++ ioc->pdev = pdev; ++ ioc->scsi_io_cb_idx = scsi_io_cb_idx; ++ ioc->tm_cb_idx = tm_cb_idx; ++ ioc->ctl_cb_idx = ctl_cb_idx; ++ ioc->base_cb_idx = base_cb_idx; ++ ioc->port_enable_cb_idx = port_enable_cb_idx; ++ ioc->transport_cb_idx = transport_cb_idx; ++ ioc->scsih_cb_idx = scsih_cb_idx; ++ ioc->config_cb_idx = config_cb_idx; ++ ioc->tm_tr_cb_idx = tm_tr_cb_idx; ++ ioc->tm_tr_volume_cb_idx = tm_tr_volume_cb_idx; ++ ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx; ++ ioc->logging_level = logging_level; ++ ioc->schedule_dead_ioc_flush_running_cmds = &_scsih_flush_running_cmds; ++ /* misc semaphores and spin locks */ ++ mutex_init(&ioc->reset_in_progress_mutex); ++ /* initializing pci_access_mutex lock */ ++ mutex_init(&ioc->pci_access_mutex); ++ spin_lock_init(&ioc->ioc_reset_in_progress_lock); ++ spin_lock_init(&ioc->scsi_lookup_lock); ++ spin_lock_init(&ioc->sas_device_lock); ++ spin_lock_init(&ioc->sas_node_lock); ++ spin_lock_init(&ioc->fw_event_lock); ++ spin_lock_init(&ioc->raid_device_lock); ++ spin_lock_init(&ioc->diag_trigger_lock); ++ ++ INIT_LIST_HEAD(&ioc->sas_device_list); ++ INIT_LIST_HEAD(&ioc->sas_device_init_list); ++ INIT_LIST_HEAD(&ioc->sas_expander_list); ++ INIT_LIST_HEAD(&ioc->fw_event_list); ++ INIT_LIST_HEAD(&ioc->raid_device_list); ++ INIT_LIST_HEAD(&ioc->sas_hba.sas_port_list); ++ INIT_LIST_HEAD(&ioc->delayed_tr_list); ++ INIT_LIST_HEAD(&ioc->delayed_sc_list); ++ INIT_LIST_HEAD(&ioc->delayed_event_ack_list); ++ INIT_LIST_HEAD(&ioc->delayed_tr_volume_list); ++ INIT_LIST_HEAD(&ioc->reply_queue_list); ++ ++ sprintf(ioc->name, "%s_cm%d", ioc->driver_name, ioc->id); ++ ++ /* init shost parameters */ ++ shost->max_cmd_len = 32; ++ shost->max_lun = max_lun; ++ shost->transportt = mpt2sas_transport_template; ++ shost->unique_id = ioc->id; ++ ++ if (max_sectors != 0xFFFF) { ++ if (max_sectors < 64) { ++ shost->max_sectors = 64; ++ pr_warn(MPT3SAS_FMT "Invalid value %d passed " \ ++ "for max_sectors, range is 64 to 32767. Assigning " ++ "value of 64.\n", ioc->name, max_sectors); ++ } else if (max_sectors > 32767) { ++ shost->max_sectors = 32767; ++ pr_warn(MPT3SAS_FMT "Invalid value %d passed " \ ++ "for max_sectors, range is 64 to 32767. Assigning " ++ "default value of 32767.\n", ioc->name, ++ max_sectors); ++ } else { ++ shost->max_sectors = max_sectors & 0xFFFE; ++ pr_info(MPT3SAS_FMT ++ "The max_sectors value is set to %d\n", ++ ioc->name, shost->max_sectors); ++ } ++ } ++ ++ /* register EEDP capabilities with SCSI layer */ ++ if (prot_mask > 0) ++ scsi_host_set_prot(shost, prot_mask); ++ else ++ scsi_host_set_prot(shost, SHOST_DIF_TYPE1_PROTECTION ++ | SHOST_DIF_TYPE2_PROTECTION ++ | SHOST_DIF_TYPE3_PROTECTION); ++ ++ scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC); ++ ++ /* event thread */ ++ snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name), ++ "fw_event_%s%d", ioc->driver_name, ioc->id); ++ ioc->firmware_event_thread = alloc_ordered_workqueue( ++ ioc->firmware_event_name, WQ_MEM_RECLAIM); ++ if (!ioc->firmware_event_thread) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rv = -ENODEV; ++ goto out_thread_fail; ++ } ++ ++ ioc->is_driver_loading = 1; ++ if ((mpt2sas_base_attach(ioc))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rv = -ENODEV; ++ goto out_attach_fail; ++ } ++ ++ if (ioc->is_warpdrive) { ++ if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS) ++ ioc->hide_drives = 0; ++ else if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_HIDE_ALL_DISKS) ++ ioc->hide_drives = 1; ++ else { ++ if (mpt2sas_get_num_volumes(ioc)) ++ ioc->hide_drives = 1; ++ else ++ ioc->hide_drives = 0; ++ } ++ } else ++ ioc->hide_drives = 0; ++ ++ rv = scsi_add_host(shost, &pdev->dev); ++ if (rv) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out_add_shost_fail; ++ } ++ ++ scsi_scan_host(shost); ++ return 0; ++out_add_shost_fail: ++ mpt2sas_base_detach(ioc); ++ out_attach_fail: ++ destroy_workqueue(ioc->firmware_event_thread); ++ out_thread_fail: ++ spin_lock(&gioc_lock_mpt2sas); ++ list_del(&ioc->list); ++ spin_unlock(&gioc_lock_mpt2sas); ++ scsi_host_put(shost); ++ return rv; ++} ++ ++#ifdef CONFIG_PM ++/** ++ * scsih_suspend_mpt2sas - power management suspend main entry point ++ * @pdev: PCI device struct ++ * @state: PM state change to (usually PCI_D3) ++ * ++ * Returns 0 success, anything else error. ++ */ ++int ++scsih_suspend_mpt2sas(struct pci_dev *pdev, pm_message_t state) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ pci_power_t device_state; ++ ++ mpt2sas_base_stop_watchdog(ioc); ++ flush_scheduled_work(); ++ scsi_block_requests(shost); ++ device_state = pci_choose_state(pdev, state); ++ pr_info(MPT3SAS_FMT ++ "pdev=0x%p, slot=%s, entering operating state [D%d]\n", ++ ioc->name, pdev, pci_name(pdev), device_state); ++ ++ pci_save_state(pdev); ++ mpt2sas_base_free_resources(ioc); ++ pci_set_power_state(pdev, device_state); ++ return 0; ++} ++ ++/** ++ * scsih_resume_mpt2sas - power management resume main entry point ++ * @pdev: PCI device struct ++ * ++ * Returns 0 success, anything else error. ++ */ ++int ++scsih_resume_mpt2sas(struct pci_dev *pdev) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ pci_power_t device_state = pdev->current_state; ++ int r; ++ ++ pr_info(MPT3SAS_FMT ++ "pdev=0x%p, slot=%s, previous operating state [D%d]\n", ++ ioc->name, pdev, pci_name(pdev), device_state); ++ ++ pci_set_power_state(pdev, PCI_D0); ++ pci_enable_wake(pdev, PCI_D0, 0); ++ pci_restore_state(pdev); ++ ioc->pdev = pdev; ++ r = mpt2sas_base_map_resources(ioc); ++ if (r) ++ return r; ++ ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, SOFT_RESET); ++ scsi_unblock_requests(shost); ++ mpt2sas_base_start_watchdog(ioc); ++ return 0; ++} ++#endif /* CONFIG_PM */ ++ ++/** ++ * scsih_pci_error_detected_mpt2sas - Called when a PCI error is detected. ++ * @pdev: PCI device struct ++ * @state: PCI channel state ++ * ++ * Description: Called when a PCI error is detected. ++ * ++ * Return value: ++ * PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT ++ */ ++pci_ers_result_t ++scsih_pci_error_detected_mpt2sas(struct pci_dev *pdev, pci_channel_state_t state) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ pr_info(MPT3SAS_FMT "PCI error: detected callback, state(%d)!!\n", ++ ioc->name, state); ++ ++ switch (state) { ++ case pci_channel_io_normal: ++ return PCI_ERS_RESULT_CAN_RECOVER; ++ case pci_channel_io_frozen: ++ /* Fatal error, prepare for slot reset */ ++ ioc->pci_error_recovery = 1; ++ scsi_block_requests(ioc->shost); ++ mpt2sas_base_stop_watchdog(ioc); ++ mpt2sas_base_free_resources(ioc); ++ return PCI_ERS_RESULT_NEED_RESET; ++ case pci_channel_io_perm_failure: ++ /* Permanent error, prepare for device removal */ ++ ioc->pci_error_recovery = 1; ++ mpt2sas_base_stop_watchdog(ioc); ++ _scsih_flush_running_cmds(ioc); ++ return PCI_ERS_RESULT_DISCONNECT; ++ } ++ return PCI_ERS_RESULT_NEED_RESET; ++} ++ ++/** ++ * scsih_pci_slot_reset_mpt2sas - Called when PCI slot has been reset. ++ * @pdev: PCI device struct ++ * ++ * Description: This routine is called by the pci error recovery ++ * code after the PCI slot has been reset, just before we ++ * should resume normal operations. ++ */ ++pci_ers_result_t ++scsih_pci_slot_reset_mpt2sas(struct pci_dev *pdev) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ int rc; ++ ++ pr_info(MPT3SAS_FMT "PCI error: slot reset callback!!\n", ++ ioc->name); ++ ++ ioc->pci_error_recovery = 0; ++ ioc->pdev = pdev; ++ pci_restore_state(pdev); ++ rc = mpt2sas_base_map_resources(ioc); ++ if (rc) ++ return PCI_ERS_RESULT_DISCONNECT; ++ ++ rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ ++ pr_warn(MPT3SAS_FMT "hard reset: %s\n", ioc->name, ++ (rc == 0) ? "success" : "failed"); ++ ++ if (!rc) ++ return PCI_ERS_RESULT_RECOVERED; ++ else ++ return PCI_ERS_RESULT_DISCONNECT; ++} ++ ++/** ++ * scsih_pci_resume_mpt2sas() - resume normal ops after PCI reset ++ * @pdev: pointer to PCI device ++ * ++ * Called when the error recovery driver tells us that its ++ * OK to resume normal operation. Use completion to allow ++ * halted scsi ops to resume. ++ */ ++void ++scsih_pci_resume_mpt2sas(struct pci_dev *pdev) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ pr_info(MPT3SAS_FMT "PCI error: resume callback!!\n", ioc->name); ++ ++ pci_cleanup_aer_uncorrect_error_status(pdev); ++ mpt2sas_base_start_watchdog(ioc); ++ scsi_unblock_requests(ioc->shost); ++} ++ ++/** ++ * scsih_pci_mmio_enabled_mpt2sas - Enable MMIO and dump debug registers ++ * @pdev: pointer to PCI device ++ */ ++pci_ers_result_t ++scsih_pci_mmio_enabled_mpt2sas(struct pci_dev *pdev) ++{ ++ struct Scsi_Host *shost = pci_get_drvdata(pdev); ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ ++ pr_info(MPT3SAS_FMT "PCI error: mmio enabled callback!!\n", ++ ioc->name); ++ ++ /* TODO - dump whatever for debugging purposes */ ++ ++ /* This called only if scsih_pci_error_detected_mpt2sas returns ++ * PCI_ERS_RESULT_CAN_RECOVER. Read/write to the device still ++ * works, no need to reset slot. ++ */ ++ return PCI_ERS_RESULT_RECOVERED; ++} ++ ++/* ++ * The pci device ids are defined in mpi/mpi2_cnfg.h. ++ */ ++static const struct pci_device_id mpt2sas_pci_table[] = { ++#ifdef MPT2SAS_SCSI ++ /* Spitfire ~ 2004 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2004, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Falcon ~ 2008 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2008, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Liberator ~ 2108 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_1, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_2, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_3, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Meteor ~ 2116 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_1, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_2, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Thunderbolt ~ 2208 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_1, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_2, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_3, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_4, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_5, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Mustang ~ 2308 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_1, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* SSS6200 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200, ++ PCI_ANY_ID, PCI_ANY_ID }, ++#else ++ /* Fury ~ 3004 and 3008 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3004, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3008, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Invader ~ 3108 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_1, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_2, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_5, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_6, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Cutlass ~ 3216 and 3224 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3216, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3224, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ /* Intruder ~ 3316 and 3324 */ ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_1, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_2, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_3, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_4, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3324_1, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3324_2, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3324_3, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3324_4, ++ PCI_ANY_ID, PCI_ANY_ID }, ++#endif /* MPT2SAS_SCSI */ ++ {0} /* Terminating entry */ ++}; ++MODULE_DEVICE_TABLE(pci, mpt2sas_pci_table); ++ ++static struct pci_error_handlers _mpt2sas_err_handler = { ++ .error_detected = scsih_pci_error_detected_mpt2sas, ++ .mmio_enabled = scsih_pci_mmio_enabled_mpt2sas, ++ .slot_reset = scsih_pci_slot_reset_mpt2sas, ++ .resume = scsih_pci_resume_mpt2sas, ++}; ++ ++static struct pci_driver mpt3sas_driver = { ++#ifdef MPT2SAS_SCSI ++ .name = MPT2SAS_DRIVER_NAME, ++#else ++ .name = MPT3SAS_DRIVER_NAME, ++#endif /* MPT2SAS_SCSI */ ++ .id_table = mpt2sas_pci_table, ++ .probe = _scsih_probe_mpt2sas, ++ .remove = scsih_remove_mpt2sas, ++ .shutdown = scsih_shutdown_mpt2sas, ++ .err_handler = &_mpt2sas_err_handler, ++#ifdef CONFIG_PM ++ .suspend = scsih_suspend_mpt2sas, ++ .resume = scsih_resume_mpt2sas, ++#endif ++}; ++ ++/** ++ * scsih_init_mpt2sas - main entry point for this driver. ++ * ++ * Returns 0 success, anything else error. ++ */ ++int ++scsih_init_mpt2sas(void) ++{ ++ mpt2_ids = 0; ++ mpt3_ids = 0; ++ ++ mpt2sas_base_initialize_callback_handler(); ++ ++ /* queuecommand callback hander */ ++ scsi_io_cb_idx = mpt2sas_base_register_callback_handler(_scsih_io_done); ++ ++ /* task managment callback handler */ ++ tm_cb_idx = mpt2sas_base_register_callback_handler(_scsih_tm_done); ++ ++ /* base internal commands callback handler */ ++ base_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_base_done); ++ port_enable_cb_idx = mpt2sas_base_register_callback_handler( ++ mpt2sas_port_enable_done); ++ ++ /* transport internal commands callback handler */ ++ transport_cb_idx = mpt2sas_base_register_callback_handler( ++ mpt2sas_transport_done); ++ ++ /* scsih internal commands callback handler */ ++ scsih_cb_idx = mpt2sas_base_register_callback_handler(_scsih_done); ++ ++ /* configuration page API internal commands callback handler */ ++ config_cb_idx = mpt2sas_base_register_callback_handler( ++ mpt2sas_config_done); ++ ++ /* ctl module callback handler */ ++ ctl_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_ctl_done); ++ ++ tm_tr_cb_idx = mpt2sas_base_register_callback_handler( ++ _scsih_tm_tr_complete); ++ ++ tm_tr_volume_cb_idx = mpt2sas_base_register_callback_handler( ++ _scsih_tm_volume_tr_complete); ++ ++ tm_sas_control_cb_idx = mpt2sas_base_register_callback_handler( ++ _scsih_sas_control_complete); ++ ++ return 0; ++} ++ ++/** ++ * scsih_exit_mpt2sas - exit point for this driver (when it is a module). ++ * ++ * Returns 0 success, anything else error. ++ */ ++void ++scsih_exit_mpt2sas(void) ++{ ++ ++ mpt2sas_base_release_callback_handler(scsi_io_cb_idx); ++ mpt2sas_base_release_callback_handler(tm_cb_idx); ++ mpt2sas_base_release_callback_handler(base_cb_idx); ++ mpt2sas_base_release_callback_handler(port_enable_cb_idx); ++ mpt2sas_base_release_callback_handler(transport_cb_idx); ++ mpt2sas_base_release_callback_handler(scsih_cb_idx); ++ mpt2sas_base_release_callback_handler(config_cb_idx); ++ mpt2sas_base_release_callback_handler(ctl_cb_idx); ++ ++ mpt2sas_base_release_callback_handler(tm_tr_cb_idx); ++ mpt2sas_base_release_callback_handler(tm_tr_volume_cb_idx); ++ mpt2sas_base_release_callback_handler(tm_sas_control_cb_idx); ++ ++/* raid transport support */ ++#ifdef MPT2SAS_SCSI ++ raid_class_release(mpt2sas_raid_template_mpt2sas); ++#else ++ raid_class_release(mpt3sas_raid_template_mpt2sas); ++#endif /* MPT2SAS_SCSI */ ++ sas_release_transport(mpt2sas_transport_template); ++} ++ ++/** ++ * _mpt2sas_init - main entry point for this driver. ++ * ++ * Returns 0 success, anything else error. ++ */ ++static int __init ++_mpt2sas_init(void) ++{ ++ int error; ++ ++#ifdef MPT2SAS_SCSI ++ pr_info("%s version %s loaded\n", MPT2SAS_DRIVER_NAME, ++ MPT2SAS_DRIVER_VERSION); ++#else ++ pr_info("%s version %s loaded\n", MPT3SAS_DRIVER_NAME, ++ MPT3SAS_DRIVER_VERSION); ++#endif /* MPT2SAS_SCSI */ ++ ++ mpt2sas_transport_template = ++ sas_attach_transport(&mpt2sas_transport_functions); ++ if (!mpt2sas_transport_template) ++ return -ENODEV; ++ ++#ifdef MPT2SAS_SCSI ++ mpt2sas_raid_template_mpt2sas = raid_class_attach(&mpt2sas_raid_functions); ++ if (!mpt2sas_raid_template_mpt2sas) { ++ sas_release_transport(mpt2sas_transport_template); ++ return -ENODEV; ++ } ++#else ++ mpt3sas_raid_template_mpt2sas = raid_class_attach(&mpt3sas_raid_functions); ++ if (!mpt3sas_raid_template_mpt2sas) { ++ sas_release_transport(mpt2sas_transport_template); ++ return -ENODEV; ++ } ++#endif /* MPT2SAS_SCSI */ ++ ++ error = scsih_init_mpt2sas(); ++ if (error) { ++ scsih_exit_mpt2sas(); ++ return error; ++ } ++ ++ ++#ifdef MPT2SAS_SCSI ++ mpt2sas_ctl_init(1); ++#else ++ mpt2sas_ctl_init(2); ++#endif /* MPT2SAS_SCSI */ ++ ++ error = pci_register_driver(&mpt3sas_driver); ++ if (error) ++ scsih_exit_mpt2sas(); ++ ++ return error; ++} ++ ++/** ++ * _mpt2sas_exit - exit point for this driver (when it is a module). ++ * ++ */ ++static void __exit ++_mpt2sas_exit(void) ++{ ++ pr_info("mpt3sas version %s unloading\n", ++ MPT3SAS_DRIVER_VERSION); ++ ++ pci_unregister_driver(&mpt3sas_driver); ++ ++#ifdef MPT2SAS_SCSI ++ mpt2sas_ctl_exit(1); ++#else ++ mpt2sas_ctl_exit(2); ++#endif /* MPT2SAS_SCSI */ ++ ++ scsih_exit_mpt2sas(); ++} ++ ++module_init(_mpt2sas_init); ++module_exit(_mpt2sas_exit); +diff --git a/drivers/scsi/mpt2sas/mpt3sas_transport.c b/drivers/scsi/mpt2sas/mpt3sas_transport.c +new file mode 100644 +index 0000000..690afa5 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_transport.c +@@ -0,0 +1,2138 @@ ++/* ++ * SAS Transport Layer for MPT (Message Passing Technology) based controllers ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_transport.c ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mpt3sas_base.h" ++ ++/** ++ * _transport_sas_node_find_by_sas_address - sas node search ++ * @ioc: per adapter object ++ * @sas_address: sas address of expander or sas host ++ * Context: Calling function should acquire ioc->sas_node_lock. ++ * ++ * Search for either hba phys or expander device based on handle, then returns ++ * the sas_node object. ++ */ ++static struct _sas_node * ++_transport_sas_node_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address) ++{ ++ if (ioc->sas_hba.sas_address == sas_address) ++ return &ioc->sas_hba; ++ else ++ return mpt2sas_scsih_expander_find_by_sas_address(ioc, ++ sas_address); ++} ++ ++/** ++ * _transport_convert_phy_link_rate - ++ * @link_rate: link rate returned from mpt firmware ++ * ++ * Convert link_rate from mpi fusion into sas_transport form. ++ */ ++static enum sas_linkrate ++_transport_convert_phy_link_rate(u8 link_rate) ++{ ++ enum sas_linkrate rc; ++ ++ switch (link_rate) { ++ case MPI2_SAS_NEG_LINK_RATE_1_5: ++ rc = SAS_LINK_RATE_1_5_GBPS; ++ break; ++ case MPI2_SAS_NEG_LINK_RATE_3_0: ++ rc = SAS_LINK_RATE_3_0_GBPS; ++ break; ++ case MPI2_SAS_NEG_LINK_RATE_6_0: ++ rc = SAS_LINK_RATE_6_0_GBPS; ++ break; ++ case MPI25_SAS_NEG_LINK_RATE_12_0: ++ rc = SAS_LINK_RATE_12_0_GBPS; ++ break; ++ case MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED: ++ rc = SAS_PHY_DISABLED; ++ break; ++ case MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED: ++ rc = SAS_LINK_RATE_FAILED; ++ break; ++ case MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR: ++ rc = SAS_SATA_PORT_SELECTOR; ++ break; ++ case MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS: ++ rc = SAS_PHY_RESET_IN_PROGRESS; ++ break; ++ ++ default: ++ case MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE: ++ case MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE: ++ rc = SAS_LINK_RATE_UNKNOWN; ++ break; ++ } ++ return rc; ++} ++ ++/** ++ * _transport_set_identify - set identify for phys and end devices ++ * @ioc: per adapter object ++ * @handle: device handle ++ * @identify: sas identify info ++ * ++ * Populates sas identify info. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_transport_set_identify(struct MPT3SAS_ADAPTER *ioc, u16 handle, ++ struct sas_identify *identify) ++{ ++ Mpi2SasDevicePage0_t sas_device_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u32 device_info; ++ u32 ioc_status; ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery) { ++ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", ++ __func__, ioc->name); ++ return -EFAULT; ++ } ++ ++ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, ++ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -ENXIO; ++ } ++ ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT ++ "handle(0x%04x), ioc_status(0x%04x)\nfailure at %s:%d/%s()!\n", ++ ioc->name, handle, ioc_status, ++ __FILE__, __LINE__, __func__); ++ return -EIO; ++ } ++ ++ memset(identify, 0, sizeof(struct sas_identify)); ++ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); ++ ++ /* sas_address */ ++ identify->sas_address = le64_to_cpu(sas_device_pg0.SASAddress); ++ ++ /* phy number of the parent device this device is linked to */ ++ identify->phy_identifier = sas_device_pg0.PhyNum; ++ ++ /* device_type */ ++ switch (device_info & MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) { ++ case MPI2_SAS_DEVICE_INFO_NO_DEVICE: ++ identify->device_type = SAS_PHY_UNUSED; ++ break; ++ case MPI2_SAS_DEVICE_INFO_END_DEVICE: ++ identify->device_type = SAS_END_DEVICE; ++ break; ++ case MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER: ++ identify->device_type = SAS_EDGE_EXPANDER_DEVICE; ++ break; ++ case MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER: ++ identify->device_type = SAS_FANOUT_EXPANDER_DEVICE; ++ break; ++ } ++ ++ /* initiator_port_protocols */ ++ if (device_info & MPI2_SAS_DEVICE_INFO_SSP_INITIATOR) ++ identify->initiator_port_protocols |= SAS_PROTOCOL_SSP; ++ if (device_info & MPI2_SAS_DEVICE_INFO_STP_INITIATOR) ++ identify->initiator_port_protocols |= SAS_PROTOCOL_STP; ++ if (device_info & MPI2_SAS_DEVICE_INFO_SMP_INITIATOR) ++ identify->initiator_port_protocols |= SAS_PROTOCOL_SMP; ++ if (device_info & MPI2_SAS_DEVICE_INFO_SATA_HOST) ++ identify->initiator_port_protocols |= SAS_PROTOCOL_SATA; ++ ++ /* target_port_protocols */ ++ if (device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) ++ identify->target_port_protocols |= SAS_PROTOCOL_SSP; ++ if (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) ++ identify->target_port_protocols |= SAS_PROTOCOL_STP; ++ if (device_info & MPI2_SAS_DEVICE_INFO_SMP_TARGET) ++ identify->target_port_protocols |= SAS_PROTOCOL_SMP; ++ if (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE) ++ identify->target_port_protocols |= SAS_PROTOCOL_SATA; ++ ++ return 0; ++} ++ ++/** ++ * mpt2sas_transport_done - internal transport layer callback handler. ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @msix_index: MSIX table index supplied by the OS ++ * @reply: reply message frame(lower 32bit addr) ++ * ++ * Callback handler when sending internal generated transport cmds. ++ * The callback index passed is `ioc->transport_cb_idx` ++ * ++ * Return 1 meaning mf should be freed from _base_interrupt ++ * 0 means the mf is freed from this function. ++ */ ++u8 ++mpt2sas_transport_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, ++ u32 reply) ++{ ++ MPI2DefaultReply_t *mpi_reply; ++ ++ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); ++ if (ioc->transport_cmds.status == MPT3_CMD_NOT_USED) ++ return 1; ++ if (ioc->transport_cmds.smid != smid) ++ return 1; ++ ioc->transport_cmds.status |= MPT3_CMD_COMPLETE; ++ if (mpi_reply) { ++ memcpy(ioc->transport_cmds.reply, mpi_reply, ++ mpi_reply->MsgLength*4); ++ ioc->transport_cmds.status |= MPT3_CMD_REPLY_VALID; ++ } ++ ioc->transport_cmds.status &= ~MPT3_CMD_PENDING; ++ complete(&ioc->transport_cmds.done); ++ return 1; ++} ++ ++/* report manufacture request structure */ ++struct rep_manu_request { ++ u8 smp_frame_type; ++ u8 function; ++ u8 reserved; ++ u8 request_length; ++}; ++ ++/* report manufacture reply structure */ ++struct rep_manu_reply { ++ u8 smp_frame_type; /* 0x41 */ ++ u8 function; /* 0x01 */ ++ u8 function_result; ++ u8 response_length; ++ u16 expander_change_count; ++ u8 reserved0[2]; ++ u8 sas_format; ++ u8 reserved2[3]; ++ u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN]; ++ u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN]; ++ u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN]; ++ u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN]; ++ u16 component_id; ++ u8 component_revision_id; ++ u8 reserved3; ++ u8 vendor_specific[8]; ++}; ++ ++/** ++ * transport_expander_report_manufacture - obtain SMP report_manufacture ++ * @ioc: per adapter object ++ * @sas_address: expander sas address ++ * @edev: the sas_expander_device object ++ * ++ * Fills in the sas_expander_device object when SMP port is created. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address, struct sas_expander_device *edev) ++{ ++ Mpi2SmpPassthroughRequest_t *mpi_request; ++ Mpi2SmpPassthroughReply_t *mpi_reply; ++ struct rep_manu_reply *manufacture_reply; ++ struct rep_manu_request *manufacture_request; ++ int rc; ++ u16 smid; ++ u32 ioc_state; ++ unsigned long timeleft; ++ void *psge; ++ u8 issue_reset = 0; ++ void *data_out = NULL; ++ dma_addr_t data_out_dma; ++ dma_addr_t data_in_dma; ++ size_t data_in_sz; ++ size_t data_out_sz; ++ u16 wait_state_count; ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery) { ++ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", ++ __func__, ioc->name); ++ return -EFAULT; ++ } ++ ++ mutex_lock(&ioc->transport_cmds.mutex); ++ ++ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: transport_cmds in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ioc->transport_cmds.status = MPT3_CMD_PENDING; ++ ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, __func__, wait_state_count); ++ } ++ if (wait_state_count) ++ pr_info(MPT3SAS_FMT "%s: ioc is operational\n", ++ ioc->name, __func__); ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ rc = 0; ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->transport_cmds.smid = smid; ++ ++ data_out_sz = sizeof(struct rep_manu_request); ++ data_in_sz = sizeof(struct rep_manu_reply); ++ data_out = pci_alloc_consistent(ioc->pdev, data_out_sz + data_in_sz, ++ &data_out_dma); ++ ++ if (!data_out) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ rc = -ENOMEM; ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ ++ data_in_dma = data_out_dma + sizeof(struct rep_manu_request); ++ ++ manufacture_request = data_out; ++ manufacture_request->smp_frame_type = 0x40; ++ manufacture_request->function = 1; ++ manufacture_request->reserved = 0; ++ manufacture_request->request_length = 0; ++ ++ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; ++ mpi_request->PhysicalPort = 0xFF; ++ mpi_request->SASAddress = cpu_to_le64(sas_address); ++ mpi_request->RequestDataLength = cpu_to_le16(data_out_sz); ++ psge = &mpi_request->SGL; ++ ++ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma, ++ data_in_sz); ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "report_manufacture - send to sas_addr(0x%016llx)\n", ++ ioc->name, (unsigned long long)sas_address)); ++ init_completion(&ioc->transport_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, ++ 10*HZ); ++ ++ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SmpPassthroughRequest_t)/4); ++ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ goto issue_host_reset; ++ } ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "report_manufacture - complete\n", ioc->name)); ++ ++ if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) { ++ u8 *tmp; ++ ++ mpi_reply = ioc->transport_cmds.reply; ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "report_manufacture - reply data transfer size(%d)\n", ++ ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength))); ++ ++ if (le16_to_cpu(mpi_reply->ResponseDataLength) != ++ sizeof(struct rep_manu_reply)) ++ goto out; ++ ++ manufacture_reply = data_out + sizeof(struct rep_manu_request); ++ strncpy(edev->vendor_id, manufacture_reply->vendor_id, ++ SAS_EXPANDER_VENDOR_ID_LEN); ++ strncpy(edev->product_id, manufacture_reply->product_id, ++ SAS_EXPANDER_PRODUCT_ID_LEN); ++ strncpy(edev->product_rev, manufacture_reply->product_rev, ++ SAS_EXPANDER_PRODUCT_REV_LEN); ++ edev->level = manufacture_reply->sas_format & 1; ++ if (edev->level) { ++ strncpy(edev->component_vendor_id, ++ manufacture_reply->component_vendor_id, ++ SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN); ++ tmp = (u8 *)&manufacture_reply->component_id; ++ edev->component_id = tmp[0] << 8 | tmp[1]; ++ edev->component_revision_id = ++ manufacture_reply->component_revision_id; ++ } ++ } else ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "report_manufacture - no reply\n", ioc->name)); ++ ++ issue_host_reset: ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ out: ++ ioc->transport_cmds.status = MPT3_CMD_NOT_USED; ++ if (data_out) ++ pci_free_consistent(ioc->pdev, data_out_sz + data_in_sz, ++ data_out, data_out_dma); ++ ++ mutex_unlock(&ioc->transport_cmds.mutex); ++ return rc; ++} ++ ++ ++/** ++ * _transport_delete_port - helper function to removing a port ++ * @ioc: per adapter object ++ * @mpt2sas_port: mpt3sas per port object ++ * ++ * Returns nothing. ++ */ ++static void ++_transport_delete_port(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_port *mpt2sas_port) ++{ ++ u64 sas_address = mpt2sas_port->remote_identify.sas_address; ++ enum sas_device_type device_type = ++ mpt2sas_port->remote_identify.device_type; ++ ++ dev_printk(KERN_INFO, &mpt2sas_port->port->dev, ++ "remove: sas_addr(0x%016llx)\n", ++ (unsigned long long) sas_address); ++ ++ ioc->logging_level |= MPT_DEBUG_TRANSPORT; ++ if (device_type == SAS_END_DEVICE) ++ mpt2sas_device_remove_by_sas_address(ioc, sas_address); ++ else if (device_type == SAS_EDGE_EXPANDER_DEVICE || ++ device_type == SAS_FANOUT_EXPANDER_DEVICE) ++ mpt2sas_expander_remove(ioc, sas_address); ++ ioc->logging_level &= ~MPT_DEBUG_TRANSPORT; ++} ++ ++/** ++ * _transport_delete_phy - helper function to removing single phy from port ++ * @ioc: per adapter object ++ * @mpt2sas_port: mpt3sas per port object ++ * @mpt2sas_phy: mpt3sas per phy object ++ * ++ * Returns nothing. ++ */ ++static void ++_transport_delete_phy(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_port *mpt2sas_port, struct _sas_phy *mpt2sas_phy) ++{ ++ u64 sas_address = mpt2sas_port->remote_identify.sas_address; ++ ++ dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev, ++ "remove: sas_addr(0x%016llx), phy(%d)\n", ++ (unsigned long long) sas_address, mpt2sas_phy->phy_id); ++ ++ list_del(&mpt2sas_phy->port_siblings); ++ mpt2sas_port->num_phys--; ++ sas_port_delete_phy(mpt2sas_port->port, mpt2sas_phy->phy); ++ mpt2sas_phy->phy_belongs_to_port = 0; ++} ++ ++/** ++ * _transport_add_phy - helper function to adding single phy to port ++ * @ioc: per adapter object ++ * @mpt2sas_port: mpt3sas per port object ++ * @mpt2sas_phy: mpt3sas per phy object ++ * ++ * Returns nothing. ++ */ ++static void ++_transport_add_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_port *mpt2sas_port, ++ struct _sas_phy *mpt2sas_phy) ++{ ++ u64 sas_address = mpt2sas_port->remote_identify.sas_address; ++ ++ dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev, ++ "add: sas_addr(0x%016llx), phy(%d)\n", (unsigned long long) ++ sas_address, mpt2sas_phy->phy_id); ++ ++ list_add_tail(&mpt2sas_phy->port_siblings, &mpt2sas_port->phy_list); ++ mpt2sas_port->num_phys++; ++ sas_port_add_phy(mpt2sas_port->port, mpt2sas_phy->phy); ++ mpt2sas_phy->phy_belongs_to_port = 1; ++} ++ ++/** ++ * _transport_add_phy_to_an_existing_port - adding new phy to existing port ++ * @ioc: per adapter object ++ * @sas_node: sas node object (either expander or sas host) ++ * @mpt2sas_phy: mpt3sas per phy object ++ * @sas_address: sas address of device/expander were phy needs to be added to ++ * ++ * Returns nothing. ++ */ ++static void ++_transport_add_phy_to_an_existing_port(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_node *sas_node, struct _sas_phy *mpt2sas_phy, ++ u64 sas_address) ++{ ++ struct _sas_port *mpt2sas_port; ++ struct _sas_phy *phy_srch; ++ ++ if (mpt2sas_phy->phy_belongs_to_port == 1) ++ return; ++ ++ list_for_each_entry(mpt2sas_port, &sas_node->sas_port_list, ++ port_list) { ++ if (mpt2sas_port->remote_identify.sas_address != ++ sas_address) ++ continue; ++ list_for_each_entry(phy_srch, &mpt2sas_port->phy_list, ++ port_siblings) { ++ if (phy_srch == mpt2sas_phy) ++ return; ++ } ++ _transport_add_phy(ioc, mpt2sas_port, mpt2sas_phy); ++ return; ++ } ++ ++} ++ ++/** ++ * _transport_del_phy_from_an_existing_port - delete phy from existing port ++ * @ioc: per adapter object ++ * @sas_node: sas node object (either expander or sas host) ++ * @mpt2sas_phy: mpt3sas per phy object ++ * ++ * Returns nothing. ++ */ ++static void ++_transport_del_phy_from_an_existing_port(struct MPT3SAS_ADAPTER *ioc, ++ struct _sas_node *sas_node, struct _sas_phy *mpt2sas_phy) ++{ ++ struct _sas_port *mpt2sas_port, *next; ++ struct _sas_phy *phy_srch; ++ ++ if (mpt2sas_phy->phy_belongs_to_port == 0) ++ return; ++ ++ list_for_each_entry_safe(mpt2sas_port, next, &sas_node->sas_port_list, ++ port_list) { ++ list_for_each_entry(phy_srch, &mpt2sas_port->phy_list, ++ port_siblings) { ++ if (phy_srch != mpt2sas_phy) ++ continue; ++ ++ if (mpt2sas_port->num_phys == 1) ++ _transport_delete_port(ioc, mpt2sas_port); ++ else ++ _transport_delete_phy(ioc, mpt2sas_port, ++ mpt2sas_phy); ++ return; ++ } ++ } ++} ++ ++/** ++ * _transport_sanity_check - sanity check when adding a new port ++ * @ioc: per adapter object ++ * @sas_node: sas node object (either expander or sas host) ++ * @sas_address: sas address of device being added ++ * ++ * See the explanation above from _transport_delete_duplicate_port ++ */ ++static void ++_transport_sanity_check(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_node, ++ u64 sas_address) ++{ ++ int i; ++ ++ for (i = 0; i < sas_node->num_phys; i++) { ++ if (sas_node->phy[i].remote_identify.sas_address != sas_address) ++ continue; ++ if (sas_node->phy[i].phy_belongs_to_port == 1) ++ _transport_del_phy_from_an_existing_port(ioc, sas_node, ++ &sas_node->phy[i]); ++ } ++} ++ ++/** ++ * mpt2sas_transport_port_add - insert port to the list ++ * @ioc: per adapter object ++ * @handle: handle of attached device ++ * @sas_address: sas address of parent expander or sas host ++ * Context: This function will acquire ioc->sas_node_lock. ++ * ++ * Adding new port object to the sas_node->sas_port_list. ++ * ++ * Returns mpt2sas_port. ++ */ ++struct _sas_port * ++mpt2sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, ++ u64 sas_address) ++{ ++ struct _sas_phy *mpt2sas_phy, *next; ++ struct _sas_port *mpt2sas_port; ++ unsigned long flags; ++ struct _sas_node *sas_node; ++ struct sas_rphy *rphy; ++ struct _sas_device *sas_device = NULL; ++ int i; ++ struct sas_port *port; ++ ++ mpt2sas_port = kzalloc(sizeof(struct _sas_port), ++ GFP_KERNEL); ++ if (!mpt2sas_port) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return NULL; ++ } ++ ++ INIT_LIST_HEAD(&mpt2sas_port->port_list); ++ INIT_LIST_HEAD(&mpt2sas_port->phy_list); ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ if (!sas_node) { ++ pr_err(MPT3SAS_FMT ++ "%s: Could not find parent sas_address(0x%016llx)!\n", ++ ioc->name, __func__, (unsigned long long)sas_address); ++ goto out_fail; ++ } ++ ++ if ((_transport_set_identify(ioc, handle, ++ &mpt2sas_port->remote_identify))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out_fail; ++ } ++ ++ if (mpt2sas_port->remote_identify.device_type == SAS_PHY_UNUSED) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out_fail; ++ } ++ ++ _transport_sanity_check(ioc, sas_node, ++ mpt2sas_port->remote_identify.sas_address); ++ ++ for (i = 0; i < sas_node->num_phys; i++) { ++ if (sas_node->phy[i].remote_identify.sas_address != ++ mpt2sas_port->remote_identify.sas_address) ++ continue; ++ list_add_tail(&sas_node->phy[i].port_siblings, ++ &mpt2sas_port->phy_list); ++ mpt2sas_port->num_phys++; ++ } ++ ++ if (!mpt2sas_port->num_phys) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out_fail; ++ } ++ ++ port = sas_port_alloc_num(sas_node->parent_dev); ++ if ((sas_port_add(port))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ goto out_fail; ++ } ++ ++ list_for_each_entry(mpt2sas_phy, &mpt2sas_port->phy_list, ++ port_siblings) { ++ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) ++ dev_printk(KERN_INFO, &port->dev, ++ "add: handle(0x%04x), sas_addr(0x%016llx), phy(%d)\n", ++ handle, (unsigned long long) ++ mpt2sas_port->remote_identify.sas_address, ++ mpt2sas_phy->phy_id); ++ sas_port_add_phy(port, mpt2sas_phy->phy); ++ mpt2sas_phy->phy_belongs_to_port = 1; ++ } ++ ++ mpt2sas_port->port = port; ++ if (mpt2sas_port->remote_identify.device_type == SAS_END_DEVICE) ++ rphy = sas_end_device_alloc(port); ++ else ++ rphy = sas_expander_alloc(port, ++ mpt2sas_port->remote_identify.device_type); ++ ++ rphy->identify = mpt2sas_port->remote_identify; ++ ++ if (mpt2sas_port->remote_identify.device_type == SAS_END_DEVICE) { ++ sas_device = mpt2sas_get_sdev_by_addr(ioc, ++ mpt2sas_port->remote_identify.sas_address); ++ if (!sas_device) { ++ dfailprintk(ioc, printk(MPT3SAS_FMT ++ "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__)); ++ goto out_fail; ++ } ++ sas_device->pend_sas_rphy_add = 1; ++ } ++ ++ if ((sas_rphy_add(rphy))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ } ++ ++ if (mpt2sas_port->remote_identify.device_type == SAS_END_DEVICE) { ++ sas_device->pend_sas_rphy_add = 0; ++ sas_device_put(sas_device); ++ } ++ ++ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) ++ dev_printk(KERN_INFO, &rphy->dev, ++ "add: handle(0x%04x), sas_addr(0x%016llx)\n", ++ handle, (unsigned long long) ++ mpt2sas_port->remote_identify.sas_address); ++ mpt2sas_port->rphy = rphy; ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ list_add_tail(&mpt2sas_port->port_list, &sas_node->sas_port_list); ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ /* fill in report manufacture */ ++ if (mpt2sas_port->remote_identify.device_type == ++ MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER || ++ mpt2sas_port->remote_identify.device_type == ++ MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER) ++ _transport_expander_report_manufacture(ioc, ++ mpt2sas_port->remote_identify.sas_address, ++ rphy_to_expander_device(rphy)); ++ return mpt2sas_port; ++ ++ out_fail: ++ list_for_each_entry_safe(mpt2sas_phy, next, &mpt2sas_port->phy_list, ++ port_siblings) ++ list_del(&mpt2sas_phy->port_siblings); ++ kfree(mpt2sas_port); ++ return NULL; ++} ++ ++/** ++ * mpt2sas_transport_port_remove - remove port from the list ++ * @ioc: per adapter object ++ * @sas_address: sas address of attached device ++ * @sas_address_parent: sas address of parent expander or sas host ++ * Context: This function will acquire ioc->sas_node_lock. ++ * ++ * Removing object and freeing associated memory from the ++ * ioc->sas_port_list. ++ * ++ * Return nothing. ++ */ ++void ++mpt2sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, ++ u64 sas_address_parent) ++{ ++ int i; ++ unsigned long flags; ++ struct _sas_port *mpt2sas_port, *next; ++ struct _sas_node *sas_node; ++ u8 found = 0; ++ struct _sas_phy *mpt2sas_phy, *next_phy; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_node = _transport_sas_node_find_by_sas_address(ioc, ++ sas_address_parent); ++ if (!sas_node) { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return; ++ } ++ list_for_each_entry_safe(mpt2sas_port, next, &sas_node->sas_port_list, ++ port_list) { ++ if (mpt2sas_port->remote_identify.sas_address != sas_address) ++ continue; ++ found = 1; ++ list_del(&mpt2sas_port->port_list); ++ goto out; ++ } ++ out: ++ if (!found) { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return; ++ } ++ ++ for (i = 0; i < sas_node->num_phys; i++) { ++ if (sas_node->phy[i].remote_identify.sas_address == sas_address) ++ memset(&sas_node->phy[i].remote_identify, 0 , ++ sizeof(struct sas_identify)); ++ } ++ ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ list_for_each_entry_safe(mpt2sas_phy, next_phy, ++ &mpt2sas_port->phy_list, port_siblings) { ++ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) ++ dev_printk(KERN_INFO, &mpt2sas_port->port->dev, ++ "remove: sas_addr(0x%016llx), phy(%d)\n", ++ (unsigned long long) ++ mpt2sas_port->remote_identify.sas_address, ++ mpt2sas_phy->phy_id); ++ mpt2sas_phy->phy_belongs_to_port = 0; ++ sas_port_delete_phy(mpt2sas_port->port, mpt2sas_phy->phy); ++ list_del(&mpt2sas_phy->port_siblings); ++ } ++ sas_port_delete(mpt2sas_port->port); ++ kfree(mpt2sas_port); ++} ++ ++/** ++ * mpt2sas_transport_add_host_phy - report sas_host phy to transport ++ * @ioc: per adapter object ++ * @mpt2sas_phy: mpt3sas per phy object ++ * @phy_pg0: sas phy page 0 ++ * @parent_dev: parent device class object ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_transport_add_host_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy ++ *mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev) ++{ ++ struct sas_phy *phy; ++ int phy_index = mpt2sas_phy->phy_id; ++ ++ ++ INIT_LIST_HEAD(&mpt2sas_phy->port_siblings); ++ phy = sas_phy_alloc(parent_dev, phy_index); ++ if (!phy) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ if ((_transport_set_identify(ioc, mpt2sas_phy->handle, ++ &mpt2sas_phy->identify))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ sas_phy_free(phy); ++ return -1; ++ } ++ phy->identify = mpt2sas_phy->identify; ++ mpt2sas_phy->attached_handle = le16_to_cpu(phy_pg0.AttachedDevHandle); ++ if (mpt2sas_phy->attached_handle) ++ _transport_set_identify(ioc, mpt2sas_phy->attached_handle, ++ &mpt2sas_phy->remote_identify); ++ phy->identify.phy_identifier = mpt2sas_phy->phy_id; ++ phy->negotiated_linkrate = _transport_convert_phy_link_rate( ++ phy_pg0.NegotiatedLinkRate & MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL); ++ phy->minimum_linkrate_hw = _transport_convert_phy_link_rate( ++ phy_pg0.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK); ++ phy->maximum_linkrate_hw = _transport_convert_phy_link_rate( ++ phy_pg0.HwLinkRate >> 4); ++ phy->minimum_linkrate = _transport_convert_phy_link_rate( ++ phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); ++ phy->maximum_linkrate = _transport_convert_phy_link_rate( ++ phy_pg0.ProgrammedLinkRate >> 4); ++ ++ if ((sas_phy_add(phy))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ sas_phy_free(phy); ++ return -1; ++ } ++ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) ++ dev_printk(KERN_INFO, &phy->dev, ++ "add: handle(0x%04x), sas_addr(0x%016llx)\n" ++ "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", ++ mpt2sas_phy->handle, (unsigned long long) ++ mpt2sas_phy->identify.sas_address, ++ mpt2sas_phy->attached_handle, ++ (unsigned long long) ++ mpt2sas_phy->remote_identify.sas_address); ++ mpt2sas_phy->phy = phy; ++ return 0; ++} ++ ++ ++/** ++ * mpt2sas_transport_add_expander_phy - report expander phy to transport ++ * @ioc: per adapter object ++ * @mpt2sas_phy: mpt3sas per phy object ++ * @expander_pg1: expander page 1 ++ * @parent_dev: parent device class object ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++int ++mpt2sas_transport_add_expander_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy ++ *mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, ++ struct device *parent_dev) ++{ ++ struct sas_phy *phy; ++ int phy_index = mpt2sas_phy->phy_id; ++ ++ INIT_LIST_HEAD(&mpt2sas_phy->port_siblings); ++ phy = sas_phy_alloc(parent_dev, phy_index); ++ if (!phy) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ if ((_transport_set_identify(ioc, mpt2sas_phy->handle, ++ &mpt2sas_phy->identify))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ sas_phy_free(phy); ++ return -1; ++ } ++ phy->identify = mpt2sas_phy->identify; ++ mpt2sas_phy->attached_handle = ++ le16_to_cpu(expander_pg1.AttachedDevHandle); ++ if (mpt2sas_phy->attached_handle) ++ _transport_set_identify(ioc, mpt2sas_phy->attached_handle, ++ &mpt2sas_phy->remote_identify); ++ phy->identify.phy_identifier = mpt2sas_phy->phy_id; ++ phy->negotiated_linkrate = _transport_convert_phy_link_rate( ++ expander_pg1.NegotiatedLinkRate & ++ MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL); ++ phy->minimum_linkrate_hw = _transport_convert_phy_link_rate( ++ expander_pg1.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK); ++ phy->maximum_linkrate_hw = _transport_convert_phy_link_rate( ++ expander_pg1.HwLinkRate >> 4); ++ phy->minimum_linkrate = _transport_convert_phy_link_rate( ++ expander_pg1.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); ++ phy->maximum_linkrate = _transport_convert_phy_link_rate( ++ expander_pg1.ProgrammedLinkRate >> 4); ++ ++ if ((sas_phy_add(phy))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ sas_phy_free(phy); ++ return -1; ++ } ++ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) ++ dev_printk(KERN_INFO, &phy->dev, ++ "add: handle(0x%04x), sas_addr(0x%016llx)\n" ++ "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", ++ mpt2sas_phy->handle, (unsigned long long) ++ mpt2sas_phy->identify.sas_address, ++ mpt2sas_phy->attached_handle, ++ (unsigned long long) ++ mpt2sas_phy->remote_identify.sas_address); ++ mpt2sas_phy->phy = phy; ++ return 0; ++} ++ ++/** ++ * mpt2sas_transport_update_links - refreshing phy link changes ++ * @ioc: per adapter object ++ * @sas_address: sas address of parent expander or sas host ++ * @handle: attached device handle ++ * @phy_numberv: phy number ++ * @link_rate: new link rate ++ * ++ * Returns nothing. ++ */ ++void ++mpt2sas_transport_update_links(struct MPT3SAS_ADAPTER *ioc, ++ u64 sas_address, u16 handle, u8 phy_number, u8 link_rate) ++{ ++ unsigned long flags; ++ struct _sas_node *sas_node; ++ struct _sas_phy *mpt2sas_phy; ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery) ++ return; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address); ++ if (!sas_node) { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return; ++ } ++ ++ mpt2sas_phy = &sas_node->phy[phy_number]; ++ mpt2sas_phy->attached_handle = handle; ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) { ++ _transport_set_identify(ioc, handle, ++ &mpt2sas_phy->remote_identify); ++ _transport_add_phy_to_an_existing_port(ioc, sas_node, ++ mpt2sas_phy, mpt2sas_phy->remote_identify.sas_address); ++ } else ++ memset(&mpt2sas_phy->remote_identify, 0 , sizeof(struct ++ sas_identify)); ++ ++ if (mpt2sas_phy->phy) ++ mpt2sas_phy->phy->negotiated_linkrate = ++ _transport_convert_phy_link_rate(link_rate); ++ ++ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) ++ dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev, ++ "refresh: parent sas_addr(0x%016llx),\n" ++ "\tlink_rate(0x%02x), phy(%d)\n" ++ "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", ++ (unsigned long long)sas_address, ++ link_rate, phy_number, handle, (unsigned long long) ++ mpt2sas_phy->remote_identify.sas_address); ++} ++ ++static inline void * ++phy_to_ioc(struct sas_phy *phy) ++{ ++ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); ++ return shost_priv(shost); ++} ++ ++static inline void * ++rphy_to_ioc(struct sas_rphy *rphy) ++{ ++ struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent); ++ return shost_priv(shost); ++} ++ ++/* report phy error log structure */ ++struct phy_error_log_request { ++ u8 smp_frame_type; /* 0x40 */ ++ u8 function; /* 0x11 */ ++ u8 allocated_response_length; ++ u8 request_length; /* 02 */ ++ u8 reserved_1[5]; ++ u8 phy_identifier; ++ u8 reserved_2[2]; ++}; ++ ++/* report phy error log reply structure */ ++struct phy_error_log_reply { ++ u8 smp_frame_type; /* 0x41 */ ++ u8 function; /* 0x11 */ ++ u8 function_result; ++ u8 response_length; ++ __be16 expander_change_count; ++ u8 reserved_1[3]; ++ u8 phy_identifier; ++ u8 reserved_2[2]; ++ __be32 invalid_dword; ++ __be32 running_disparity_error; ++ __be32 loss_of_dword_sync; ++ __be32 phy_reset_problem; ++}; ++ ++/** ++ * _transport_get_expander_phy_error_log - return expander counters ++ * @ioc: per adapter object ++ * @phy: The sas phy object ++ * ++ * Returns 0 for success, non-zero for failure. ++ * ++ */ ++static int ++_transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc, ++ struct sas_phy *phy) ++{ ++ Mpi2SmpPassthroughRequest_t *mpi_request; ++ Mpi2SmpPassthroughReply_t *mpi_reply; ++ struct phy_error_log_request *phy_error_log_request; ++ struct phy_error_log_reply *phy_error_log_reply; ++ int rc; ++ u16 smid; ++ u32 ioc_state; ++ unsigned long timeleft; ++ void *psge; ++ u8 issue_reset = 0; ++ void *data_out = NULL; ++ dma_addr_t data_out_dma; ++ u32 sz; ++ u16 wait_state_count; ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery) { ++ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", ++ __func__, ioc->name); ++ return -EFAULT; ++ } ++ ++ mutex_lock(&ioc->transport_cmds.mutex); ++ ++ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: transport_cmds in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ioc->transport_cmds.status = MPT3_CMD_PENDING; ++ ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, __func__, wait_state_count); ++ } ++ if (wait_state_count) ++ pr_info(MPT3SAS_FMT "%s: ioc is operational\n", ++ ioc->name, __func__); ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->transport_cmds.smid = smid; ++ ++ sz = sizeof(struct phy_error_log_request) + ++ sizeof(struct phy_error_log_reply); ++ data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma); ++ if (!data_out) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ rc = -ENOMEM; ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ ++ rc = -EINVAL; ++ memset(data_out, 0, sz); ++ phy_error_log_request = data_out; ++ phy_error_log_request->smp_frame_type = 0x40; ++ phy_error_log_request->function = 0x11; ++ phy_error_log_request->request_length = 2; ++ phy_error_log_request->allocated_response_length = 0; ++ phy_error_log_request->phy_identifier = phy->number; ++ ++ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; ++ mpi_request->PhysicalPort = 0xFF; ++ mpi_request->VF_ID = 0; /* TODO */ ++ mpi_request->VP_ID = 0; ++ mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address); ++ mpi_request->RequestDataLength = ++ cpu_to_le16(sizeof(struct phy_error_log_request)); ++ psge = &mpi_request->SGL; ++ ++ ioc->build_sg(ioc, psge, data_out_dma, ++ sizeof(struct phy_error_log_request), ++ data_out_dma + sizeof(struct phy_error_log_request), ++ sizeof(struct phy_error_log_reply)); ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_error_log - send to sas_addr(0x%016llx), phy(%d)\n", ++ ioc->name, (unsigned long long)phy->identify.sas_address, ++ phy->number)); ++ init_completion(&ioc->transport_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, ++ 10*HZ); ++ ++ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SmpPassthroughRequest_t)/4); ++ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ goto issue_host_reset; ++ } ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_error_log - complete\n", ioc->name)); ++ ++ if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) { ++ ++ mpi_reply = ioc->transport_cmds.reply; ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_error_log - reply data transfer size(%d)\n", ++ ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength))); ++ ++ if (le16_to_cpu(mpi_reply->ResponseDataLength) != ++ sizeof(struct phy_error_log_reply)) ++ goto out; ++ ++ phy_error_log_reply = data_out + ++ sizeof(struct phy_error_log_request); ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_error_log - function_result(%d)\n", ++ ioc->name, phy_error_log_reply->function_result)); ++ ++ phy->invalid_dword_count = ++ be32_to_cpu(phy_error_log_reply->invalid_dword); ++ phy->running_disparity_error_count = ++ be32_to_cpu(phy_error_log_reply->running_disparity_error); ++ phy->loss_of_dword_sync_count = ++ be32_to_cpu(phy_error_log_reply->loss_of_dword_sync); ++ phy->phy_reset_problem_count = ++ be32_to_cpu(phy_error_log_reply->phy_reset_problem); ++ rc = 0; ++ } else ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_error_log - no reply\n", ioc->name)); ++ ++ issue_host_reset: ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ out: ++ ioc->transport_cmds.status = MPT3_CMD_NOT_USED; ++ if (data_out) ++ pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma); ++ ++ mutex_unlock(&ioc->transport_cmds.mutex); ++ return rc; ++} ++ ++/** ++ * _transport_get_linkerrors - return phy counters for both hba and expanders ++ * @phy: The sas phy object ++ * ++ * Returns 0 for success, non-zero for failure. ++ * ++ */ ++static int ++_transport_get_linkerrors(struct sas_phy *phy) ++{ ++ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy); ++ unsigned long flags; ++ Mpi2ConfigReply_t mpi_reply; ++ Mpi2SasPhyPage1_t phy_pg1; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ if (_transport_sas_node_find_by_sas_address(ioc, ++ phy->identify.sas_address) == NULL) { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return -EINVAL; ++ } ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ if (phy->identify.sas_address != ioc->sas_hba.sas_address) ++ return _transport_get_expander_phy_error_log(ioc, phy); ++ ++ /* get hba phy error logs */ ++ if ((mpt2sas_config_get_phy_pg1(ioc, &mpi_reply, &phy_pg1, ++ phy->number))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -ENXIO; ++ } ++ ++ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) ++ pr_info(MPT3SAS_FMT ++ "phy(%d), ioc_status (0x%04x), loginfo(0x%08x)\n", ++ ioc->name, phy->number, ++ le16_to_cpu(mpi_reply.IOCStatus), ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ ++ phy->invalid_dword_count = le32_to_cpu(phy_pg1.InvalidDwordCount); ++ phy->running_disparity_error_count = ++ le32_to_cpu(phy_pg1.RunningDisparityErrorCount); ++ phy->loss_of_dword_sync_count = ++ le32_to_cpu(phy_pg1.LossDwordSynchCount); ++ phy->phy_reset_problem_count = ++ le32_to_cpu(phy_pg1.PhyResetProblemCount); ++ return 0; ++} ++ ++/** ++ * _transport_get_enclosure_identifier - ++ * @phy: The sas phy object ++ * ++ * Obtain the enclosure logical id for an expander. ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier) ++{ ++ struct MPT3SAS_ADAPTER *ioc = rphy_to_ioc(rphy); ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ int rc; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ rphy->identify.sas_address); ++ if (sas_device) { ++ *identifier = sas_device->enclosure_logical_id; ++ rc = 0; ++ sas_device_put(sas_device); ++ } else { ++ *identifier = 0; ++ rc = -ENXIO; ++ } ++ ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ return rc; ++} ++ ++/** ++ * _transport_get_bay_identifier - ++ * @phy: The sas phy object ++ * ++ * Returns the slot id for a device that resides inside an enclosure. ++ */ ++static int ++_transport_get_bay_identifier(struct sas_rphy *rphy) ++{ ++ struct MPT3SAS_ADAPTER *ioc = rphy_to_ioc(rphy); ++ struct _sas_device *sas_device; ++ unsigned long flags; ++ int rc; ++ ++ spin_lock_irqsave(&ioc->sas_device_lock, flags); ++ sas_device = __mpt2sas_get_sdev_by_addr(ioc, ++ rphy->identify.sas_address); ++ if (sas_device) { ++ rc = sas_device->slot; ++ sas_device_put(sas_device); ++ } else { ++ rc = -ENXIO; ++ } ++ spin_unlock_irqrestore(&ioc->sas_device_lock, flags); ++ return rc; ++} ++ ++/* phy control request structure */ ++struct phy_control_request { ++ u8 smp_frame_type; /* 0x40 */ ++ u8 function; /* 0x91 */ ++ u8 allocated_response_length; ++ u8 request_length; /* 0x09 */ ++ u16 expander_change_count; ++ u8 reserved_1[3]; ++ u8 phy_identifier; ++ u8 phy_operation; ++ u8 reserved_2[13]; ++ u64 attached_device_name; ++ u8 programmed_min_physical_link_rate; ++ u8 programmed_max_physical_link_rate; ++ u8 reserved_3[6]; ++}; ++ ++/* phy control reply structure */ ++struct phy_control_reply { ++ u8 smp_frame_type; /* 0x41 */ ++ u8 function; /* 0x11 */ ++ u8 function_result; ++ u8 response_length; ++}; ++ ++#define SMP_PHY_CONTROL_LINK_RESET (0x01) ++#define SMP_PHY_CONTROL_HARD_RESET (0x02) ++#define SMP_PHY_CONTROL_DISABLE (0x03) ++ ++/** ++ * _transport_expander_phy_control - expander phy control ++ * @ioc: per adapter object ++ * @phy: The sas phy object ++ * ++ * Returns 0 for success, non-zero for failure. ++ * ++ */ ++static int ++_transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc, ++ struct sas_phy *phy, u8 phy_operation) ++{ ++ Mpi2SmpPassthroughRequest_t *mpi_request; ++ Mpi2SmpPassthroughReply_t *mpi_reply; ++ struct phy_control_request *phy_control_request; ++ struct phy_control_reply *phy_control_reply; ++ int rc; ++ u16 smid; ++ u32 ioc_state; ++ unsigned long timeleft; ++ void *psge; ++ u8 issue_reset = 0; ++ void *data_out = NULL; ++ dma_addr_t data_out_dma; ++ u32 sz; ++ u16 wait_state_count; ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery) { ++ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", ++ __func__, ioc->name); ++ return -EFAULT; ++ } ++ ++ mutex_lock(&ioc->transport_cmds.mutex); ++ ++ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: transport_cmds in use\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ioc->transport_cmds.status = MPT3_CMD_PENDING; ++ ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto out; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, __func__, wait_state_count); ++ } ++ if (wait_state_count) ++ pr_info(MPT3SAS_FMT "%s: ioc is operational\n", ++ ioc->name, __func__); ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->transport_cmds.smid = smid; ++ ++ sz = sizeof(struct phy_control_request) + ++ sizeof(struct phy_control_reply); ++ data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma); ++ if (!data_out) { ++ pr_err("failure at %s:%d/%s()!\n", __FILE__, ++ __LINE__, __func__); ++ rc = -ENOMEM; ++ mpt2sas_base_free_smid(ioc, smid); ++ goto out; ++ } ++ ++ rc = -EINVAL; ++ memset(data_out, 0, sz); ++ phy_control_request = data_out; ++ phy_control_request->smp_frame_type = 0x40; ++ phy_control_request->function = 0x91; ++ phy_control_request->request_length = 9; ++ phy_control_request->allocated_response_length = 0; ++ phy_control_request->phy_identifier = phy->number; ++ phy_control_request->phy_operation = phy_operation; ++ phy_control_request->programmed_min_physical_link_rate = ++ phy->minimum_linkrate << 4; ++ phy_control_request->programmed_max_physical_link_rate = ++ phy->maximum_linkrate << 4; ++ ++ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; ++ mpi_request->PhysicalPort = 0xFF; ++ mpi_request->VF_ID = 0; /* TODO */ ++ mpi_request->VP_ID = 0; ++ mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address); ++ mpi_request->RequestDataLength = ++ cpu_to_le16(sizeof(struct phy_error_log_request)); ++ psge = &mpi_request->SGL; ++ ++ ioc->build_sg(ioc, psge, data_out_dma, ++ sizeof(struct phy_control_request), ++ data_out_dma + sizeof(struct phy_control_request), ++ sizeof(struct phy_control_reply)); ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_control - send to sas_addr(0x%016llx), phy(%d), opcode(%d)\n", ++ ioc->name, (unsigned long long)phy->identify.sas_address, ++ phy->number, phy_operation)); ++ init_completion(&ioc->transport_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, ++ 10*HZ); ++ ++ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s: timeout\n", ++ ioc->name, __func__); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SmpPassthroughRequest_t)/4); ++ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ goto issue_host_reset; ++ } ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_control - complete\n", ioc->name)); ++ ++ if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) { ++ ++ mpi_reply = ioc->transport_cmds.reply; ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_control - reply data transfer size(%d)\n", ++ ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength))); ++ ++ if (le16_to_cpu(mpi_reply->ResponseDataLength) != ++ sizeof(struct phy_control_reply)) ++ goto out; ++ ++ phy_control_reply = data_out + ++ sizeof(struct phy_control_request); ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_control - function_result(%d)\n", ++ ioc->name, phy_control_reply->function_result)); ++ ++ rc = 0; ++ } else ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "phy_control - no reply\n", ioc->name)); ++ ++ issue_host_reset: ++ if (issue_reset) ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ out: ++ ioc->transport_cmds.status = MPT3_CMD_NOT_USED; ++ if (data_out) ++ pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma); ++ ++ mutex_unlock(&ioc->transport_cmds.mutex); ++ return rc; ++} ++ ++/** ++ * _transport_phy_reset - ++ * @phy: The sas phy object ++ * @hard_reset: ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_transport_phy_reset(struct sas_phy *phy, int hard_reset) ++{ ++ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy); ++ Mpi2SasIoUnitControlReply_t mpi_reply; ++ Mpi2SasIoUnitControlRequest_t mpi_request; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ if (_transport_sas_node_find_by_sas_address(ioc, ++ phy->identify.sas_address) == NULL) { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return -EINVAL; ++ } ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ /* handle expander phys */ ++ if (phy->identify.sas_address != ioc->sas_hba.sas_address) ++ return _transport_expander_phy_control(ioc, phy, ++ (hard_reset == 1) ? SMP_PHY_CONTROL_HARD_RESET : ++ SMP_PHY_CONTROL_LINK_RESET); ++ ++ /* handle hba phys */ ++ memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); ++ mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; ++ mpi_request.Operation = hard_reset ? ++ MPI2_SAS_OP_PHY_HARD_RESET : MPI2_SAS_OP_PHY_LINK_RESET; ++ mpi_request.PhyNum = phy->number; ++ ++ if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply, &mpi_request))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -ENXIO; ++ } ++ ++ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) ++ pr_info(MPT3SAS_FMT ++ "phy(%d), ioc_status(0x%04x), loginfo(0x%08x)\n", ++ ioc->name, phy->number, le16_to_cpu(mpi_reply.IOCStatus), ++ le32_to_cpu(mpi_reply.IOCLogInfo)); ++ ++ return 0; ++} ++ ++/** ++ * _transport_phy_enable - enable/disable phys ++ * @phy: The sas phy object ++ * @enable: enable phy when true ++ * ++ * Only support sas_host direct attached phys. ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_transport_phy_enable(struct sas_phy *phy, int enable) ++{ ++ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy); ++ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; ++ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 ioc_status; ++ u16 sz; ++ int rc = 0; ++ unsigned long flags; ++ int i, discovery_active; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ if (_transport_sas_node_find_by_sas_address(ioc, ++ phy->identify.sas_address) == NULL) { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return -EINVAL; ++ } ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ /* handle expander phys */ ++ if (phy->identify.sas_address != ioc->sas_hba.sas_address) ++ return _transport_expander_phy_control(ioc, phy, ++ (enable == 1) ? SMP_PHY_CONTROL_LINK_RESET : ++ SMP_PHY_CONTROL_DISABLE); ++ ++ /* handle hba phys */ ++ ++ /* read sas_iounit page 0 */ ++ sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys * ++ sizeof(Mpi2SasIOUnit0PhyData_t)); ++ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); ++ if (!sas_iounit_pg0) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -ENOMEM; ++ goto out; ++ } ++ if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, ++ sas_iounit_pg0, sz))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -ENXIO; ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -EIO; ++ goto out; ++ } ++ ++ /* unable to enable/disable phys when when discovery is active */ ++ for (i = 0, discovery_active = 0; i < ioc->sas_hba.num_phys ; i++) { ++ if (sas_iounit_pg0->PhyData[i].PortFlags & ++ MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) { ++ pr_err(MPT3SAS_FMT "discovery is active on " \ ++ "port = %d, phy = %d: unable to enable/disable " ++ "phys, try again later!\n", ioc->name, ++ sas_iounit_pg0->PhyData[i].Port, i); ++ discovery_active = 1; ++ } ++ } ++ ++ if (discovery_active) { ++ rc = -EAGAIN; ++ goto out; ++ } ++ ++ /* read sas_iounit page 1 */ ++ sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * ++ sizeof(Mpi2SasIOUnit1PhyData_t)); ++ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); ++ if (!sas_iounit_pg1) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -ENOMEM; ++ goto out; ++ } ++ if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, ++ sas_iounit_pg1, sz))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -ENXIO; ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -EIO; ++ goto out; ++ } ++ ++ /* copy Port/PortFlags/PhyFlags from page 0 */ ++ for (i = 0; i < ioc->sas_hba.num_phys ; i++) { ++ sas_iounit_pg1->PhyData[i].Port = ++ sas_iounit_pg0->PhyData[i].Port; ++ sas_iounit_pg1->PhyData[i].PortFlags = ++ (sas_iounit_pg0->PhyData[i].PortFlags & ++ MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG); ++ sas_iounit_pg1->PhyData[i].PhyFlags = ++ (sas_iounit_pg0->PhyData[i].PhyFlags & ++ (MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED + ++ MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED)); ++ } ++ ++ if (enable) ++ sas_iounit_pg1->PhyData[phy->number].PhyFlags ++ &= ~MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE; ++ else ++ sas_iounit_pg1->PhyData[phy->number].PhyFlags ++ |= MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE; ++ ++ mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, sz); ++ ++ /* link reset */ ++ if (enable) ++ _transport_phy_reset(phy, 0); ++ ++ out: ++ kfree(sas_iounit_pg1); ++ kfree(sas_iounit_pg0); ++ return rc; ++} ++ ++/** ++ * _transport_phy_speed - set phy min/max link rates ++ * @phy: The sas phy object ++ * @rates: rates defined in sas_phy_linkrates ++ * ++ * Only support sas_host direct attached phys. ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++_transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates) ++{ ++ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy); ++ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; ++ Mpi2SasPhyPage0_t phy_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 ioc_status; ++ u16 sz; ++ int i; ++ int rc = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioc->sas_node_lock, flags); ++ if (_transport_sas_node_find_by_sas_address(ioc, ++ phy->identify.sas_address) == NULL) { ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ return -EINVAL; ++ } ++ spin_unlock_irqrestore(&ioc->sas_node_lock, flags); ++ ++ if (!rates->minimum_linkrate) ++ rates->minimum_linkrate = phy->minimum_linkrate; ++ else if (rates->minimum_linkrate < phy->minimum_linkrate_hw) ++ rates->minimum_linkrate = phy->minimum_linkrate_hw; ++ ++ if (!rates->maximum_linkrate) ++ rates->maximum_linkrate = phy->maximum_linkrate; ++ else if (rates->maximum_linkrate > phy->maximum_linkrate_hw) ++ rates->maximum_linkrate = phy->maximum_linkrate_hw; ++ ++ /* handle expander phys */ ++ if (phy->identify.sas_address != ioc->sas_hba.sas_address) { ++ phy->minimum_linkrate = rates->minimum_linkrate; ++ phy->maximum_linkrate = rates->maximum_linkrate; ++ return _transport_expander_phy_control(ioc, phy, ++ SMP_PHY_CONTROL_LINK_RESET); ++ } ++ ++ /* handle hba phys */ ++ ++ /* sas_iounit page 1 */ ++ sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * ++ sizeof(Mpi2SasIOUnit1PhyData_t)); ++ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); ++ if (!sas_iounit_pg1) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -ENOMEM; ++ goto out; ++ } ++ if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, ++ sas_iounit_pg1, sz))) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -ENXIO; ++ goto out; ++ } ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -EIO; ++ goto out; ++ } ++ ++ for (i = 0; i < ioc->sas_hba.num_phys; i++) { ++ if (phy->number != i) { ++ sas_iounit_pg1->PhyData[i].MaxMinLinkRate = ++ (ioc->sas_hba.phy[i].phy->minimum_linkrate + ++ (ioc->sas_hba.phy[i].phy->maximum_linkrate << 4)); ++ } else { ++ sas_iounit_pg1->PhyData[i].MaxMinLinkRate = ++ (rates->minimum_linkrate + ++ (rates->maximum_linkrate << 4)); ++ } ++ } ++ ++ if (mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, ++ sz)) { ++ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ /* link reset */ ++ _transport_phy_reset(phy, 0); ++ ++ /* read phy page 0, then update the rates in the sas transport phy */ ++ if (!mpt2sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0, ++ phy->number)) { ++ phy->minimum_linkrate = _transport_convert_phy_link_rate( ++ phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); ++ phy->maximum_linkrate = _transport_convert_phy_link_rate( ++ phy_pg0.ProgrammedLinkRate >> 4); ++ phy->negotiated_linkrate = _transport_convert_phy_link_rate( ++ phy_pg0.NegotiatedLinkRate & ++ MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL); ++ } ++ ++ out: ++ kfree(sas_iounit_pg1); ++ return rc; ++} ++ ++/** ++ * _transport_smp_handler - transport portal for smp passthru ++ * @shost: shost object ++ * @rphy: sas transport rphy object ++ * @req: ++ * ++ * This used primarily for smp_utils. ++ * Example: ++ * smp_rep_general /sys/class/bsg/expander-5:0 ++ */ ++static int ++_transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, ++ struct request *req) ++{ ++ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); ++ Mpi2SmpPassthroughRequest_t *mpi_request; ++ Mpi2SmpPassthroughReply_t *mpi_reply; ++ int rc, i; ++ u16 smid; ++ u32 ioc_state; ++ unsigned long timeleft; ++ void *psge; ++ u8 issue_reset = 0; ++ dma_addr_t dma_addr_in = 0; ++ dma_addr_t dma_addr_out = 0; ++ dma_addr_t pci_dma_in = 0; ++ dma_addr_t pci_dma_out = 0; ++ void *pci_addr_in = NULL; ++ void *pci_addr_out = NULL; ++ u16 wait_state_count; ++ struct request *rsp = req->next_rq; ++ struct bio_vec *bvec = NULL; ++ ++ if (!rsp) { ++ pr_err(MPT3SAS_FMT "%s: the smp response space is missing\n", ++ ioc->name, __func__); ++ return -EINVAL; ++ } ++ ++ if (ioc->shost_recovery || ioc->pci_error_recovery) { ++ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", ++ __func__, ioc->name); ++ return -EFAULT; ++ } ++ ++ rc = mutex_lock_interruptible(&ioc->transport_cmds.mutex); ++ if (rc) ++ return rc; ++ ++ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) { ++ pr_err(MPT3SAS_FMT "%s: transport_cmds in use\n", ioc->name, ++ __func__); ++ rc = -EAGAIN; ++ goto out; ++ } ++ ioc->transport_cmds.status = MPT3_CMD_PENDING; ++ ++ /* Check if the request is split across multiple segments */ ++ if (req->bio->bi_vcnt > 1) { ++ u32 offset = 0; ++ ++ /* Allocate memory and copy the request */ ++ pci_addr_out = pci_alloc_consistent(ioc->pdev, ++ blk_rq_bytes(req), &pci_dma_out); ++ if (!pci_addr_out) { ++ pr_info(MPT3SAS_FMT "%s(): PCI Addr out = NULL\n", ++ ioc->name, __func__); ++ rc = -ENOMEM; ++ goto out; ++ } ++ ++ bio_for_each_segment(bvec, req->bio, i) { ++ memcpy(pci_addr_out + offset, ++ page_address(bvec->bv_page) + bvec->bv_offset, ++ bvec->bv_len); ++ offset += bvec->bv_len; ++ } ++ } else { ++ dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio), ++ blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL); ++ if (pci_dma_mapping_error(ioc->pdev, dma_addr_out)) { ++ pr_info(MPT3SAS_FMT "%s(): DMA Addr out = NULL\n", ++ ioc->name, __func__); ++ rc = -ENOMEM; ++ goto free_pci; ++ } ++ } ++ ++ /* Check if the response needs to be populated across ++ * multiple segments */ ++ if (rsp->bio->bi_vcnt > 1) { ++ pci_addr_in = pci_alloc_consistent(ioc->pdev, blk_rq_bytes(rsp), ++ &pci_dma_in); ++ if (!pci_addr_in) { ++ pr_info(MPT3SAS_FMT "%s(): PCI Addr in = NULL\n", ++ ioc->name, __func__); ++ rc = -ENOMEM; ++ goto unmap; ++ } ++ } else { ++ dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio), ++ blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL); ++ if (pci_dma_mapping_error(ioc->pdev, dma_addr_in)) { ++ pr_info(MPT3SAS_FMT "%s(): DMA Addr in = NULL\n", ++ ioc->name, __func__); ++ rc = -ENOMEM; ++ goto unmap; ++ } ++ } ++ ++ wait_state_count = 0; ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { ++ if (wait_state_count++ == 10) { ++ pr_err(MPT3SAS_FMT ++ "%s: failed due to ioc not operational\n", ++ ioc->name, __func__); ++ rc = -EFAULT; ++ goto unmap; ++ } ++ ssleep(1); ++ ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ++ pr_info(MPT3SAS_FMT ++ "%s: waiting for operational state(count=%d)\n", ++ ioc->name, __func__, wait_state_count); ++ } ++ if (wait_state_count) ++ pr_info(MPT3SAS_FMT "%s: ioc is operational\n", ++ ioc->name, __func__); ++ ++ smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); ++ if (!smid) { ++ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ++ ioc->name, __func__); ++ rc = -EAGAIN; ++ goto unmap; ++ } ++ ++ rc = 0; ++ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ++ ioc->transport_cmds.smid = smid; ++ ++ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); ++ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; ++ mpi_request->PhysicalPort = 0xFF; ++ mpi_request->SASAddress = (rphy) ? ++ cpu_to_le64(rphy->identify.sas_address) : ++ cpu_to_le64(ioc->sas_hba.sas_address); ++ mpi_request->RequestDataLength = cpu_to_le16(blk_rq_bytes(req) - 4); ++ psge = &mpi_request->SGL; ++ ++ if (req->bio->bi_vcnt > 1) ++ ioc->build_sg(ioc, psge, pci_dma_out, (blk_rq_bytes(req) - 4), ++ pci_dma_in, (blk_rq_bytes(rsp) + 4)); ++ else ++ ioc->build_sg(ioc, psge, dma_addr_out, (blk_rq_bytes(req) - 4), ++ dma_addr_in, (blk_rq_bytes(rsp) + 4)); ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s - sending smp request\n", ioc->name, __func__)); ++ ++ init_completion(&ioc->transport_cmds.done); ++ mpt2sas_base_put_smid_default(ioc, smid); ++ timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, ++ 10*HZ); ++ ++ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { ++ pr_err(MPT3SAS_FMT "%s : timeout\n", ++ __func__, ioc->name); ++ _debug_dump_mf(mpi_request, ++ sizeof(Mpi2SmpPassthroughRequest_t)/4); ++ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) ++ issue_reset = 1; ++ goto issue_host_reset; ++ } ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s - complete\n", ioc->name, __func__)); ++ ++ if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) { ++ ++ mpi_reply = ioc->transport_cmds.reply; ++ ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s - reply data transfer size(%d)\n", ++ ioc->name, __func__, ++ le16_to_cpu(mpi_reply->ResponseDataLength))); ++ ++ memcpy(req->sense, mpi_reply, sizeof(*mpi_reply)); ++ req->sense_len = sizeof(*mpi_reply); ++ req->resid_len = 0; ++ rsp->resid_len -= ++ le16_to_cpu(mpi_reply->ResponseDataLength); ++ ++ /* check if the resp needs to be copied from the allocated ++ * pci mem */ ++ if (rsp->bio->bi_vcnt > 1) { ++ u32 offset = 0; ++ u32 bytes_to_copy = ++ le16_to_cpu(mpi_reply->ResponseDataLength); ++ bio_for_each_segment(bvec, rsp->bio, i) { ++ if (bytes_to_copy <= bvec->bv_len) { ++ memcpy(page_address(bvec->bv_page) + ++ bvec->bv_offset, pci_addr_in + ++ offset, bytes_to_copy); ++ break; ++ } else { ++ memcpy(page_address(bvec->bv_page) + ++ bvec->bv_offset, pci_addr_in + ++ offset, bvec->bv_len); ++ bytes_to_copy -= bvec->bv_len; ++ } ++ offset += bvec->bv_len; ++ } ++ } ++ } else { ++ dtransportprintk(ioc, pr_info(MPT3SAS_FMT ++ "%s - no reply\n", ioc->name, __func__)); ++ rc = -ENXIO; ++ } ++ ++ issue_host_reset: ++ if (issue_reset) { ++ mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, ++ FORCE_BIG_HAMMER); ++ rc = -ETIMEDOUT; ++ } ++ ++ unmap: ++ if (dma_addr_out) ++ pci_unmap_single(ioc->pdev, dma_addr_out, blk_rq_bytes(req), ++ PCI_DMA_BIDIRECTIONAL); ++ if (dma_addr_in) ++ pci_unmap_single(ioc->pdev, dma_addr_in, blk_rq_bytes(rsp), ++ PCI_DMA_BIDIRECTIONAL); ++ ++ free_pci: ++ if (pci_addr_out) ++ pci_free_consistent(ioc->pdev, blk_rq_bytes(req), pci_addr_out, ++ pci_dma_out); ++ ++ if (pci_addr_in) ++ pci_free_consistent(ioc->pdev, blk_rq_bytes(rsp), pci_addr_in, ++ pci_dma_in); ++ ++ out: ++ ioc->transport_cmds.status = MPT3_CMD_NOT_USED; ++ mutex_unlock(&ioc->transport_cmds.mutex); ++ return rc; ++} ++ ++struct sas_function_template mpt2sas_transport_functions = { ++ .get_linkerrors = _transport_get_linkerrors, ++ .get_enclosure_identifier = _transport_get_enclosure_identifier, ++ .get_bay_identifier = _transport_get_bay_identifier, ++ .phy_reset = _transport_phy_reset, ++ .phy_enable = _transport_phy_enable, ++ .set_phy_speed = _transport_phy_speed, ++ .smp_handler = _transport_smp_handler, ++}; ++ ++struct scsi_transport_template *mpt2sas_transport_template; +diff --git a/drivers/scsi/mpt2sas/mpt3sas_trigger_diag.c b/drivers/scsi/mpt2sas/mpt3sas_trigger_diag.c +new file mode 100644 +index 0000000..d5038ec +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_trigger_diag.c +@@ -0,0 +1,434 @@ ++/* ++ * This module provides common API to set Diagnostic trigger for MPT ++ * (Message Passing Technology) based controllers ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "mpt3sas_base.h" ++ ++/** ++ * _mpt2sas_raise_sigio - notifiy app ++ * @ioc: per adapter object ++ * @event_data: ++ */ ++static void ++_mpt2sas_raise_sigio(struct MPT3SAS_ADAPTER *ioc, ++ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data) ++{ ++ Mpi2EventNotificationReply_t *mpi_reply; ++ u16 sz, event_data_sz; ++ unsigned long flags; ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ++ ioc->name, __func__)); ++ ++ sz = offsetof(Mpi2EventNotificationReply_t, EventData) + ++ sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T) + 4; ++ mpi_reply = kzalloc(sz, GFP_KERNEL); ++ if (!mpi_reply) ++ goto out; ++ mpi_reply->Event = cpu_to_le16(MPI3_EVENT_DIAGNOSTIC_TRIGGER_FIRED); ++ event_data_sz = (sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T) + 4) / 4; ++ mpi_reply->EventDataLength = cpu_to_le16(event_data_sz); ++ memcpy(&mpi_reply->EventData, event_data, ++ sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T)); ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: add to driver event log\n", ++ ioc->name, __func__)); ++ mpt2sas_ctl_add_to_event_log(ioc, mpi_reply); ++ kfree(mpi_reply); ++ out: ++ ++ /* clearing the diag_trigger_active flag */ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: clearing diag_trigger_active flag\n", ++ ioc->name, __func__)); ++ ioc->diag_trigger_active = 0; ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++} ++ ++/** ++ * mpt2sas_process_trigger_data - process the event data for the trigger ++ * @ioc: per adapter object ++ * @event_data: ++ */ ++void ++mpt2sas_process_trigger_data(struct MPT3SAS_ADAPTER *ioc, ++ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data) ++{ ++ u8 issue_reset = 0; ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ++ ioc->name, __func__)); ++ ++ /* release the diag buffer trace */ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) == 0) { ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: release trace diag buffer\n", ioc->name, __func__)); ++ mpt2sas_send_diag_release(ioc, MPI2_DIAG_BUF_TYPE_TRACE, ++ &issue_reset); ++ } ++ ++ _mpt2sas_raise_sigio(ioc, event_data); ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++} ++ ++/** ++ * mpt2sas_trigger_master - Master trigger handler ++ * @ioc: per adapter object ++ * @trigger_bitmask: ++ * ++ */ ++void ++mpt2sas_trigger_master(struct MPT3SAS_ADAPTER *ioc, u32 trigger_bitmask) ++{ ++ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data; ++ unsigned long flags; ++ u8 found_match = 0; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ ++ if (trigger_bitmask & MASTER_TRIGGER_FW_FAULT || ++ trigger_bitmask & MASTER_TRIGGER_ADAPTER_RESET) ++ goto by_pass_checks; ++ ++ /* check to see if trace buffers are currently registered */ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ /* check to see if trace buffers are currently released */ ++ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ by_pass_checks: ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enter - trigger_bitmask = 0x%08x\n", ++ ioc->name, __func__, trigger_bitmask)); ++ ++ /* don't send trigger if an trigger is currently active */ ++ if (ioc->diag_trigger_active) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ goto out; ++ } ++ ++ /* check for the trigger condition */ ++ if (ioc->diag_trigger_master.MasterData & trigger_bitmask) { ++ found_match = 1; ++ ioc->diag_trigger_active = 1; ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: setting diag_trigger_active flag\n", ++ ioc->name, __func__)); ++ } ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ ++ if (!found_match) ++ goto out; ++ ++ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T)); ++ event_data.trigger_type = MPT3SAS_TRIGGER_MASTER; ++ event_data.u.master.MasterData = trigger_bitmask; ++ ++ if (trigger_bitmask & MASTER_TRIGGER_FW_FAULT || ++ trigger_bitmask & MASTER_TRIGGER_ADAPTER_RESET) ++ _mpt2sas_raise_sigio(ioc, &event_data); ++ else ++ mpt2sas_send_trigger_data_event(ioc, &event_data); ++ ++ out: ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++} ++ ++/** ++ * mpt2sas_trigger_event - Event trigger handler ++ * @ioc: per adapter object ++ * @event: ++ * @log_entry_qualifier: ++ * ++ */ ++void ++mpt2sas_trigger_event(struct MPT3SAS_ADAPTER *ioc, u16 event, ++ u16 log_entry_qualifier) ++{ ++ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data; ++ struct SL_WH_EVENT_TRIGGER_T *event_trigger; ++ int i; ++ unsigned long flags; ++ u8 found_match; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ ++ /* check to see if trace buffers are currently registered */ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ /* check to see if trace buffers are currently released */ ++ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enter - event = 0x%04x, log_entry_qualifier = 0x%04x\n", ++ ioc->name, __func__, event, log_entry_qualifier)); ++ ++ /* don't send trigger if an trigger is currently active */ ++ if (ioc->diag_trigger_active) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ goto out; ++ } ++ ++ /* check for the trigger condition */ ++ event_trigger = ioc->diag_trigger_event.EventTriggerEntry; ++ for (i = 0 , found_match = 0; i < ioc->diag_trigger_event.ValidEntries ++ && !found_match; i++, event_trigger++) { ++ if (event_trigger->EventValue != event) ++ continue; ++ if (event == MPI2_EVENT_LOG_ENTRY_ADDED) { ++ if (event_trigger->LogEntryQualifier == ++ log_entry_qualifier) ++ found_match = 1; ++ continue; ++ } ++ found_match = 1; ++ ioc->diag_trigger_active = 1; ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: setting diag_trigger_active flag\n", ++ ioc->name, __func__)); ++ } ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ ++ if (!found_match) ++ goto out; ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: setting diag_trigger_active flag\n", ++ ioc->name, __func__)); ++ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T)); ++ event_data.trigger_type = MPT3SAS_TRIGGER_EVENT; ++ event_data.u.event.EventValue = event; ++ event_data.u.event.LogEntryQualifier = log_entry_qualifier; ++ mpt2sas_send_trigger_data_event(ioc, &event_data); ++ out: ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++} ++ ++/** ++ * mpt2sas_trigger_scsi - SCSI trigger handler ++ * @ioc: per adapter object ++ * @sense_key: ++ * @asc: ++ * @ascq: ++ * ++ */ ++void ++mpt2sas_trigger_scsi(struct MPT3SAS_ADAPTER *ioc, u8 sense_key, u8 asc, ++ u8 ascq) ++{ ++ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data; ++ struct SL_WH_SCSI_TRIGGER_T *scsi_trigger; ++ int i; ++ unsigned long flags; ++ u8 found_match; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ ++ /* check to see if trace buffers are currently registered */ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ /* check to see if trace buffers are currently released */ ++ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enter - sense_key = 0x%02x, asc = 0x%02x, ascq = 0x%02x\n", ++ ioc->name, __func__, sense_key, asc, ascq)); ++ ++ /* don't send trigger if an trigger is currently active */ ++ if (ioc->diag_trigger_active) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ goto out; ++ } ++ ++ /* check for the trigger condition */ ++ scsi_trigger = ioc->diag_trigger_scsi.SCSITriggerEntry; ++ for (i = 0 , found_match = 0; i < ioc->diag_trigger_scsi.ValidEntries ++ && !found_match; i++, scsi_trigger++) { ++ if (scsi_trigger->SenseKey != sense_key) ++ continue; ++ if (!(scsi_trigger->ASC == 0xFF || scsi_trigger->ASC == asc)) ++ continue; ++ if (!(scsi_trigger->ASCQ == 0xFF || scsi_trigger->ASCQ == ascq)) ++ continue; ++ found_match = 1; ++ ioc->diag_trigger_active = 1; ++ } ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ ++ if (!found_match) ++ goto out; ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: setting diag_trigger_active flag\n", ++ ioc->name, __func__)); ++ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T)); ++ event_data.trigger_type = MPT3SAS_TRIGGER_SCSI; ++ event_data.u.scsi.SenseKey = sense_key; ++ event_data.u.scsi.ASC = asc; ++ event_data.u.scsi.ASCQ = ascq; ++ mpt2sas_send_trigger_data_event(ioc, &event_data); ++ out: ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++} ++ ++/** ++ * mpt2sas_trigger_mpi - MPI trigger handler ++ * @ioc: per adapter object ++ * @ioc_status: ++ * @loginfo: ++ * ++ */ ++void ++mpt2sas_trigger_mpi(struct MPT3SAS_ADAPTER *ioc, u16 ioc_status, u32 loginfo) ++{ ++ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data; ++ struct SL_WH_MPI_TRIGGER_T *mpi_trigger; ++ int i; ++ unsigned long flags; ++ u8 found_match; ++ ++ spin_lock_irqsave(&ioc->diag_trigger_lock, flags); ++ ++ /* check to see if trace buffers are currently registered */ ++ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ /* check to see if trace buffers are currently released */ ++ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & ++ MPT3_DIAG_BUFFER_IS_RELEASED) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ return; ++ } ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: enter - ioc_status = 0x%04x, loginfo = 0x%08x\n", ++ ioc->name, __func__, ioc_status, loginfo)); ++ ++ /* don't send trigger if an trigger is currently active */ ++ if (ioc->diag_trigger_active) { ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ goto out; ++ } ++ ++ /* check for the trigger condition */ ++ mpi_trigger = ioc->diag_trigger_mpi.MPITriggerEntry; ++ for (i = 0 , found_match = 0; i < ioc->diag_trigger_mpi.ValidEntries ++ && !found_match; i++, mpi_trigger++) { ++ if (mpi_trigger->IOCStatus != ioc_status) ++ continue; ++ if (!(mpi_trigger->IocLogInfo == 0xFFFFFFFF || ++ mpi_trigger->IocLogInfo == loginfo)) ++ continue; ++ found_match = 1; ++ ioc->diag_trigger_active = 1; ++ } ++ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); ++ ++ if (!found_match) ++ goto out; ++ ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT ++ "%s: setting diag_trigger_active flag\n", ++ ioc->name, __func__)); ++ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T)); ++ event_data.trigger_type = MPT3SAS_TRIGGER_MPI; ++ event_data.u.mpi.IOCStatus = ioc_status; ++ event_data.u.mpi.IocLogInfo = loginfo; ++ mpt2sas_send_trigger_data_event(ioc, &event_data); ++ out: ++ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name, ++ __func__)); ++} +diff --git a/drivers/scsi/mpt2sas/mpt3sas_trigger_diag.h b/drivers/scsi/mpt2sas/mpt3sas_trigger_diag.h +new file mode 100644 +index 0000000..6586a46 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_trigger_diag.h +@@ -0,0 +1,194 @@ ++/* ++ * This is the Fusion MPT base driver providing common API layer interface ++ * to set Diagnostic triggers for MPT (Message Passing Technology) based ++ * controllers ++ * ++ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.h ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2014 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ /* Diagnostic Trigger Configuration Data Structures */ ++ ++#ifndef MPT3SAS_TRIGGER_DIAG_H_INCLUDED ++#define MPT3SAS_TRIGGER_DIAG_H_INCLUDED ++ ++/* limitation on number of entries */ ++#define NUM_VALID_ENTRIES (20) ++ ++/* trigger types */ ++#define MPT3SAS_TRIGGER_MASTER (1) ++#define MPT3SAS_TRIGGER_EVENT (2) ++#define MPT3SAS_TRIGGER_SCSI (3) ++#define MPT3SAS_TRIGGER_MPI (4) ++ ++/* trigger names */ ++#define MASTER_TRIGGER_FILE_NAME "diag_trigger_master" ++#define EVENT_TRIGGERS_FILE_NAME "diag_trigger_event" ++#define SCSI_TRIGGERS_FILE_NAME "diag_trigger_scsi" ++#define MPI_TRIGGER_FILE_NAME "diag_trigger_mpi" ++ ++/* master trigger bitmask */ ++#define MASTER_TRIGGER_FW_FAULT (0x00000001) ++#define MASTER_TRIGGER_ADAPTER_RESET (0x00000002) ++#define MASTER_TRIGGER_TASK_MANAGMENT (0x00000004) ++#define MASTER_TRIGGER_DEVICE_REMOVAL (0x00000008) ++ ++/* fake firmware event for tigger */ ++#define MPI3_EVENT_DIAGNOSTIC_TRIGGER_FIRED (0x6E) ++ ++/** ++ * MasterTrigger is a single U32 passed to/from sysfs. ++ * ++ * Bit Flags (enables) include: ++ * 1. FW Faults ++ * 2. Adapter Reset issued by driver ++ * 3. TMs ++ * 4. Device Remove Event sent by FW ++ */ ++ ++struct SL_WH_MASTER_TRIGGER_T { ++ uint32_t MasterData; ++}; ++ ++/** ++ * struct SL_WH_EVENT_TRIGGER_T - Definition of an event trigger element ++ * @EventValue: Event Code to trigger on ++ * @LogEntryQualifier: Type of FW event that logged (Log Entry Added Event only) ++ * ++ * Defines an event that should induce a DIAG_TRIGGER driver event if observed. ++ */ ++struct SL_WH_EVENT_TRIGGER_T { ++ uint16_t EventValue; ++ uint16_t LogEntryQualifier; ++}; ++ ++/** ++ * struct SL_WH_EVENT_TRIGGERS_T - Structure passed to/from sysfs containing a ++ * list of Event Triggers to be monitored for. ++ * @ValidEntries: Number of _SL_WH_EVENT_TRIGGER_T structures contained in this ++ * structure. ++ * @EventTriggerEntry: List of Event trigger elements. ++ * ++ * This binary structure is transferred via sysfs to get/set Event Triggers ++ * in the Linux Driver. ++ */ ++ ++struct SL_WH_EVENT_TRIGGERS_T { ++ uint32_t ValidEntries; ++ struct SL_WH_EVENT_TRIGGER_T EventTriggerEntry[NUM_VALID_ENTRIES]; ++}; ++ ++/** ++ * struct SL_WH_SCSI_TRIGGER_T - Definition of a SCSI trigger element ++ * @ASCQ: Additional Sense Code Qualifier. Can be specific or 0xFF for ++ * wildcard. ++ * @ASC: Additional Sense Code. Can be specific or 0xFF for wildcard ++ * @SenseKey: SCSI Sense Key ++ * ++ * Defines a sense key (single or many variants) that should induce a ++ * DIAG_TRIGGER driver event if observed. ++ */ ++struct SL_WH_SCSI_TRIGGER_T { ++ U8 ASCQ; ++ U8 ASC; ++ U8 SenseKey; ++ U8 Reserved; ++}; ++ ++/** ++ * struct SL_WH_SCSI_TRIGGERS_T - Structure passed to/from sysfs containing a ++ * list of SCSI sense codes that should trigger a DIAG_SERVICE event when ++ * observed. ++ * @ValidEntries: Number of _SL_WH_SCSI_TRIGGER_T structures contained in this ++ * structure. ++ * @SCSITriggerEntry: List of SCSI Sense Code trigger elements. ++ * ++ * This binary structure is transferred via sysfs to get/set SCSI Sense Code ++ * Triggers in the Linux Driver. ++ */ ++struct SL_WH_SCSI_TRIGGERS_T { ++ uint32_t ValidEntries; ++ struct SL_WH_SCSI_TRIGGER_T SCSITriggerEntry[NUM_VALID_ENTRIES]; ++}; ++ ++/** ++ * struct SL_WH_MPI_TRIGGER_T - Definition of an MPI trigger element ++ * @IOCStatus: MPI IOCStatus ++ * @IocLogInfo: MPI IocLogInfo. Can be specific or 0xFFFFFFFF for wildcard ++ * ++ * Defines a MPI IOCStatus/IocLogInfo pair that should induce a DIAG_TRIGGER ++ * driver event if observed. ++ */ ++struct SL_WH_MPI_TRIGGER_T { ++ uint16_t IOCStatus; ++ uint16_t Reserved; ++ uint32_t IocLogInfo; ++}; ++ ++/** ++ * struct SL_WH_MPI_TRIGGERS_T - Structure passed to/from sysfs containing a ++ * list of MPI IOCStatus/IocLogInfo pairs that should trigger a DIAG_SERVICE ++ * event when observed. ++ * @ValidEntries: Number of _SL_WH_MPI_TRIGGER_T structures contained in this ++ * structure. ++ * @MPITriggerEntry: List of MPI IOCStatus/IocLogInfo trigger elements. ++ * ++ * This binary structure is transferred via sysfs to get/set MPI Error Triggers ++ * in the Linux Driver. ++ */ ++struct SL_WH_MPI_TRIGGERS_T { ++ uint32_t ValidEntries; ++ struct SL_WH_MPI_TRIGGER_T MPITriggerEntry[NUM_VALID_ENTRIES]; ++}; ++ ++/** ++ * struct SL_WH_TRIGGERS_EVENT_DATA_T - event data for trigger ++ * @trigger_type: trigger type (see MPT3SAS_TRIGGER_XXXX) ++ * @u: trigger condition that caused trigger to be sent ++ */ ++struct SL_WH_TRIGGERS_EVENT_DATA_T { ++ uint32_t trigger_type; ++ union { ++ struct SL_WH_MASTER_TRIGGER_T master; ++ struct SL_WH_EVENT_TRIGGER_T event; ++ struct SL_WH_SCSI_TRIGGER_T scsi; ++ struct SL_WH_MPI_TRIGGER_T mpi; ++ } u; ++}; ++#endif /* MPT3SAS_TRIGGER_DIAG_H_INCLUDED */ +diff --git a/drivers/scsi/mpt2sas/mpt3sas_warpdrive.c b/drivers/scsi/mpt2sas/mpt3sas_warpdrive.c +new file mode 100644 +index 0000000..2542263 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/mpt3sas_warpdrive.c +@@ -0,0 +1,344 @@ ++/* ++ * Scsi Host Layer for MPT (Message Passing Technology) based controllers ++ * ++ * Copyright (C) 2012-2014 LSI Corporation ++ * Copyright (C) 2013-2015 Avago Technologies ++ * (mailto: MPT-FusionLinux.pdl@avagotech.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * NO WARRANTY ++ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ * solely responsible for determining the appropriateness of using and ++ * distributing the Program and assumes all risks associated with its ++ * exercise of rights under this Agreement, including but not limited to ++ * the risks and costs of program errors, damage to or loss of data, ++ * programs or equipment, and unavailability or interruption of operations. ++ ++ * DISCLAIMER OF LIABILITY ++ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program. ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include "mpt3sas_base.h" ++ ++/** ++ * _warpdrive_disable_ddio - Disable direct I/O for all the volumes ++ * @ioc: per adapter object ++ */ ++static void ++_warpdrive_disable_ddio(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2RaidVolPage1_t vol_pg1; ++ Mpi2ConfigReply_t mpi_reply; ++ struct _raid_device *raid_device; ++ u16 handle; ++ u16 ioc_status; ++ unsigned long flags; ++ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, ++ &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) ++ break; ++ handle = le16_to_cpu(vol_pg1.DevHandle); ++ spin_lock_irqsave(&ioc->raid_device_lock, flags); ++ raid_device = mpt2sas_raid_device_find_by_handle(ioc, handle); ++ if (raid_device) ++ raid_device->direct_io_enabled = 0; ++ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); ++ } ++ return; ++} ++ ++ ++/** ++ * mpt2sas_get_num_volumes - Get number of volumes in the ioc ++ * @ioc: per adapter object ++ */ ++u8 ++mpt2sas_get_num_volumes(struct MPT3SAS_ADAPTER *ioc) ++{ ++ Mpi2RaidVolPage1_t vol_pg1; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 handle; ++ u8 vol_cnt = 0; ++ u16 ioc_status; ++ ++ handle = 0xFFFF; ++ while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, ++ &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { ++ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & ++ MPI2_IOCSTATUS_MASK; ++ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) ++ break; ++ vol_cnt++; ++ handle = le16_to_cpu(vol_pg1.DevHandle); ++ } ++ return vol_cnt; ++} ++ ++ ++/** ++ * mpt2sas_init_warpdrive_properties - Set properties for warpdrive direct I/O. ++ * @ioc: per adapter object ++ * @raid_device: the raid_device object ++ */ ++void ++mpt2sas_init_warpdrive_properties(struct MPT3SAS_ADAPTER *ioc, ++ struct _raid_device *raid_device) ++{ ++ Mpi2RaidVolPage0_t *vol_pg0; ++ Mpi2RaidPhysDiskPage0_t pd_pg0; ++ Mpi2ConfigReply_t mpi_reply; ++ u16 sz; ++ u8 num_pds, count; ++ unsigned long stripe_sz, block_sz; ++ u8 stripe_exp, block_exp; ++ u64 dev_max_lba; ++ ++ if (!ioc->is_warpdrive) ++ return; ++ ++ if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "globally as drives are exposed\n", ioc->name); ++ return; ++ } ++ if (mpt2sas_get_num_volumes(ioc) > 1) { ++ _warpdrive_disable_ddio(ioc); ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "globally as number of drives > 1\n", ioc->name); ++ return; ++ } ++ if ((mpt2sas_config_get_number_pds(ioc, raid_device->handle, ++ &num_pds)) || !num_pds) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "Failure in computing number of drives\n", ioc->name); ++ return; ++ } ++ ++ sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds * ++ sizeof(Mpi2RaidVol0PhysDisk_t)); ++ vol_pg0 = kzalloc(sz, GFP_KERNEL); ++ if (!vol_pg0) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "Memory allocation failure for RVPG0\n", ioc->name); ++ return; ++ } ++ ++ if ((mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0, ++ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "Failure in retrieving RVPG0\n", ioc->name); ++ kfree(vol_pg0); ++ return; ++ } ++ ++ /* ++ * WARPDRIVE:If number of physical disks in a volume exceeds the max pds ++ * assumed for WARPDRIVE, disable direct I/O ++ */ ++ if (num_pds > MPT_MAX_WARPDRIVE_PDS) { ++ pr_warn(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "for the drive with handle(0x%04x): num_mem=%d, " ++ "max_mem_allowed=%d\n", ioc->name, raid_device->handle, ++ num_pds, MPT_MAX_WARPDRIVE_PDS); ++ kfree(vol_pg0); ++ return; ++ } ++ for (count = 0; count < num_pds; count++) { ++ if (mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, ++ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM, ++ vol_pg0->PhysDisk[count].PhysDiskNum) || ++ pd_pg0.DevHandle == MPT3SAS_INVALID_DEVICE_HANDLE) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is " ++ "disabled for the drive with handle(0x%04x) member" ++ "handle retrieval failed for member number=%d\n", ++ ioc->name, raid_device->handle, ++ vol_pg0->PhysDisk[count].PhysDiskNum); ++ goto out_error; ++ } ++ /* Disable direct I/O if member drive lba exceeds 4 bytes */ ++ dev_max_lba = le64_to_cpu(pd_pg0.DeviceMaxLBA); ++ if (dev_max_lba >> 32) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is " ++ "disabled for the drive with handle(0x%04x) member" ++ " handle (0x%04x) unsupported max lba 0x%016llx\n", ++ ioc->name, raid_device->handle, ++ le16_to_cpu(pd_pg0.DevHandle), ++ (unsigned long long)dev_max_lba); ++ goto out_error; ++ } ++ ++ raid_device->pd_handle[count] = le16_to_cpu(pd_pg0.DevHandle); ++ } ++ ++ /* ++ * Assumption for WD: Direct I/O is not supported if the volume is ++ * not RAID0 ++ */ ++ if (raid_device->volume_type != MPI2_RAID_VOL_TYPE_RAID0) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "for the drive with handle(0x%04x): type=%d, " ++ "s_sz=%uK, blk_size=%u\n", ioc->name, ++ raid_device->handle, raid_device->volume_type, ++ (le32_to_cpu(vol_pg0->StripeSize) * ++ le16_to_cpu(vol_pg0->BlockSize)) / 1024, ++ le16_to_cpu(vol_pg0->BlockSize)); ++ goto out_error; ++ } ++ ++ stripe_sz = le32_to_cpu(vol_pg0->StripeSize); ++ stripe_exp = find_first_bit(&stripe_sz, 32); ++ if (stripe_exp == 32) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "for the drive with handle(0x%04x) invalid stripe sz %uK\n", ++ ioc->name, raid_device->handle, ++ (le32_to_cpu(vol_pg0->StripeSize) * ++ le16_to_cpu(vol_pg0->BlockSize)) / 1024); ++ goto out_error; ++ } ++ raid_device->stripe_exponent = stripe_exp; ++ block_sz = le16_to_cpu(vol_pg0->BlockSize); ++ block_exp = find_first_bit(&block_sz, 16); ++ if (block_exp == 16) { ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " ++ "for the drive with handle(0x%04x) invalid block sz %u\n", ++ ioc->name, raid_device->handle, ++ le16_to_cpu(vol_pg0->BlockSize)); ++ goto out_error; ++ } ++ raid_device->block_exponent = block_exp; ++ raid_device->direct_io_enabled = 1; ++ ++ pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is Enabled for the drive" ++ " with handle(0x%04x)\n", ioc->name, raid_device->handle); ++ /* ++ * WARPDRIVE: Though the following fields are not used for direct IO, ++ * stored for future purpose: ++ */ ++ raid_device->max_lba = le64_to_cpu(vol_pg0->MaxLBA); ++ raid_device->stripe_sz = le32_to_cpu(vol_pg0->StripeSize); ++ raid_device->block_sz = le16_to_cpu(vol_pg0->BlockSize); ++ ++ ++ kfree(vol_pg0); ++ return; ++ ++out_error: ++ raid_device->direct_io_enabled = 0; ++ for (count = 0; count < num_pds; count++) ++ raid_device->pd_handle[count] = 0; ++ kfree(vol_pg0); ++ return; ++} ++ ++/** ++ * mpt2sas_scsi_direct_io_get - returns direct io flag ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * ++ * Returns the smid stored scmd pointer. ++ */ ++inline u8 ++mpt2sas_scsi_direct_io_get(struct MPT3SAS_ADAPTER *ioc, u16 smid) ++{ ++ return ioc->scsi_lookup[smid - 1].direct_io; ++} ++ ++/** ++ * mpt2sas_scsi_direct_io_set - sets direct io flag ++ * @ioc: per adapter object ++ * @smid: system request message index ++ * @direct_io: Zero or non-zero value to set in the direct_io flag ++ * ++ * Returns Nothing. ++ */ ++inline void ++mpt2sas_scsi_direct_io_set(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 direct_io) ++{ ++ ioc->scsi_lookup[smid - 1].direct_io = direct_io; ++} ++ ++/** ++ * mpt2sas_setup_direct_io - setup MPI request for WARPDRIVE Direct I/O ++ * @ioc: per adapter object ++ * @scmd: pointer to scsi command object ++ * @raid_device: pointer to raid device data structure ++ * @mpi_request: pointer to the SCSI_IO reqest message frame ++ * @smid: system request message index ++ * ++ * Returns nothing ++ */ ++void ++mpt2sas_setup_direct_io(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, ++ struct _raid_device *raid_device, Mpi2SCSIIORequest_t *mpi_request, ++ u16 smid) ++{ ++ sector_t v_lba, p_lba, stripe_off, column, io_size; ++ u32 stripe_sz, stripe_exp; ++ u8 num_pds, cmd = scmd->cmnd[0]; ++ ++ if (cmd != READ_10 && cmd != WRITE_10 && ++ cmd != READ_16 && cmd != WRITE_16) ++ return; ++ ++ if (cmd == READ_10 || cmd == WRITE_10) ++ v_lba = get_unaligned_be32(&mpi_request->CDB.CDB32[2]); ++ else ++ v_lba = get_unaligned_be64(&mpi_request->CDB.CDB32[2]); ++ ++ io_size = scsi_bufflen(scmd) >> raid_device->block_exponent; ++ ++ if (v_lba + io_size - 1 > raid_device->max_lba) ++ return; ++ ++ stripe_sz = raid_device->stripe_sz; ++ stripe_exp = raid_device->stripe_exponent; ++ stripe_off = v_lba & (stripe_sz - 1); ++ ++ /* Return unless IO falls within a stripe */ ++ if (stripe_off + io_size > stripe_sz) ++ return; ++ ++ num_pds = raid_device->num_pds; ++ p_lba = v_lba >> stripe_exp; ++ column = sector_div(p_lba, num_pds); ++ p_lba = (p_lba << stripe_exp) + stripe_off; ++ mpi_request->DevHandle = cpu_to_le16(raid_device->pd_handle[column]); ++ ++ if (cmd == READ_10 || cmd == WRITE_10) ++ put_unaligned_be32(lower_32_bits(p_lba), ++ &mpi_request->CDB.CDB32[2]); ++ else ++ put_unaligned_be64(p_lba, &mpi_request->CDB.CDB32[2]); ++ ++ mpt2sas_scsi_direct_io_set(ioc, smid, 1); ++} +diff --git a/drivers/scsi/mpt2sas/wrapper_mpt3sas_scsih.c b/drivers/scsi/mpt2sas/wrapper_mpt3sas_scsih.c +new file mode 100644 +index 0000000..7852050 +--- /dev/null ++++ b/drivers/scsi/mpt2sas/wrapper_mpt3sas_scsih.c +@@ -0,0 +1,4 @@ ++#define MPT2SAS_SCSI ++/* This directive is used to create the mpt2sas driver from the mpt3sas sources */ ++ ++#include "mpt3sas_scsih.c" +diff --git a/drivers/scsi/mpt3sas/Kconfig b/drivers/scsi/mpt3sas/Kconfig +index 5743420..9aa67e2 100644 +--- a/drivers/scsi/mpt3sas/Kconfig ++++ b/drivers/scsi/mpt3sas/Kconfig +@@ -40,14 +40,6 @@ + # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + # USA. + +-config SCSI_MPT2SAS +- tristate "LSI MPT Fusion SAS 2.0 Device Driver" +- depends on PCI && SCSI +- select SCSI_SAS_ATTRS +- select RAID_ATTRS +- ---help--- +- This driver supports PCI-Express SAS 6Gb/s Host Adapters. +- + config SCSI_MPT3SAS + tristate "LSI MPT Fusion SAS 3.0 Device Driver" + depends on PCI && SCSI +@@ -56,18 +48,6 @@ config SCSI_MPT3SAS + ---help--- + This driver supports PCI-Express SAS 12Gb/s Host Adapters. + +-config SCSI_MPT2SAS_MAX_SGE +- int "LSI MPT Fusion SAS 2.0 Max number of SG Entries (16 - 256)" +- depends on PCI && SCSI && SCSI_MPT3SAS +- default "128" +- range 16 256 +- ---help--- +- This option allows you to specify the maximum number of scatter- +- gather entries per I/O. The driver default is 128, which matches +- MAX_PHYS_SEGMENTS in most kernels. However in SuSE kernels this +- can be 256. However, it may decreased down to 16. Decreasing this +- parameter will reduce memory requirements on a per controller instance. +- + config SCSI_MPT3SAS_MAX_SGE + int "LSI MPT Fusion SAS 3.0 Max number of SG Entries (16 - 256)" + depends on PCI && SCSI && SCSI_MPT3SAS +diff --git a/drivers/scsi/mpt3sas/Makefile b/drivers/scsi/mpt3sas/Makefile +index 0b90877..450b84b 100644 +--- a/drivers/scsi/mpt3sas/Makefile ++++ b/drivers/scsi/mpt3sas/Makefile +@@ -1,6 +1,5 @@ + # mpt2-3sas makefile + +-obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas.o + obj-$(CONFIG_SCSI_MPT3SAS) += mpt3sas.o + + obj-m += mpt3sas.o +@@ -12,11 +11,3 @@ mpt3sas-y += mpt3sas_base.o \ + mpt3sas_trigger_diag.o \ + mpt3sas_warpdrive.o + +-obj-m += mpt2sas.o +-mpt2sas-y += mpt3sas_base.o \ +- mpt3sas_config.o \ +- wrapper_mpt3sas_scsih.o \ +- mpt3sas_transport.o \ +- mpt3sas_ctl.o \ +- mpt3sas_trigger_diag.o \ +- mpt3sas_warpdrive.o +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/Fix-cacheinfo-compilation-issues-for-3.10.patch b/kernel-std/centos/patches/Fix-cacheinfo-compilation-issues-for-3.10.patch new file mode 100644 index 00000000..cbd98714 --- /dev/null +++ b/kernel-std/centos/patches/Fix-cacheinfo-compilation-issues-for-3.10.patch @@ -0,0 +1,114 @@ +From f49689ec7d42e30014e2aebd57bff050b187ef22 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Alex Kozyrev +Date: Wed, 19 Jul 2017 02:25:15 -0500 +Subject: [PATCH 17/26] Fix cacheinfo compilation issues for 3.10 + +Had to revert commit 7cc277b489b4fe91f42eb596b282879c2d13152e: +"Install the callbacks via the state machine and let the core invoke +the callbacks on the already online CPUs. No functional change." +There is no hotplug state machine in 3.10 kernel. +Also implemented cpumap_print_to_pagebuf() function in place. + +Signed-off-by: Jim Somerville +--- + drivers/base/cacheinfo.c | 65 ++++++++++++++++++++++++++++++++++++------------ + 1 file changed, 49 insertions(+), 16 deletions(-) + +diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c +index eb3af27..c924f7e 100644 +--- a/drivers/base/cacheinfo.c ++++ b/drivers/base/cacheinfo.c +@@ -383,7 +383,12 @@ static ssize_t shared_cpumap_show_func(struct device *dev, bool list, char *buf) + struct cacheinfo *this_leaf = dev_get_drvdata(dev); + const struct cpumask *mask = &this_leaf->shared_cpu_map; + +- return cpumap_print_to_pagebuf(list, buf, mask); ++ int len = list? ++ cpulist_scnprintf(buf, PAGE_SIZE-2, mask) : ++ cpumask_scnprintf(buf, PAGE_SIZE-2, mask); ++ buf[len++] = '\n'; ++ buf[len] = '\0'; ++ return len; + } + + static ssize_t shared_cpu_map_show(struct device *dev, +@@ -633,30 +638,58 @@ err: + return rc; + } + +-static int cacheinfo_cpu_online(unsigned int cpu) ++static void cache_remove_dev(unsigned int cpu) + { +- int rc = detect_cache_attributes(cpu); ++ if (!cpumask_test_cpu(cpu, &cache_dev_map)) ++ return; ++ cpumask_clear_cpu(cpu, &cache_dev_map); + +- if (rc) +- return rc; +- rc = cache_add_dev(cpu); +- if (rc) +- free_cache_attributes(cpu); +- return rc; ++ cpu_cache_sysfs_exit(cpu); + } + +-static int cacheinfo_cpu_pre_down(unsigned int cpu) ++static int cacheinfo_cpu_callback(struct notifier_block *nfb, ++ unsigned long action, void *hcpu) + { +- if (cpumask_test_and_clear_cpu(cpu, &cache_dev_map)) +- cpu_cache_sysfs_exit(cpu); ++ unsigned int cpu = (unsigned long)hcpu; ++ int rc = 0; + +- free_cache_attributes(cpu); +- return 0; ++ switch (action & ~CPU_TASKS_FROZEN) { ++ case CPU_ONLINE: ++ rc = detect_cache_attributes(cpu); ++ if (!rc) ++ rc = cache_add_dev(cpu); ++ break; ++ case CPU_DEAD: ++ cache_remove_dev(cpu); ++ if (per_cpu_cacheinfo(cpu)) ++ free_cache_attributes(cpu); ++ break; ++ } ++ return notifier_from_errno(rc); + } + + static int __init cacheinfo_sysfs_init(void) + { +- return cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "base/cacheinfo:online", +- cacheinfo_cpu_online, cacheinfo_cpu_pre_down); ++ int cpu, rc = 0; ++ ++ cpu_notifier_register_begin(); ++ ++ for_each_online_cpu(cpu) { ++ rc = detect_cache_attributes(cpu); ++ if (rc) ++ goto out; ++ rc = cache_add_dev(cpu); ++ if (rc) { ++ free_cache_attributes(cpu); ++ pr_err("error populating cacheinfo..cpu%d\n", cpu); ++ goto out; ++ } ++ } ++ __hotcpu_notifier(cacheinfo_cpu_callback, 0); ++ ++out: ++ cpu_notifier_register_done(); ++ return rc; + } ++ + device_initcall(cacheinfo_sysfs_init); +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/Fix-compile-issue-when-transparent-hugepages-are-off.patch b/kernel-std/centos/patches/Fix-compile-issue-when-transparent-hugepages-are-off.patch new file mode 100644 index 00000000..581ff9dc --- /dev/null +++ b/kernel-std/centos/patches/Fix-compile-issue-when-transparent-hugepages-are-off.patch @@ -0,0 +1,29 @@ +From b6ceef1c915827b50ce3f76da4dc47f3eb768b44 Mon Sep 17 00:00:00 2001 +Message-Id: +From: Jim Somerville +Date: Thu, 15 Dec 2016 14:27:48 -0500 +Subject: [PATCH 01/26] Fix compile issue when transparent hugepages are off + +Signed-off-by: Jim Somerville +--- + mm/swap.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/mm/swap.c b/mm/swap.c +index 0982a35..6dcf38c 100644 +--- a/mm/swap.c ++++ b/mm/swap.c +@@ -998,8 +998,10 @@ void release_pages(struct page **pages, int nr, bool cold) + if (!put_page_testzero(page)) + continue; + ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE + VM_BUG_ON_PAGE(check_mmu_gather && + trans_huge_mmu_gather_count(page), page); ++#endif + + if (PageLRU(page)) { + if (!was_thp) +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/Make-kernel-start-eth-devices-at-offset.patch b/kernel-std/centos/patches/Make-kernel-start-eth-devices-at-offset.patch new file mode 100644 index 00000000..068f0c85 --- /dev/null +++ b/kernel-std/centos/patches/Make-kernel-start-eth-devices-at-offset.patch @@ -0,0 +1,37 @@ +From 0e86f726ba6e46ee206ecc7e09ce049ed4145f6c Mon Sep 17 00:00:00 2001 +Message-Id: <0e86f726ba6e46ee206ecc7e09ce049ed4145f6c.1527544850.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Chris Friesen +Date: Thu, 12 May 2016 18:00:00 -0400 +Subject: [PATCH 07/26] Make kernel start eth devices at offset + +In order to avoid naming collisions, we want to make the kernel +start naming its "ethX" devices at eth1000 instead of eth0. This +will let us rename to a range starting at eth0. + +Signed-off-by: Jim Somerville +--- + net/core/dev.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/net/core/dev.c b/net/core/dev.c +index 92d6c59..238c90d 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -1090,6 +1090,12 @@ static int __dev_alloc_name(struct net *net, const char *name, char *buf) + set_bit(i, inuse); + } + ++ /* WRS extension, want kernel to start at eth1000 */ ++ if (strcmp(name, "eth%d") == 0) { ++ for (i=0; i < 1000; i++) ++ set_bit(i, inuse); ++ } ++ + i = find_first_zero_bit(inuse, max_netdevices); + free_page((unsigned long) inuse); + } +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/Notification-of-death-of-arbitrary-processes.patch b/kernel-std/centos/patches/Notification-of-death-of-arbitrary-processes.patch new file mode 100644 index 00000000..4077b59b --- /dev/null +++ b/kernel-std/centos/patches/Notification-of-death-of-arbitrary-processes.patch @@ -0,0 +1,539 @@ +From f18a1429e701642388614b47073f4425020deee6 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Chris Friesen +Date: Thu, 7 Apr 2016 11:16:19 -0600 +Subject: [PATCH 02/26] Notification of death of arbitrary processes + +Note: this commit was copied from Titanium Cloud Rel2 + +This exposes a new feature which may be called to request +notification when an arbitrary process changes state. The +caller specifies a pid, signal number, and event mask, and +when that pid dies, or is stopped, or anything else that +would normally cause a SIGCHLD, the kernel will send the +specified signal to the caller if the event is in the event +mask originally passed down. The siginfo_t struct will +contain the same information as would be included with SIGCHLD. + +This is exposed to userspace via the prctl() call with the +PR_DO_NOTIFY_TASK_STATE option. + +Signed-off-by: Jim Somerville +--- + include/linux/init_task.h | 9 ++ + include/linux/sched.h | 6 ++ + include/uapi/linux/prctl.h | 18 ++++ + init/Kconfig | 15 +++ + kernel/Makefile | 1 + + kernel/death_notify.c | 227 +++++++++++++++++++++++++++++++++++++++++++++ + kernel/death_notify.h | 45 +++++++++ + kernel/exit.c | 6 ++ + kernel/fork.c | 4 + + kernel/signal.c | 11 +++ + kernel/sys.c | 9 ++ + 11 files changed, 351 insertions(+) + create mode 100644 kernel/death_notify.c + create mode 100644 kernel/death_notify.h + +diff --git a/include/linux/init_task.h b/include/linux/init_task.h +index a05294b..cfb7197 100644 +--- a/include/linux/init_task.h ++++ b/include/linux/init_task.h +@@ -77,6 +77,14 @@ extern struct nsproxy init_nsproxy; + .signalfd_wqh = __WAIT_QUEUE_HEAD_INITIALIZER(sighand.signalfd_wqh), \ + } + ++#ifdef CONFIG_SIGEXIT ++#define INIT_SIGEXIT(tsk) \ ++ .notify = LIST_HEAD_INIT(tsk.notify), \ ++ .monitor = LIST_HEAD_INIT(tsk.monitor), ++#else ++#define INIT_SIGEXIT(tsk) ++#endif ++ + extern struct group_info init_groups; + + #define INIT_STRUCT_PID { \ +@@ -224,6 +232,7 @@ extern struct task_group root_task_group; + .alloc_lock = __SPIN_LOCK_UNLOCKED(tsk.alloc_lock), \ + .journal_info = NULL, \ + .cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), \ ++ INIT_SIGEXIT(tsk) \ + .pi_lock = __RAW_SPIN_LOCK_UNLOCKED(tsk.pi_lock), \ + .timer_slack_ns = 50000, /* 50 usec default slack */ \ + .pids = { \ +diff --git a/include/linux/sched.h b/include/linux/sched.h +index 0646138..3933e24 100644 +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -1631,6 +1631,12 @@ struct task_struct { + short il_next; + short pref_node_fork; + #endif ++#ifdef CONFIG_SIGEXIT ++ /* list of processes to notify on death */ ++ struct list_head notify; ++ /* list of outstanding monitor requests */ ++ struct list_head monitor; ++#endif + #ifdef CONFIG_NUMA_BALANCING + int numa_scan_seq; + unsigned int numa_scan_period; +diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h +index 8ddaa82..53d6392 100644 +--- a/include/uapi/linux/prctl.h ++++ b/include/uapi/linux/prctl.h +@@ -55,6 +55,24 @@ + #define PR_SET_NAME 15 /* Set process name */ + #define PR_GET_NAME 16 /* Get process name */ + ++#ifdef CONFIG_SIGEXIT ++#define PR_DO_NOTIFY_TASK_STATE 17 /* Set/get notification for task ++ state changes */ ++ ++/* This is the data structure for requestion process death ++ * (and other state change) information. Sig of -1 means ++ * query, sig of 0 means deregistration, positive sig means ++ * that you want to set it. sig and events are value-result ++ * and will be updated with the previous values on every ++ * successful call. ++ */ ++struct task_state_notify_info { ++ pid_t pid; ++ int sig; ++ unsigned int events; ++}; ++#endif ++ + /* Get/set process endian */ + #define PR_GET_ENDIAN 19 + #define PR_SET_ENDIAN 20 +diff --git a/init/Kconfig b/init/Kconfig +index 6ec689c..550cea4 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1538,6 +1538,21 @@ config VM_EVENT_COUNTERS + on EXPERT systems. /proc/vmstat will only show page counts + if VM event counters are disabled. + ++config SIGEXIT ++ bool "Notification of death of arbitrary processes" ++ default n ++ help ++ When enabled this exposes a new feature which may be called to request ++ notification when an arbitrary process changes state. The caller specifies ++ a pid, signal number, and event mask, and when that pid dies, or is ++ stopped, or anything else that would normally cause a SIGCHLD, the ++ kernel will send the specified signal to the caller if the event is in ++ the event mask originally passed down. The siginfo_t struct will ++ contain the same information as would be included with SIGCHLD. ++ ++ This is exposed to userspace via the prctl() ++ call with the PR_DO_NOTIFY_TASK_STATE option ++ + config SLUB_DEBUG + default y + bool "Enable SLUB debugging support" if EXPERT +diff --git a/kernel/Makefile b/kernel/Makefile +index 2fb90fa..44a82c1 100644 +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -112,6 +112,7 @@ obj-$(CONFIG_RING_BUFFER) += trace/ + obj-$(CONFIG_TRACEPOINTS) += trace/ + obj-$(CONFIG_IRQ_WORK) += irq_work.o + obj-$(CONFIG_CPU_PM) += cpu_pm.o ++obj-$(CONFIG_SIGEXIT) += death_notify.o + + obj-$(CONFIG_PERF_EVENTS) += events/ + +diff --git a/kernel/death_notify.c b/kernel/death_notify.c +new file mode 100644 +index 0000000..889b929 +--- /dev/null ++++ b/kernel/death_notify.c +@@ -0,0 +1,227 @@ ++/* ++ * kernel/death_notify.c, Process death notification support ++ * ++ * Copyright (c) 2006-2014 Wind River Systems, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "death_notify.h" ++ ++static void unlink_status_notifier(struct signotifier *n) ++{ ++ list_del(&n->monitor_list); ++ list_del(&n->notify_list); ++ kfree(n); ++} ++ ++static void handle_already_monitoring(struct signotifier *node, ++ struct task_state_notify_info *args, ++ struct task_state_notify_info *oldargs) ++{ ++ /* Store the old values */ ++ oldargs->sig = node->sig; ++ oldargs->events = node->events; ++ ++ /* We know that args->sig is 0 or a valid signal. */ ++ if (args->sig > 0) { ++ /* Update the new values */ ++ node->sig = args->sig; ++ node->events = args->events; ++ } else if (!args->sig) { ++ /* args->sig of 0 means to deregister */ ++ unlink_status_notifier(node); ++ } ++} ++ ++static void setup_new_node(struct task_struct *p, ++ struct signotifier *node, ++ struct task_state_notify_info *args) ++{ ++ node->notify_tsk = current; ++ node->sig = args->sig; ++ node->events = args->events; ++ ++ /* Add this node to the list of notification requests ++ * for the specified process. ++ */ ++ list_add_tail(&node->notify_list, &p->notify); ++ ++ /* Also add this node to the list of monitor requests ++ * for the current process. ++ */ ++ list_add_tail(&node->monitor_list, ¤t->monitor); ++} ++ ++ ++/* Returns 0 if arguments are valid, 1 if they are not. */ ++static int invalid_args(struct task_state_notify_info *args) ++{ ++ int ret = 1; ++ ++ if (args->pid <= 0) ++ goto out; ++ ++ /* Sig of -1 implies query, sig of 0 implies deregistration. ++ * Otherwise sig must be positive and within range. ++ */ ++ if ((args->sig < -1) || (args->sig > _NSIG)) ++ goto out; ++ ++ /* If positive sig, must have valid events. */ ++ if (args->sig > 0) { ++ if (!args->events || (args->events >= (1 << (NSIGCHLD+1)))) ++ goto out; ++ } ++ ++ ret = 0; ++out: ++ return ret; ++} ++ ++/* Notify those registered for process state updates via do_notify_task_state(). ++ * If "del" is nonzero, the process is dying and we want to free ++ * the nodes in the list as we go. ++ * ++ * Note: we only notify processes for events in which they have registered ++ * interest. ++ * ++ * Must be called holding a lock on tasklist_lock. ++ */ ++void do_notify_others(struct task_struct *tsk, struct siginfo *info) ++{ ++ struct signotifier *node; ++ unsigned int events; ++ ++ /* This method of generating the event bit must be ++ * matched in the userspace library. ++ */ ++ events = 1 << (info->si_code & 0xFF); ++ ++ list_for_each_entry(node, &tsk->notify, notify_list) { ++ if (events & node->events) { ++ info->si_signo = node->sig; ++ group_send_sig_info(node->sig, info, node->notify_tsk); ++ } ++ } ++} ++ ++void release_notify_others(struct task_struct *p) ++{ ++ struct signotifier *n, *t; ++ ++ /* Need to clean up any outstanding requests where we ++ * wanted to be notified when others died. ++ */ ++ list_for_each_entry_safe(n, t, &p->monitor, monitor_list) { ++ unlink_status_notifier(n); ++ } ++ ++ /* Also need to clean up any outstanding requests where others ++ * wanted to be notified when we died. ++ */ ++ list_for_each_entry_safe(n, t, &p->notify, notify_list) { ++ unlink_status_notifier(n); ++ } ++} ++ ++/* If the config is defined, then processes can call this routine ++ * to request notification when the specified task's state changes. ++ * On the death (or other state change) of the specified process, ++ * we will send them the specified signal if the event is listed ++ * in their event bitfield. ++ * ++ * A sig of 0 means that we want to deregister. ++ * ++ * The sig/events fields are value/result. On success we update them ++ * to reflect what they were before the call. ++ * ++ * Returns error code on error, on success we return 0. ++ */ ++int do_notify_task_state(unsigned long arg) ++{ ++ int err; ++ struct task_struct *p; ++ struct signotifier *node, *tmp; ++ struct task_state_notify_info args, oldargs; ++ ++ if (copy_from_user(&args, (struct task_state_notify_info __user *)arg, ++ sizeof(args))) ++ return -EFAULT; ++ oldargs.pid = args.pid; ++ ++ /* Validate the arguments passed in. */ ++ err = -EINVAL; ++ if (invalid_args(&args)) ++ goto out; ++ ++ /* We must hold a write lock on tasklist_lock to add the notification ++ * later on, and we need some lock on tasklist_lock for ++ * find_task_by_pid(), so may as well take the write lock now. ++ * Must use write_lock_irq(). ++ */ ++ qwrite_lock_irq(&tasklist_lock); ++ ++ err = -ESRCH; ++ p = find_task_by_vpid(args.pid); ++ if (!p) ++ goto unlock_out; ++ ++ /* Now we know pid exists, unlikely to fail. */ ++ err = 0; ++ ++ /* Check if we're already monitoring the specified pid. If so, update ++ * the monitoring parameters and return the old ones. ++ */ ++ list_for_each_entry(tmp, &p->notify, notify_list) { ++ if (tmp->notify_tsk == current) { ++ handle_already_monitoring(tmp, &args, &oldargs); ++ goto unlock_out; ++ } ++ } ++ ++ /* If we get here, we're not currently monitoring the process. */ ++ oldargs.sig = 0; ++ oldargs.events = 0; ++ ++ /* If we wanted to set up a new monitor, do it now. If we didn't ++ * manage to allocate memory for the new node, then we return ++ * an appropriate error. ++ */ ++ if (args.sig > 0) { ++ node = kmalloc(sizeof(*node), GFP_ATOMIC); ++ if (node) ++ setup_new_node(p, node, &args); ++ else ++ err = -ENOMEM; ++ } ++ ++unlock_out: ++ qwrite_unlock_irq(&tasklist_lock); ++ ++ /* Copy the old values back to caller. */ ++ if (copy_to_user((struct task_state_notify_info __user *)arg, ++ &oldargs, sizeof(oldargs))) ++ err = -EFAULT; ++ ++out: ++ return err; ++} +diff --git a/kernel/death_notify.h b/kernel/death_notify.h +new file mode 100644 +index 0000000..b2b8e8c +--- /dev/null ++++ b/kernel/death_notify.h +@@ -0,0 +1,45 @@ ++/* ++ * kernel/death_notify.h, Process death notification support ++ * ++ * Copyright (c) 2006-2014 Wind River Systems, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++#ifndef _KERNEL_DEATH_NOTIFY_H ++#define _KERNEL_DEATH_NOTIFY_H ++ ++#ifdef CONFIG_SIGEXIT ++ ++struct signotifier { ++ struct task_struct *notify_tsk; ++ struct list_head notify_list; ++ struct list_head monitor_list; ++ int sig; ++ unsigned int events; ++}; ++ ++extern int do_notify_task_state(unsigned long arg); ++extern void do_notify_others(struct task_struct *tsk, ++ struct siginfo *info); ++extern void release_notify_others(struct task_struct *p); ++ ++#else /* !CONFIG_SIGEXIT */ ++ ++static inline void do_notify_others(struct task_struct *tsk, ++ struct siginfo *info) {} ++static inline void release_notify_others(struct task_struct *p) {} ++ ++#endif /* CONFIG_SIGEXIT */ ++#endif +diff --git a/kernel/exit.c b/kernel/exit.c +index 1afa799..a2ea26b 100644 +--- a/kernel/exit.c ++++ b/kernel/exit.c +@@ -59,6 +59,9 @@ + #include + #include + #include ++#ifdef CONFIG_SIGEXIT ++#include "death_notify.h" ++#endif + + static void exit_mm(struct task_struct * tsk); + +@@ -184,6 +187,9 @@ repeat: + proc_flush_task(p); + + tasklist_write_lock_irq(); ++#ifdef CONFIG_SIGEXIT ++ release_notify_others(p); ++#endif + ptrace_release_task(p); + __exit_signal(p); + +diff --git a/kernel/fork.c b/kernel/fork.c +index 55e4ca4..e721eed 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -1471,6 +1471,10 @@ static struct task_struct *copy_process(unsigned long clone_flags, + p->sequential_io = 0; + p->sequential_io_avg = 0; + #endif ++#ifdef CONFIG_SIGEXIT ++ INIT_LIST_HEAD(&p->notify); ++ INIT_LIST_HEAD(&p->monitor); ++#endif + + /* Perform scheduler related setup. Assign this task to a CPU. */ + retval = sched_fork(clone_flags, p); +diff --git a/kernel/signal.c b/kernel/signal.c +index a5aa525..b9d138e 100644 +--- a/kernel/signal.c ++++ b/kernel/signal.c +@@ -46,6 +46,9 @@ + #include + #include + #include "audit.h" /* audit_signal_info() */ ++#ifdef CONFIG_SIGEXIT ++#include "death_notify.h" ++#endif + + /* + * SLAB caches for signal bits. +@@ -1740,6 +1743,10 @@ bool do_notify_parent(struct task_struct *tsk, int sig) + __wake_up_parent(tsk, tsk->parent); + spin_unlock_irqrestore(&psig->siglock, flags); + ++#ifdef CONFIG_SIGEXIT ++ do_notify_others(tsk, &info); ++#endif ++ + return autoreap; + } + +@@ -1811,6 +1818,10 @@ static void do_notify_parent_cldstop(struct task_struct *tsk, + */ + __wake_up_parent(tsk, parent); + spin_unlock_irqrestore(&sighand->siglock, flags); ++ ++#ifdef CONFIG_SIGEXIT ++ do_notify_others(tsk, &info); ++#endif + } + + static inline int may_ptrace_stop(void) +diff --git a/kernel/sys.c b/kernel/sys.c +index cb90b1c..c68f98e 100644 +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -64,6 +64,10 @@ + #include + #include + ++#ifdef CONFIG_SIGEXIT ++#include "death_notify.h" ++#endif ++ + #ifndef SET_UNALIGN_CTL + # define SET_UNALIGN_CTL(a,b) (-EINVAL) + #endif +@@ -2474,6 +2478,11 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, + else + error = PR_MCE_KILL_DEFAULT; + break; ++#ifdef CONFIG_SIGEXIT ++ case PR_DO_NOTIFY_TASK_STATE: ++ error = do_notify_task_state(arg2); ++ break; ++#endif + case PR_SET_MM: + error = prctl_set_mm(arg2, arg3, arg4, arg5); + break; +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/PCI-Add-ACS-quirk-for-Intel-Fortville-NICs.patch b/kernel-std/centos/patches/PCI-Add-ACS-quirk-for-Intel-Fortville-NICs.patch new file mode 100644 index 00000000..106be5d8 --- /dev/null +++ b/kernel-std/centos/patches/PCI-Add-ACS-quirk-for-Intel-Fortville-NICs.patch @@ -0,0 +1,34 @@ +From 98f7b9d926abbe0bb60ab0a14a306516fa36b9d8 Mon Sep 17 00:00:00 2001 +Message-Id: <98f7b9d926abbe0bb60ab0a14a306516fa36b9d8.1527544850.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Dahir Osman +Date: Wed, 13 Jan 2016 10:01:11 -0500 +Subject: [PATCH 04/26] PCI: Add ACS quirk for Intel Fortville NICs + +Use quirks to determine isolation for now until a later kernel can +properly read the Fortville ACS capabilities. + +Signed-off-by: Jim Somerville +--- + drivers/pci/quirks.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c +index 5614e3f..4a0bfed 100644 +--- a/drivers/pci/quirks.c ++++ b/drivers/pci/quirks.c +@@ -4289,6 +4289,10 @@ static const struct pci_dev_acs_enabled { + /* I219 */ + { PCI_VENDOR_ID_INTEL, 0x15b7, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x15b8, pci_quirk_mf_endpoint_acs }, ++ /* I40 */ ++ { PCI_VENDOR_ID_INTEL, 0x1572, pci_quirk_mf_endpoint_acs }, ++ { PCI_VENDOR_ID_INTEL, 0x1586, pci_quirk_mf_endpoint_acs }, ++ { PCI_VENDOR_ID_INTEL, 0x1583, pci_quirk_mf_endpoint_acs }, + /* Intel PCH root ports */ + { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs }, + { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_spt_pch_acs }, +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/Porting-Cacheinfo-from-Kernel-4.10.17.patch b/kernel-std/centos/patches/Porting-Cacheinfo-from-Kernel-4.10.17.patch new file mode 100644 index 00000000..f9694dcc --- /dev/null +++ b/kernel-std/centos/patches/Porting-Cacheinfo-from-Kernel-4.10.17.patch @@ -0,0 +1,2124 @@ +From 68c649b9bc60dd2b526e0d181edde358196d2e78 Mon Sep 17 00:00:00 2001 +Message-Id: <68c649b9bc60dd2b526e0d181edde358196d2e78.1527544850.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Alex Kozyrev +Date: Wed, 19 Jul 2017 02:21:59 -0500 +Subject: [PATCH 16/26] Porting Cacheinfo from Kernel 4.10.17 + +Original source code from tag v4.10.17 in Linux stable tree for: +intel_cacheinfo.c, cacheinfo.c and cacheinfo.h. +Main commit that we are interested for is 246246cbde5e840012f853e27630ebb59f409486: +This patch adds initial support for providing processor cache information +to userspace through sysfs interface. This is based on already existing +implementations(x86, ia64, s390 and powerpc) and hence the interface is +intended to be fully compatible. + +The main purpose of this generic support is to avoid further code +duplication to support new architectures and also to unify all the existing +different implementations. + +This implementation maintains the hierarchy of cache objects which reflects +the system's cache topology. Cache devices are instantiated as needed as +CPUs come online. The cache information is replicated per-cpu even if they are +shared. A per-cpu array of cache information maintained is used mainly for +sysfs-related book keeping. + +It also implements the shared_cpu_map attribute, which is essential for +enabling both kernel and user-space to discover the system's overall cache +topology. + +This patch also add the missing ABI documentation for the cacheinfo sysfs +interface already, which is well defined and widely used. + +sysfs-devices-system-cpu was nodified by taking commit 1d78dc59f5ab6f467e49882518453adc7e4caa44: +Add an ABI document entry for /sys/devices/system/cpu/cpu*/cache/index*/id. + +cpu.h and cpu.c was enhanced with commit 3d52943b3a51497a777e6d7d840a38596a92cee9: +This patch adds a new function to create per-cpu devices. +This helps in: +1. reusing the device infrastructure to create any cpu related + attributes and corresponding sysfs instead of creating and + dealing with raw kobjects directly +2. retaining the legacy path(/sys/devices/system/cpu/..) to support + existing sysfs ABI +3. avoiding to create links in the bus directory pointing to the + device as there would be per-cpu instance of these devices with + the same name since dev->bus is not populated to cpu_sysbus on + purpose + +Signed-off-by: Jim Somerville +--- + Documentation/ABI/testing/sysfs-devices-system-cpu | 65 ++ + arch/x86/kernel/cpu/intel_cacheinfo.c | 830 +++++++-------------- + drivers/base/Makefile | 2 +- + drivers/base/cacheinfo.c | 662 ++++++++++++++++ + drivers/base/cpu.c | 54 ++ + include/linux/cacheinfo.h | 104 +++ + include/linux/cpu.h | 3 + + 7 files changed, 1147 insertions(+), 573 deletions(-) + create mode 100644 drivers/base/cacheinfo.c + create mode 100644 include/linux/cacheinfo.h + +diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu +index 55338e6..eaf8da1 100644 +--- a/Documentation/ABI/testing/sysfs-devices-system-cpu ++++ b/Documentation/ABI/testing/sysfs-devices-system-cpu +@@ -201,6 +201,71 @@ Description: address and size of the percpu note. + + crash_notes_size: size of the note of cpu#. + ++ ++What: /sys/devices/system/cpu/cpu*/cache/index*/ ++Date: July 2014(documented, existed before August 2008) ++Contact: Sudeep Holla ++ Linux kernel mailing list ++Description: Parameters for the CPU cache attributes ++ ++ allocation_policy: ++ - WriteAllocate: allocate a memory location to a cache line ++ on a cache miss because of a write ++ - ReadAllocate: allocate a memory location to a cache line ++ on a cache miss because of a read ++ - ReadWriteAllocate: both writeallocate and readallocate ++ ++ attributes: LEGACY used only on IA64 and is same as write_policy ++ ++ coherency_line_size: the minimum amount of data in bytes that gets ++ transferred from memory to cache ++ ++ level: the cache hierarchy in the multi-level cache configuration ++ ++ number_of_sets: total number of sets in the cache, a set is a ++ collection of cache lines with the same cache index ++ ++ physical_line_partition: number of physical cache line per cache tag ++ ++ shared_cpu_list: the list of logical cpus sharing the cache ++ ++ shared_cpu_map: logical cpu mask containing the list of cpus sharing ++ the cache ++ ++ size: the total cache size in kB ++ ++ type: ++ - Instruction: cache that only holds instructions ++ - Data: cache that only caches data ++ - Unified: cache that holds both data and instructions ++ ++ ways_of_associativity: degree of freedom in placing a particular block ++ of memory in the cache ++ ++ write_policy: ++ - WriteThrough: data is written to both the cache line ++ and to the block in the lower-level memory ++ - WriteBack: data is written only to the cache line and ++ the modified cache line is written to main ++ memory only when it is replaced ++ ++ ++What: /sys/devices/system/cpu/cpu*/cache/index*/id ++Date: September 2016 ++Contact: Linux kernel mailing list ++Description: Cache id ++ ++ The id provides a unique number for a specific instance of ++ a cache of a particular type. E.g. there may be a level ++ 3 unified cache on each socket in a server and we may ++ assign them ids 0, 1, 2, ... ++ ++ Note that id value can be non-contiguous. E.g. level 1 ++ caches typically exist per core, but there may not be a ++ power of two cores on a socket, so these caches may be ++ numbered 0, 1, 2, 3, 4, 5, 8, 9, 10, ... ++ ++ + What: /sys/devices/system/cpu/cpuX/cpufreq/throttle_stats + /sys/devices/system/cpu/cpuX/cpufreq/throttle_stats/turbo_stat + /sys/devices/system/cpu/cpuX/cpufreq/throttle_stats/sub_turbo_stat +diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c +index d529019..bf23bd2 100644 +--- a/arch/x86/kernel/cpu/intel_cacheinfo.c ++++ b/arch/x86/kernel/cpu/intel_cacheinfo.c +@@ -1,5 +1,5 @@ + /* +- * Routines to indentify caches on Intel CPU. ++ * Routines to identify caches on Intel CPU. + * + * Changes: + * Venkatesh Pallipadi : Adding cache identification through cpuid(4) +@@ -7,16 +7,14 @@ + * Andi Kleen / Andreas Herrmann : CPUID4 emulation on AMD. + */ + +-#include + #include +-#include +-#include ++#include + #include + #include ++#include + #include + + #include +-#include + #include + #include + +@@ -116,10 +114,10 @@ static const struct _cache_table cache_table[] = + + + enum _cache_type { +- CACHE_TYPE_NULL = 0, +- CACHE_TYPE_DATA = 1, +- CACHE_TYPE_INST = 2, +- CACHE_TYPE_UNIFIED = 3 ++ CTYPE_NULL = 0, ++ CTYPE_DATA = 1, ++ CTYPE_INST = 2, ++ CTYPE_UNIFIED = 3 + }; + + union _cpuid4_leaf_eax { +@@ -160,12 +158,7 @@ struct _cpuid4_info_regs { + struct amd_northbridge *nb; + }; + +-struct _cpuid4_info { +- struct _cpuid4_info_regs base; +- DECLARE_BITMAP(shared_cpu_map, NR_CPUS); +-}; +- +-unsigned short num_cache_leaves; ++static unsigned short num_cache_leaves; + + /* AMD doesn't have CPUID4. Emulate it here to report the same + information to the user. This makes some assumptions about the machine: +@@ -221,6 +214,13 @@ static const unsigned short assocs[] = { + static const unsigned char levels[] = { 1, 1, 2, 3 }; + static const unsigned char types[] = { 1, 2, 3, 3 }; + ++static const enum cache_type cache_type_map[] = { ++ [CTYPE_NULL] = CACHE_TYPE_NOCACHE, ++ [CTYPE_DATA] = CACHE_TYPE_DATA, ++ [CTYPE_INST] = CACHE_TYPE_INST, ++ [CTYPE_UNIFIED] = CACHE_TYPE_UNIFIED, ++}; ++ + static void + amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax, + union _cpuid4_leaf_ebx *ebx, +@@ -292,14 +292,8 @@ amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax, + (ebx->split.ways_of_associativity + 1) - 1; + } + +-struct _cache_attr { +- struct attribute attr; +- ssize_t (*show)(struct _cpuid4_info *, char *, unsigned int); +- ssize_t (*store)(struct _cpuid4_info *, const char *, size_t count, +- unsigned int); +-}; +- + #if defined(CONFIG_AMD_NB) && defined(CONFIG_SYSFS) ++ + /* + * L3 cache descriptors + */ +@@ -326,20 +320,6 @@ static void amd_calc_l3_indices(struct amd_northbridge *nb) + l3->indices = (max(max3(sc0, sc1, sc2), sc3) << 10) - 1; + } + +-static void amd_init_l3_cache(struct _cpuid4_info_regs *this_leaf, int index) +-{ +- int node; +- +- /* only for L3, and not in virtualized environments */ +- if (index < 3) +- return; +- +- node = amd_get_nb_id(smp_processor_id()); +- this_leaf->nb = node_to_amd_nb(node); +- if (this_leaf->nb && !this_leaf->nb->l3_cache.indices) +- amd_calc_l3_indices(this_leaf->nb); +-} +- + /* + * check whether a slot used for disabling an L3 index is occupied. + * @l3: L3 cache descriptor +@@ -347,7 +327,7 @@ static void amd_init_l3_cache(struct _cpuid4_info_regs *this_leaf, int index) + * + * @returns: the disabled index if used or negative value if slot free. + */ +-int amd_get_l3_disable_slot(struct amd_northbridge *nb, unsigned slot) ++static int amd_get_l3_disable_slot(struct amd_northbridge *nb, unsigned slot) + { + unsigned int reg = 0; + +@@ -360,15 +340,13 @@ int amd_get_l3_disable_slot(struct amd_northbridge *nb, unsigned slot) + return -1; + } + +-static ssize_t show_cache_disable(struct _cpuid4_info *this_leaf, char *buf, ++static ssize_t show_cache_disable(struct cacheinfo *this_leaf, char *buf, + unsigned int slot) + { + int index; ++ struct amd_northbridge *nb = this_leaf->priv; + +- if (!this_leaf->base.nb || !amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) +- return -EINVAL; +- +- index = amd_get_l3_disable_slot(this_leaf->base.nb, slot); ++ index = amd_get_l3_disable_slot(nb, slot); + if (index >= 0) + return sprintf(buf, "%d\n", index); + +@@ -377,9 +355,10 @@ static ssize_t show_cache_disable(struct _cpuid4_info *this_leaf, char *buf, + + #define SHOW_CACHE_DISABLE(slot) \ + static ssize_t \ +-show_cache_disable_##slot(struct _cpuid4_info *this_leaf, char *buf, \ +- unsigned int cpu) \ ++cache_disable_##slot##_show(struct device *dev, \ ++ struct device_attribute *attr, char *buf) \ + { \ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); \ + return show_cache_disable(this_leaf, buf, slot); \ + } + SHOW_CACHE_DISABLE(0) +@@ -425,8 +404,8 @@ static void amd_l3_disable_index(struct amd_northbridge *nb, int cpu, + * + * @return: 0 on success, error status on failure + */ +-int amd_set_l3_disable_slot(struct amd_northbridge *nb, int cpu, unsigned slot, +- unsigned long index) ++static int amd_set_l3_disable_slot(struct amd_northbridge *nb, int cpu, ++ unsigned slot, unsigned long index) + { + int ret = 0; + +@@ -447,28 +426,26 @@ int amd_set_l3_disable_slot(struct amd_northbridge *nb, int cpu, unsigned slot, + return 0; + } + +-static ssize_t store_cache_disable(struct _cpuid4_info *this_leaf, +- const char *buf, size_t count, +- unsigned int slot) ++static ssize_t store_cache_disable(struct cacheinfo *this_leaf, ++ const char *buf, size_t count, ++ unsigned int slot) + { + unsigned long val = 0; + int cpu, err = 0; ++ struct amd_northbridge *nb = this_leaf->priv; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + +- if (!this_leaf->base.nb || !amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) +- return -EINVAL; +- +- cpu = cpumask_first(to_cpumask(this_leaf->shared_cpu_map)); ++ cpu = cpumask_first(&this_leaf->shared_cpu_map); + + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + +- err = amd_set_l3_disable_slot(this_leaf->base.nb, cpu, slot, val); ++ err = amd_set_l3_disable_slot(nb, cpu, slot, val); + if (err) { + if (err == -EEXIST) +- pr_warning("L3 slot %d in use/index already disabled!\n", ++ pr_warn("L3 slot %d in use/index already disabled!\n", + slot); + return err; + } +@@ -477,41 +454,36 @@ static ssize_t store_cache_disable(struct _cpuid4_info *this_leaf, + + #define STORE_CACHE_DISABLE(slot) \ + static ssize_t \ +-store_cache_disable_##slot(struct _cpuid4_info *this_leaf, \ +- const char *buf, size_t count, \ +- unsigned int cpu) \ ++cache_disable_##slot##_store(struct device *dev, \ ++ struct device_attribute *attr, \ ++ const char *buf, size_t count) \ + { \ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); \ + return store_cache_disable(this_leaf, buf, count, slot); \ + } + STORE_CACHE_DISABLE(0) + STORE_CACHE_DISABLE(1) + +-static struct _cache_attr cache_disable_0 = __ATTR(cache_disable_0, 0644, +- show_cache_disable_0, store_cache_disable_0); +-static struct _cache_attr cache_disable_1 = __ATTR(cache_disable_1, 0644, +- show_cache_disable_1, store_cache_disable_1); +- +-static ssize_t +-show_subcaches(struct _cpuid4_info *this_leaf, char *buf, unsigned int cpu) ++static ssize_t subcaches_show(struct device *dev, ++ struct device_attribute *attr, char *buf) + { +- if (!this_leaf->base.nb || !amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) +- return -EINVAL; ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ int cpu = cpumask_first(&this_leaf->shared_cpu_map); + + return sprintf(buf, "%x\n", amd_get_subcaches(cpu)); + } + +-static ssize_t +-store_subcaches(struct _cpuid4_info *this_leaf, const char *buf, size_t count, +- unsigned int cpu) ++static ssize_t subcaches_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) + { ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ int cpu = cpumask_first(&this_leaf->shared_cpu_map); + unsigned long val; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + +- if (!this_leaf->base.nb || !amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) +- return -EINVAL; +- + if (kstrtoul(buf, 16, &val) < 0) + return -EINVAL; + +@@ -521,9 +493,92 @@ store_subcaches(struct _cpuid4_info *this_leaf, const char *buf, size_t count, + return count; + } + +-static struct _cache_attr subcaches = +- __ATTR(subcaches, 0644, show_subcaches, store_subcaches); ++static DEVICE_ATTR_RW(cache_disable_0); ++static DEVICE_ATTR_RW(cache_disable_1); ++static DEVICE_ATTR_RW(subcaches); ++ ++static umode_t ++cache_private_attrs_is_visible(struct kobject *kobj, ++ struct attribute *attr, int unused) ++{ ++ struct device *dev = kobj_to_dev(kobj); ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ umode_t mode = attr->mode; ++ ++ if (!this_leaf->priv) ++ return 0; ++ ++ if ((attr == &dev_attr_subcaches.attr) && ++ amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) ++ return mode; ++ ++ if ((attr == &dev_attr_cache_disable_0.attr || ++ attr == &dev_attr_cache_disable_1.attr) && ++ amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) ++ return mode; ++ ++ return 0; ++} ++ ++static struct attribute_group cache_private_group = { ++ .is_visible = cache_private_attrs_is_visible, ++}; ++ ++static void init_amd_l3_attrs(void) ++{ ++ int n = 1; ++ static struct attribute **amd_l3_attrs; ++ ++ if (amd_l3_attrs) /* already initialized */ ++ return; ++ ++ if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) ++ n += 2; ++ if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) ++ n += 1; ++ ++ amd_l3_attrs = kcalloc(n, sizeof(*amd_l3_attrs), GFP_KERNEL); ++ if (!amd_l3_attrs) ++ return; ++ ++ n = 0; ++ if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) { ++ amd_l3_attrs[n++] = &dev_attr_cache_disable_0.attr; ++ amd_l3_attrs[n++] = &dev_attr_cache_disable_1.attr; ++ } ++ if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) ++ amd_l3_attrs[n++] = &dev_attr_subcaches.attr; ++ ++ cache_private_group.attrs = amd_l3_attrs; ++} ++ ++const struct attribute_group * ++cache_get_priv_group(struct cacheinfo *this_leaf) ++{ ++ struct amd_northbridge *nb = this_leaf->priv; ++ ++ if (this_leaf->level < 3 || !nb) ++ return NULL; ++ ++ if (nb && nb->l3_cache.indices) ++ init_amd_l3_attrs(); ++ ++ return &cache_private_group; ++} ++ ++static void amd_init_l3_cache(struct _cpuid4_info_regs *this_leaf, int index) ++{ ++ int node; ++ ++ /* only for L3, and not in virtualized environments */ ++ if (index < 3) ++ return; + ++ node = amd_get_nb_id(smp_processor_id()); ++ this_leaf->nb = node_to_amd_nb(node); ++ if (this_leaf->nb && !this_leaf->nb->l3_cache.indices) ++ amd_calc_l3_indices(this_leaf->nb); ++} + #else + #define amd_init_l3_cache(x, y) + #endif /* CONFIG_AMD_NB && CONFIG_SYSFS */ +@@ -537,7 +592,7 @@ cpuid4_cache_lookup_regs(int index, struct _cpuid4_info_regs *this_leaf) + unsigned edx; + + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) { +- if (cpu_has_topoext) ++ if (boot_cpu_has(X86_FEATURE_TOPOEXT)) + cpuid_count(0x8000001d, index, &eax.full, + &ebx.full, &ecx.full, &edx); + else +@@ -547,7 +602,7 @@ cpuid4_cache_lookup_regs(int index, struct _cpuid4_info_regs *this_leaf) + cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &edx); + } + +- if (eax.split.type == CACHE_TYPE_NULL) ++ if (eax.split.type == CTYPE_NULL) + return -EIO; /* better error ? */ + + this_leaf->eax = eax; +@@ -576,14 +631,14 @@ static int find_num_cache_leaves(struct cpuinfo_x86 *c) + /* Do cpuid(op) loop to find out num_cache_leaves */ + cpuid_count(op, i, &eax, &ebx, &ecx, &edx); + cache_eax.full = eax; +- } while (cache_eax.split.type != CACHE_TYPE_NULL); ++ } while (cache_eax.split.type != CTYPE_NULL); + return i; + } + + void init_amd_cacheinfo(struct cpuinfo_x86 *c) + { + +- if (cpu_has_topoext) { ++ if (boot_cpu_has(X86_FEATURE_TOPOEXT)) { + num_cache_leaves = find_num_cache_leaves(c); + } else if (c->extended_cpuid_level >= 0x80000006) { + if (cpuid_edx(0x80000006) & 0xf000) +@@ -600,7 +655,7 @@ unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c) + unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */ + unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */ + unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb; +-#ifdef CONFIG_X86_HT ++#ifdef CONFIG_SMP + unsigned int cpu = c->cpu_index; + #endif + +@@ -618,36 +673,34 @@ unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c) + * parameters cpuid leaf to find the cache details + */ + for (i = 0; i < num_cache_leaves; i++) { +- struct _cpuid4_info_regs this_leaf; ++ struct _cpuid4_info_regs this_leaf = {}; + int retval; + + retval = cpuid4_cache_lookup_regs(i, &this_leaf); +- if (retval >= 0) { +- switch (this_leaf.eax.split.level) { +- case 1: +- if (this_leaf.eax.split.type == +- CACHE_TYPE_DATA) +- new_l1d = this_leaf.size/1024; +- else if (this_leaf.eax.split.type == +- CACHE_TYPE_INST) +- new_l1i = this_leaf.size/1024; +- break; +- case 2: +- new_l2 = this_leaf.size/1024; +- num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing; +- index_msb = get_count_order(num_threads_sharing); +- l2_id = c->apicid & ~((1 << index_msb) - 1); +- break; +- case 3: +- new_l3 = this_leaf.size/1024; +- num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing; +- index_msb = get_count_order( +- num_threads_sharing); +- l3_id = c->apicid & ~((1 << index_msb) - 1); +- break; +- default: +- break; +- } ++ if (retval < 0) ++ continue; ++ ++ switch (this_leaf.eax.split.level) { ++ case 1: ++ if (this_leaf.eax.split.type == CTYPE_DATA) ++ new_l1d = this_leaf.size/1024; ++ else if (this_leaf.eax.split.type == CTYPE_INST) ++ new_l1i = this_leaf.size/1024; ++ break; ++ case 2: ++ new_l2 = this_leaf.size/1024; ++ num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing; ++ index_msb = get_count_order(num_threads_sharing); ++ l2_id = c->apicid & ~((1 << index_msb) - 1); ++ break; ++ case 3: ++ new_l3 = this_leaf.size/1024; ++ num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing; ++ index_msb = get_count_order(num_threads_sharing); ++ l3_id = c->apicid & ~((1 << index_msb) - 1); ++ break; ++ default: ++ break; + } + } + } +@@ -721,34 +774,40 @@ unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c) + + if (new_l2) { + l2 = new_l2; +-#ifdef CONFIG_X86_HT ++#ifdef CONFIG_SMP + per_cpu(cpu_llc_id, cpu) = l2_id; + #endif + } + + if (new_l3) { + l3 = new_l3; +-#ifdef CONFIG_X86_HT ++#ifdef CONFIG_SMP + per_cpu(cpu_llc_id, cpu) = l3_id; + #endif + } + ++#ifdef CONFIG_SMP ++ /* ++ * If cpu_llc_id is not yet set, this means cpuid_level < 4 which in ++ * turns means that the only possibility is SMT (as indicated in ++ * cpuid1). Since cpuid2 doesn't specify shared caches, and we know ++ * that SMT shares all caches, we can unconditionally set cpu_llc_id to ++ * c->phys_proc_id. ++ */ ++ if (per_cpu(cpu_llc_id, cpu) == BAD_APICID) ++ per_cpu(cpu_llc_id, cpu) = c->phys_proc_id; ++#endif ++ + c->x86_cache_size = l3 ? l3 : (l2 ? l2 : (l1i+l1d)); + + return l2; + } + +-#ifdef CONFIG_SYSFS +- +-/* pointer to _cpuid4_info array (for each cache leaf) */ +-static DEFINE_PER_CPU(struct _cpuid4_info *, ici_cpuid4_info); +-#define CPUID4_INFO_IDX(x, y) (&((per_cpu(ici_cpuid4_info, x))[y])) +- +-#ifdef CONFIG_SMP +- +-static int cache_shared_amd_cpu_map_setup(unsigned int cpu, int index) ++static int __cache_amd_cpumap_setup(unsigned int cpu, int index, ++ struct _cpuid4_info_regs *base) + { +- struct _cpuid4_info *this_leaf; ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ struct cacheinfo *this_leaf; + int i, sibling; + + /* +@@ -757,40 +816,43 @@ static int cache_shared_amd_cpu_map_setup(unsigned int cpu, int index) + */ + if (index == 3) { + for_each_cpu(i, cpu_llc_shared_mask(cpu)) { +- if (!per_cpu(ici_cpuid4_info, i)) ++ this_cpu_ci = get_cpu_cacheinfo(i); ++ if (!this_cpu_ci->info_list) + continue; +- this_leaf = CPUID4_INFO_IDX(i, index); ++ this_leaf = this_cpu_ci->info_list + index; + for_each_cpu(sibling, cpu_llc_shared_mask(cpu)) { + if (!cpu_online(sibling)) + continue; +- set_bit(sibling, this_leaf->shared_cpu_map); ++ cpumask_set_cpu(sibling, ++ &this_leaf->shared_cpu_map); + } + } +- } else if (cpu_has_topoext) { ++ } else if (boot_cpu_has(X86_FEATURE_TOPOEXT)) { + unsigned int apicid, nshared, first, last; + +- if (!per_cpu(ici_cpuid4_info, cpu)) +- return 0; +- +- this_leaf = CPUID4_INFO_IDX(cpu, index); +- nshared = this_leaf->base.eax.split.num_threads_sharing + 1; ++ this_leaf = this_cpu_ci->info_list + index; ++ nshared = base->eax.split.num_threads_sharing + 1; + apicid = cpu_data(cpu).apicid; + first = apicid - (apicid % nshared); + last = first + nshared - 1; + + for_each_online_cpu(i) { ++ this_cpu_ci = get_cpu_cacheinfo(i); ++ if (!this_cpu_ci->info_list) ++ continue; ++ + apicid = cpu_data(i).apicid; + if ((apicid < first) || (apicid > last)) + continue; +- if (!per_cpu(ici_cpuid4_info, i)) +- continue; +- this_leaf = CPUID4_INFO_IDX(i, index); ++ ++ this_leaf = this_cpu_ci->info_list + index; + + for_each_online_cpu(sibling) { + apicid = cpu_data(sibling).apicid; + if ((apicid < first) || (apicid > last)) + continue; +- set_bit(sibling, this_leaf->shared_cpu_map); ++ cpumask_set_cpu(sibling, ++ &this_leaf->shared_cpu_map); + } + } + } else +@@ -799,72 +861,70 @@ static int cache_shared_amd_cpu_map_setup(unsigned int cpu, int index) + return 1; + } + +-static void cache_shared_cpu_map_setup(unsigned int cpu, int index) ++static void __cache_cpumap_setup(unsigned int cpu, int index, ++ struct _cpuid4_info_regs *base) + { +- struct _cpuid4_info *this_leaf, *sibling_leaf; ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ struct cacheinfo *this_leaf, *sibling_leaf; + unsigned long num_threads_sharing; + int index_msb, i; + struct cpuinfo_x86 *c = &cpu_data(cpu); + + if (c->x86_vendor == X86_VENDOR_AMD) { +- if (cache_shared_amd_cpu_map_setup(cpu, index)) ++ if (__cache_amd_cpumap_setup(cpu, index, base)) + return; + } + +- this_leaf = CPUID4_INFO_IDX(cpu, index); +- num_threads_sharing = 1 + this_leaf->base.eax.split.num_threads_sharing; ++ this_leaf = this_cpu_ci->info_list + index; ++ num_threads_sharing = 1 + base->eax.split.num_threads_sharing; + ++ cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map); + if (num_threads_sharing == 1) +- cpumask_set_cpu(cpu, to_cpumask(this_leaf->shared_cpu_map)); +- else { +- index_msb = get_count_order(num_threads_sharing); ++ return; + +- for_each_online_cpu(i) { +- if (cpu_data(i).apicid >> index_msb == +- c->apicid >> index_msb) { +- cpumask_set_cpu(i, +- to_cpumask(this_leaf->shared_cpu_map)); +- if (i != cpu && per_cpu(ici_cpuid4_info, i)) { +- sibling_leaf = +- CPUID4_INFO_IDX(i, index); +- cpumask_set_cpu(cpu, to_cpumask( +- sibling_leaf->shared_cpu_map)); +- } +- } ++ index_msb = get_count_order(num_threads_sharing); ++ ++ for_each_online_cpu(i) ++ if (cpu_data(i).apicid >> index_msb == c->apicid >> index_msb) { ++ struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i); ++ ++ if (i == cpu || !sib_cpu_ci->info_list) ++ continue;/* skip if itself or no cacheinfo */ ++ sibling_leaf = sib_cpu_ci->info_list + index; ++ cpumask_set_cpu(i, &this_leaf->shared_cpu_map); ++ cpumask_set_cpu(cpu, &sibling_leaf->shared_cpu_map); + } +- } +-} +-static void cache_remove_shared_cpu_map(unsigned int cpu, int index) +-{ +- struct _cpuid4_info *this_leaf, *sibling_leaf; +- int sibling; +- +- this_leaf = CPUID4_INFO_IDX(cpu, index); +- for_each_cpu(sibling, to_cpumask(this_leaf->shared_cpu_map)) { +- sibling_leaf = CPUID4_INFO_IDX(sibling, index); +- cpumask_clear_cpu(cpu, +- to_cpumask(sibling_leaf->shared_cpu_map)); +- } +-} +-#else +-static void cache_shared_cpu_map_setup(unsigned int cpu, int index) +-{ + } + +-static void cache_remove_shared_cpu_map(unsigned int cpu, int index) ++static void ci_leaf_init(struct cacheinfo *this_leaf, ++ struct _cpuid4_info_regs *base) + { ++ this_leaf->id = base->id; ++ this_leaf->attributes = CACHE_ID; ++ this_leaf->level = base->eax.split.level; ++ this_leaf->type = cache_type_map[base->eax.split.type]; ++ this_leaf->coherency_line_size = ++ base->ebx.split.coherency_line_size + 1; ++ this_leaf->ways_of_associativity = ++ base->ebx.split.ways_of_associativity + 1; ++ this_leaf->size = base->size; ++ this_leaf->number_of_sets = base->ecx.split.number_of_sets + 1; ++ this_leaf->physical_line_partition = ++ base->ebx.split.physical_line_partition + 1; ++ this_leaf->priv = base->nb; + } +-#endif + +-static void free_cache_attributes(unsigned int cpu) ++static int __init_cache_level(unsigned int cpu) + { +- int i; +- +- for (i = 0; i < num_cache_leaves; i++) +- cache_remove_shared_cpu_map(cpu, i); ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); + +- kfree(per_cpu(ici_cpuid4_info, cpu)); +- per_cpu(ici_cpuid4_info, cpu) = NULL; ++ if (!num_cache_leaves) ++ return -ENOENT; ++ if (!this_cpu_ci) ++ return -EINVAL; ++ this_cpu_ci->num_levels = 3; ++ this_cpu_ci->num_leaves = num_cache_leaves; ++ return 0; + } + + /* +@@ -886,411 +946,37 @@ static void get_cache_id(int cpu, struct _cpuid4_info_regs *id4_regs) + int get_cpu_cache_id(int cpu, int level) + { + int i; ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); + +- for (i = 0; i < num_cache_leaves; i++) { +- struct _cpuid4_info *this_leaf = CPUID4_INFO_IDX(cpu, i); ++ for (i = 0; i < this_cpu_ci->num_leaves; i++) { ++ struct cacheinfo *this_leaf = this_cpu_ci->info_list + i; + +- if (this_leaf->base.eax.split.level == level) +- return this_leaf->base.id; ++ if (this_leaf->level == level) ++ return this_leaf->id; + } + + return -1; + } + +-static void get_cpu_leaves(void *_retval) +-{ +- int j, *retval = _retval, cpu = smp_processor_id(); +- +- /* Do cpuid and store the results */ +- for (j = 0; j < num_cache_leaves; j++) { +- struct _cpuid4_info *this_leaf = CPUID4_INFO_IDX(cpu, j); +- +- *retval = cpuid4_cache_lookup_regs(j, &this_leaf->base); +- if (unlikely(*retval < 0)) { +- int i; +- +- for (i = 0; i < j; i++) +- cache_remove_shared_cpu_map(cpu, i); +- break; +- } +- cache_shared_cpu_map_setup(cpu, j); +- get_cache_id(cpu, &this_leaf->base); +- } +-} +- +-static int detect_cache_attributes(unsigned int cpu) +-{ +- int retval; +- +- if (num_cache_leaves == 0) +- return -ENOENT; +- +- per_cpu(ici_cpuid4_info, cpu) = kzalloc( +- sizeof(struct _cpuid4_info) * num_cache_leaves, GFP_KERNEL); +- if (per_cpu(ici_cpuid4_info, cpu) == NULL) +- return -ENOMEM; +- +- smp_call_function_single(cpu, get_cpu_leaves, &retval, true); +- if (retval) { +- kfree(per_cpu(ici_cpuid4_info, cpu)); +- per_cpu(ici_cpuid4_info, cpu) = NULL; +- } +- +- return retval; +-} +- +-#include +-#include +-#include +- +-/* pointer to kobject for cpuX/cache */ +-static DEFINE_PER_CPU(struct kobject *, ici_cache_kobject); +- +-struct _index_kobject { +- struct kobject kobj; +- unsigned int cpu; +- unsigned short index; +-}; +- +-/* pointer to array of kobjects for cpuX/cache/indexY */ +-static DEFINE_PER_CPU(struct _index_kobject *, ici_index_kobject); +-#define INDEX_KOBJECT_PTR(x, y) (&((per_cpu(ici_index_kobject, x))[y])) +- +-#define show_one_plus(file_name, object, val) \ +-static ssize_t show_##file_name(struct _cpuid4_info *this_leaf, char *buf, \ +- unsigned int cpu) \ +-{ \ +- return sprintf(buf, "%lu\n", (unsigned long)this_leaf->object + val); \ +-} +- +-show_one_plus(level, base.eax.split.level, 0); +-show_one_plus(coherency_line_size, base.ebx.split.coherency_line_size, 1); +-show_one_plus(physical_line_partition, base.ebx.split.physical_line_partition, 1); +-show_one_plus(ways_of_associativity, base.ebx.split.ways_of_associativity, 1); +-show_one_plus(number_of_sets, base.ecx.split.number_of_sets, 1); +- +-static ssize_t show_id(struct _cpuid4_info *this_leaf, char *buf, +- unsigned int cpu) +-{ +- return sprintf(buf, "%u\n", this_leaf->base.id); +-} +- +-static ssize_t show_size(struct _cpuid4_info *this_leaf, char *buf, +- unsigned int cpu) +-{ +- return sprintf(buf, "%luK\n", this_leaf->base.size / 1024); +-} +- +-static ssize_t show_shared_cpu_map_func(struct _cpuid4_info *this_leaf, +- int type, char *buf) +-{ +- ptrdiff_t len = PTR_ALIGN(buf + PAGE_SIZE - 1, PAGE_SIZE) - buf; +- int n = 0; +- +- if (len > 1) { +- const struct cpumask *mask; +- +- mask = to_cpumask(this_leaf->shared_cpu_map); +- n = type ? +- cpulist_scnprintf(buf, len-2, mask) : +- cpumask_scnprintf(buf, len-2, mask); +- buf[n++] = '\n'; +- buf[n] = '\0'; +- } +- return n; +-} +- +-static inline ssize_t show_shared_cpu_map(struct _cpuid4_info *leaf, char *buf, +- unsigned int cpu) +-{ +- return show_shared_cpu_map_func(leaf, 0, buf); +-} +- +-static inline ssize_t show_shared_cpu_list(struct _cpuid4_info *leaf, char *buf, +- unsigned int cpu) +-{ +- return show_shared_cpu_map_func(leaf, 1, buf); +-} +- +-static ssize_t show_type(struct _cpuid4_info *this_leaf, char *buf, +- unsigned int cpu) +-{ +- switch (this_leaf->base.eax.split.type) { +- case CACHE_TYPE_DATA: +- return sprintf(buf, "Data\n"); +- case CACHE_TYPE_INST: +- return sprintf(buf, "Instruction\n"); +- case CACHE_TYPE_UNIFIED: +- return sprintf(buf, "Unified\n"); +- default: +- return sprintf(buf, "Unknown\n"); +- } +-} +- +-#define to_object(k) container_of(k, struct _index_kobject, kobj) +-#define to_attr(a) container_of(a, struct _cache_attr, attr) +- +-#define define_one_ro(_name) \ +-static struct _cache_attr _name = \ +- __ATTR(_name, 0444, show_##_name, NULL) +- +-define_one_ro(id); +-define_one_ro(level); +-define_one_ro(type); +-define_one_ro(coherency_line_size); +-define_one_ro(physical_line_partition); +-define_one_ro(ways_of_associativity); +-define_one_ro(number_of_sets); +-define_one_ro(size); +-define_one_ro(shared_cpu_map); +-define_one_ro(shared_cpu_list); +- +-static struct attribute *default_attrs[] = { +- &id.attr, +- &type.attr, +- &level.attr, +- &coherency_line_size.attr, +- &physical_line_partition.attr, +- &ways_of_associativity.attr, +- &number_of_sets.attr, +- &size.attr, +- &shared_cpu_map.attr, +- &shared_cpu_list.attr, +- NULL +-}; +- +-#ifdef CONFIG_AMD_NB +-static struct attribute **amd_l3_attrs(void) +-{ +- static struct attribute **attrs; +- int n; +- +- if (attrs) +- return attrs; +- +- n = ARRAY_SIZE(default_attrs); +- +- if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) +- n += 2; +- +- if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) +- n += 1; +- +- attrs = kzalloc(n * sizeof (struct attribute *), GFP_KERNEL); +- if (attrs == NULL) +- return attrs = default_attrs; +- +- for (n = 0; default_attrs[n]; n++) +- attrs[n] = default_attrs[n]; +- +- if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) { +- attrs[n++] = &cache_disable_0.attr; +- attrs[n++] = &cache_disable_1.attr; +- } +- +- if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) +- attrs[n++] = &subcaches.attr; +- +- return attrs; +-} +-#endif +- +-static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf) +-{ +- struct _cache_attr *fattr = to_attr(attr); +- struct _index_kobject *this_leaf = to_object(kobj); +- ssize_t ret; +- +- ret = fattr->show ? +- fattr->show(CPUID4_INFO_IDX(this_leaf->cpu, this_leaf->index), +- buf, this_leaf->cpu) : +- 0; +- return ret; +-} +- +-static ssize_t store(struct kobject *kobj, struct attribute *attr, +- const char *buf, size_t count) +-{ +- struct _cache_attr *fattr = to_attr(attr); +- struct _index_kobject *this_leaf = to_object(kobj); +- ssize_t ret; +- +- ret = fattr->store ? +- fattr->store(CPUID4_INFO_IDX(this_leaf->cpu, this_leaf->index), +- buf, count, this_leaf->cpu) : +- 0; +- return ret; +-} +- +-static const struct sysfs_ops sysfs_ops = { +- .show = show, +- .store = store, +-}; +- +-static struct kobj_type ktype_cache = { +- .sysfs_ops = &sysfs_ops, +- .default_attrs = default_attrs, +-}; +- +-static struct kobj_type ktype_percpu_entry = { +- .sysfs_ops = &sysfs_ops, +-}; +- +-static void cpuid4_cache_sysfs_exit(unsigned int cpu) +-{ +- kfree(per_cpu(ici_cache_kobject, cpu)); +- kfree(per_cpu(ici_index_kobject, cpu)); +- per_cpu(ici_cache_kobject, cpu) = NULL; +- per_cpu(ici_index_kobject, cpu) = NULL; +- free_cache_attributes(cpu); +-} +- +-static int cpuid4_cache_sysfs_init(unsigned int cpu) +-{ +- int err; +- +- if (num_cache_leaves == 0) +- return -ENOENT; +- +- err = detect_cache_attributes(cpu); +- if (err) +- return err; +- +- /* Allocate all required memory */ +- per_cpu(ici_cache_kobject, cpu) = +- kzalloc(sizeof(struct kobject), GFP_KERNEL); +- if (unlikely(per_cpu(ici_cache_kobject, cpu) == NULL)) +- goto err_out; +- +- per_cpu(ici_index_kobject, cpu) = kzalloc( +- sizeof(struct _index_kobject) * num_cache_leaves, GFP_KERNEL); +- if (unlikely(per_cpu(ici_index_kobject, cpu) == NULL)) +- goto err_out; +- +- return 0; +- +-err_out: +- cpuid4_cache_sysfs_exit(cpu); +- return -ENOMEM; +-} +- +-static DECLARE_BITMAP(cache_dev_map, NR_CPUS); +- +-/* Add/Remove cache interface for CPU device */ +-static int cache_add_dev(struct device *dev) ++static int __populate_cache_leaves(unsigned int cpu) + { +- unsigned int cpu = dev->id; +- unsigned long i, j; +- struct _index_kobject *this_object; +- struct _cpuid4_info *this_leaf; +- int retval; +- +- retval = cpuid4_cache_sysfs_init(cpu); +- if (unlikely(retval < 0)) +- return retval; +- +- retval = kobject_init_and_add(per_cpu(ici_cache_kobject, cpu), +- &ktype_percpu_entry, +- &dev->kobj, "%s", "cache"); +- if (retval < 0) { +- cpuid4_cache_sysfs_exit(cpu); +- return retval; +- } +- +- for (i = 0; i < num_cache_leaves; i++) { +- this_object = INDEX_KOBJECT_PTR(cpu, i); +- this_object->cpu = cpu; +- this_object->index = i; ++ unsigned int idx, ret; ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ struct cacheinfo *this_leaf = this_cpu_ci->info_list; ++ struct _cpuid4_info_regs id4_regs = {}; + +- this_leaf = CPUID4_INFO_IDX(cpu, i); +- +- ktype_cache.default_attrs = default_attrs; +-#ifdef CONFIG_AMD_NB +- if (this_leaf->base.nb) +- ktype_cache.default_attrs = amd_l3_attrs(); +-#endif +- retval = kobject_init_and_add(&(this_object->kobj), +- &ktype_cache, +- per_cpu(ici_cache_kobject, cpu), +- "index%1lu", i); +- if (unlikely(retval)) { +- for (j = 0; j < i; j++) +- kobject_put(&(INDEX_KOBJECT_PTR(cpu, j)->kobj)); +- kobject_put(per_cpu(ici_cache_kobject, cpu)); +- cpuid4_cache_sysfs_exit(cpu); +- return retval; +- } +- kobject_uevent(&(this_object->kobj), KOBJ_ADD); ++ for (idx = 0; idx < this_cpu_ci->num_leaves; idx++) { ++ ret = cpuid4_cache_lookup_regs(idx, &id4_regs); ++ if (ret) ++ return ret; ++ get_cache_id(cpu, &id4_regs); ++ ci_leaf_init(this_leaf++, &id4_regs); ++ __cache_cpumap_setup(cpu, idx, &id4_regs); + } +- cpumask_set_cpu(cpu, to_cpumask(cache_dev_map)); ++ this_cpu_ci->cpu_map_populated = true; + +- kobject_uevent(per_cpu(ici_cache_kobject, cpu), KOBJ_ADD); + return 0; + } + +-static void cache_remove_dev(struct device *dev) +-{ +- unsigned int cpu = dev->id; +- unsigned long i; +- +- if (per_cpu(ici_cpuid4_info, cpu) == NULL) +- return; +- if (!cpumask_test_cpu(cpu, to_cpumask(cache_dev_map))) +- return; +- cpumask_clear_cpu(cpu, to_cpumask(cache_dev_map)); +- +- for (i = 0; i < num_cache_leaves; i++) +- kobject_put(&(INDEX_KOBJECT_PTR(cpu, i)->kobj)); +- kobject_put(per_cpu(ici_cache_kobject, cpu)); +- cpuid4_cache_sysfs_exit(cpu); +-} +- +-static int cacheinfo_cpu_callback(struct notifier_block *nfb, +- unsigned long action, void *hcpu) +-{ +- unsigned int cpu = (unsigned long)hcpu; +- struct device *dev; +- +- dev = get_cpu_device(cpu); +- switch (action) { +- case CPU_ONLINE: +- case CPU_ONLINE_FROZEN: +- cache_add_dev(dev); +- break; +- case CPU_DEAD: +- case CPU_DEAD_FROZEN: +- cache_remove_dev(dev); +- break; +- } +- return NOTIFY_OK; +-} +- +-static struct notifier_block cacheinfo_cpu_notifier = { +- .notifier_call = cacheinfo_cpu_callback, +-}; +- +-static int __init cache_sysfs_init(void) +-{ +- int i, err = 0; +- +- if (num_cache_leaves == 0) +- return 0; +- +- cpu_notifier_register_begin(); +- for_each_online_cpu(i) { +- struct device *dev = get_cpu_device(i); +- +- err = cache_add_dev(dev); +- if (err) +- goto out; +- } +- __register_hotcpu_notifier(&cacheinfo_cpu_notifier); +- +-out: +- cpu_notifier_register_done(); +- return err; +-} +- +-device_initcall(cache_sysfs_init); +- +-#endif ++DEFINE_SMP_CALL_CACHE_FUNCTION(init_cache_level) ++DEFINE_SMP_CALL_CACHE_FUNCTION(populate_cache_leaves) +diff --git a/drivers/base/Makefile b/drivers/base/Makefile +index 53c3fe1..527d291 100644 +--- a/drivers/base/Makefile ++++ b/drivers/base/Makefile +@@ -4,7 +4,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \ + driver.o class.o platform.o \ + cpu.o firmware.o init.o map.o devres.o \ + attribute_container.o transport_class.o \ +- topology.o container.o property.o ++ topology.o container.o property.o cacheinfo.o + obj-$(CONFIG_DEVTMPFS) += devtmpfs.o + obj-$(CONFIG_DMA_CMA) += dma-contiguous.o + obj-y += power/ +diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c +new file mode 100644 +index 0000000..eb3af27 +--- /dev/null ++++ b/drivers/base/cacheinfo.c +@@ -0,0 +1,662 @@ ++/* ++ * cacheinfo support - processor cache information via sysfs ++ * ++ * Based on arch/x86/kernel/cpu/intel_cacheinfo.c ++ * Author: Sudeep Holla ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed "as is" WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* pointer to per cpu cacheinfo */ ++static DEFINE_PER_CPU(struct cpu_cacheinfo, ci_cpu_cacheinfo); ++#define ci_cacheinfo(cpu) (&per_cpu(ci_cpu_cacheinfo, cpu)) ++#define cache_leaves(cpu) (ci_cacheinfo(cpu)->num_leaves) ++#define per_cpu_cacheinfo(cpu) (ci_cacheinfo(cpu)->info_list) ++ ++struct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu) ++{ ++ return ci_cacheinfo(cpu); ++} ++ ++#ifdef CONFIG_OF ++static int cache_setup_of_node(unsigned int cpu) ++{ ++ struct device_node *np; ++ struct cacheinfo *this_leaf; ++ struct device *cpu_dev = get_cpu_device(cpu); ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ unsigned int index = 0; ++ ++ /* skip if of_node is already populated */ ++ if (this_cpu_ci->info_list->of_node) ++ return 0; ++ ++ if (!cpu_dev) { ++ pr_err("No cpu device for CPU %d\n", cpu); ++ return -ENODEV; ++ } ++ np = cpu_dev->of_node; ++ if (!np) { ++ pr_err("Failed to find cpu%d device node\n", cpu); ++ return -ENOENT; ++ } ++ ++ while (index < cache_leaves(cpu)) { ++ this_leaf = this_cpu_ci->info_list + index; ++ if (this_leaf->level != 1) ++ np = of_find_next_cache_node(np); ++ else ++ np = of_node_get(np);/* cpu node itself */ ++ if (!np) ++ break; ++ this_leaf->of_node = np; ++ index++; ++ } ++ ++ if (index != cache_leaves(cpu)) /* not all OF nodes populated */ ++ return -ENOENT; ++ ++ return 0; ++} ++ ++static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, ++ struct cacheinfo *sib_leaf) ++{ ++ return sib_leaf->of_node == this_leaf->of_node; ++} ++ ++/* OF properties to query for a given cache type */ ++struct cache_type_info { ++ const char *size_prop; ++ const char *line_size_props[2]; ++ const char *nr_sets_prop; ++}; ++ ++static const struct cache_type_info cache_type_info[] = { ++ { ++ .size_prop = "cache-size", ++ .line_size_props = { "cache-line-size", ++ "cache-block-size", }, ++ .nr_sets_prop = "cache-sets", ++ }, { ++ .size_prop = "i-cache-size", ++ .line_size_props = { "i-cache-line-size", ++ "i-cache-block-size", }, ++ .nr_sets_prop = "i-cache-sets", ++ }, { ++ .size_prop = "d-cache-size", ++ .line_size_props = { "d-cache-line-size", ++ "d-cache-block-size", }, ++ .nr_sets_prop = "d-cache-sets", ++ }, ++}; ++ ++static inline int get_cacheinfo_idx(enum cache_type type) ++{ ++ if (type == CACHE_TYPE_UNIFIED) ++ return 0; ++ return type; ++} ++ ++static void cache_size(struct cacheinfo *this_leaf) ++{ ++ const char *propname; ++ const __be32 *cache_size; ++ int ct_idx; ++ ++ ct_idx = get_cacheinfo_idx(this_leaf->type); ++ propname = cache_type_info[ct_idx].size_prop; ++ ++ cache_size = of_get_property(this_leaf->of_node, propname, NULL); ++ if (cache_size) ++ this_leaf->size = of_read_number(cache_size, 1); ++} ++ ++/* not cache_line_size() because that's a macro in include/linux/cache.h */ ++static void cache_get_line_size(struct cacheinfo *this_leaf) ++{ ++ const __be32 *line_size; ++ int i, lim, ct_idx; ++ ++ ct_idx = get_cacheinfo_idx(this_leaf->type); ++ lim = ARRAY_SIZE(cache_type_info[ct_idx].line_size_props); ++ ++ for (i = 0; i < lim; i++) { ++ const char *propname; ++ ++ propname = cache_type_info[ct_idx].line_size_props[i]; ++ line_size = of_get_property(this_leaf->of_node, propname, NULL); ++ if (line_size) ++ break; ++ } ++ ++ if (line_size) ++ this_leaf->coherency_line_size = of_read_number(line_size, 1); ++} ++ ++static void cache_nr_sets(struct cacheinfo *this_leaf) ++{ ++ const char *propname; ++ const __be32 *nr_sets; ++ int ct_idx; ++ ++ ct_idx = get_cacheinfo_idx(this_leaf->type); ++ propname = cache_type_info[ct_idx].nr_sets_prop; ++ ++ nr_sets = of_get_property(this_leaf->of_node, propname, NULL); ++ if (nr_sets) ++ this_leaf->number_of_sets = of_read_number(nr_sets, 1); ++} ++ ++static void cache_associativity(struct cacheinfo *this_leaf) ++{ ++ unsigned int line_size = this_leaf->coherency_line_size; ++ unsigned int nr_sets = this_leaf->number_of_sets; ++ unsigned int size = this_leaf->size; ++ ++ /* ++ * If the cache is fully associative, there is no need to ++ * check the other properties. ++ */ ++ if (!(nr_sets == 1) && (nr_sets > 0 && size > 0 && line_size > 0)) ++ this_leaf->ways_of_associativity = (size / nr_sets) / line_size; ++} ++ ++static void cache_of_override_properties(unsigned int cpu) ++{ ++ int index; ++ struct cacheinfo *this_leaf; ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ ++ for (index = 0; index < cache_leaves(cpu); index++) { ++ this_leaf = this_cpu_ci->info_list + index; ++ cache_size(this_leaf); ++ cache_get_line_size(this_leaf); ++ cache_nr_sets(this_leaf); ++ cache_associativity(this_leaf); ++ } ++} ++#else ++static void cache_of_override_properties(unsigned int cpu) { } ++static inline int cache_setup_of_node(unsigned int cpu) { return 0; } ++static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, ++ struct cacheinfo *sib_leaf) ++{ ++ /* ++ * For non-DT systems, assume unique level 1 cache, system-wide ++ * shared caches for all other levels. This will be used only if ++ * arch specific code has not populated shared_cpu_map ++ */ ++ return !(this_leaf->level == 1); ++} ++#endif ++ ++static int cache_shared_cpu_map_setup(unsigned int cpu) ++{ ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ struct cacheinfo *this_leaf, *sib_leaf; ++ unsigned int index; ++ int ret = 0; ++ ++ if (this_cpu_ci->cpu_map_populated) ++ return 0; ++ ++ if (of_have_populated_dt()) ++ ret = cache_setup_of_node(cpu); ++ else if (!acpi_disabled) ++ /* No cache property/hierarchy support yet in ACPI */ ++ ret = -ENOTSUPP; ++ if (ret) ++ return ret; ++ ++ for (index = 0; index < cache_leaves(cpu); index++) { ++ unsigned int i; ++ ++ this_leaf = this_cpu_ci->info_list + index; ++ /* skip if shared_cpu_map is already populated */ ++ if (!cpumask_empty(&this_leaf->shared_cpu_map)) ++ continue; ++ ++ cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map); ++ for_each_online_cpu(i) { ++ struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i); ++ ++ if (i == cpu || !sib_cpu_ci->info_list) ++ continue;/* skip if itself or no cacheinfo */ ++ sib_leaf = sib_cpu_ci->info_list + index; ++ if (cache_leaves_are_shared(this_leaf, sib_leaf)) { ++ cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map); ++ cpumask_set_cpu(i, &this_leaf->shared_cpu_map); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static void cache_shared_cpu_map_remove(unsigned int cpu) ++{ ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ struct cacheinfo *this_leaf, *sib_leaf; ++ unsigned int sibling, index; ++ ++ for (index = 0; index < cache_leaves(cpu); index++) { ++ this_leaf = this_cpu_ci->info_list + index; ++ for_each_cpu(sibling, &this_leaf->shared_cpu_map) { ++ struct cpu_cacheinfo *sib_cpu_ci; ++ ++ if (sibling == cpu) /* skip itself */ ++ continue; ++ ++ sib_cpu_ci = get_cpu_cacheinfo(sibling); ++ if (!sib_cpu_ci->info_list) ++ continue; ++ ++ sib_leaf = sib_cpu_ci->info_list + index; ++ cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map); ++ cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map); ++ } ++ of_node_put(this_leaf->of_node); ++ } ++} ++ ++static void cache_override_properties(unsigned int cpu) ++{ ++ if (of_have_populated_dt()) ++ return cache_of_override_properties(cpu); ++} ++ ++static void free_cache_attributes(unsigned int cpu) ++{ ++ if (!per_cpu_cacheinfo(cpu)) ++ return; ++ ++ cache_shared_cpu_map_remove(cpu); ++ ++ kfree(per_cpu_cacheinfo(cpu)); ++ per_cpu_cacheinfo(cpu) = NULL; ++} ++ ++int __weak init_cache_level(unsigned int cpu) ++{ ++ return -ENOENT; ++} ++ ++int __weak populate_cache_leaves(unsigned int cpu) ++{ ++ return -ENOENT; ++} ++ ++static int detect_cache_attributes(unsigned int cpu) ++{ ++ int ret; ++ ++ if (init_cache_level(cpu) || !cache_leaves(cpu)) ++ return -ENOENT; ++ ++ per_cpu_cacheinfo(cpu) = kcalloc(cache_leaves(cpu), ++ sizeof(struct cacheinfo), GFP_KERNEL); ++ if (per_cpu_cacheinfo(cpu) == NULL) ++ return -ENOMEM; ++ ++ ret = populate_cache_leaves(cpu); ++ if (ret) ++ goto free_ci; ++ /* ++ * For systems using DT for cache hierarchy, of_node and shared_cpu_map ++ * will be set up here only if they are not populated already ++ */ ++ ret = cache_shared_cpu_map_setup(cpu); ++ if (ret) { ++ pr_warn("Unable to detect cache hierarchy for CPU %d\n", cpu); ++ goto free_ci; ++ } ++ ++ cache_override_properties(cpu); ++ return 0; ++ ++free_ci: ++ free_cache_attributes(cpu); ++ return ret; ++} ++ ++/* pointer to cpuX/cache device */ ++static DEFINE_PER_CPU(struct device *, ci_cache_dev); ++#define per_cpu_cache_dev(cpu) (per_cpu(ci_cache_dev, cpu)) ++ ++static cpumask_t cache_dev_map; ++ ++/* pointer to array of devices for cpuX/cache/indexY */ ++static DEFINE_PER_CPU(struct device **, ci_index_dev); ++#define per_cpu_index_dev(cpu) (per_cpu(ci_index_dev, cpu)) ++#define per_cache_index_dev(cpu, idx) ((per_cpu_index_dev(cpu))[idx]) ++ ++#define show_one(file_name, object) \ ++static ssize_t file_name##_show(struct device *dev, \ ++ struct device_attribute *attr, char *buf) \ ++{ \ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); \ ++ return sprintf(buf, "%u\n", this_leaf->object); \ ++} ++ ++show_one(id, id); ++show_one(level, level); ++show_one(coherency_line_size, coherency_line_size); ++show_one(number_of_sets, number_of_sets); ++show_one(physical_line_partition, physical_line_partition); ++show_one(ways_of_associativity, ways_of_associativity); ++ ++static ssize_t size_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%uK\n", this_leaf->size >> 10); ++} ++ ++static ssize_t shared_cpumap_show_func(struct device *dev, bool list, char *buf) ++{ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ const struct cpumask *mask = &this_leaf->shared_cpu_map; ++ ++ return cpumap_print_to_pagebuf(list, buf, mask); ++} ++ ++static ssize_t shared_cpu_map_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return shared_cpumap_show_func(dev, false, buf); ++} ++ ++static ssize_t shared_cpu_list_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return shared_cpumap_show_func(dev, true, buf); ++} ++ ++static ssize_t type_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ ++ switch (this_leaf->type) { ++ case CACHE_TYPE_DATA: ++ return sprintf(buf, "Data\n"); ++ case CACHE_TYPE_INST: ++ return sprintf(buf, "Instruction\n"); ++ case CACHE_TYPE_UNIFIED: ++ return sprintf(buf, "Unified\n"); ++ default: ++ return -EINVAL; ++ } ++} ++ ++static ssize_t allocation_policy_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ unsigned int ci_attr = this_leaf->attributes; ++ int n = 0; ++ ++ if ((ci_attr & CACHE_READ_ALLOCATE) && (ci_attr & CACHE_WRITE_ALLOCATE)) ++ n = sprintf(buf, "ReadWriteAllocate\n"); ++ else if (ci_attr & CACHE_READ_ALLOCATE) ++ n = sprintf(buf, "ReadAllocate\n"); ++ else if (ci_attr & CACHE_WRITE_ALLOCATE) ++ n = sprintf(buf, "WriteAllocate\n"); ++ return n; ++} ++ ++static ssize_t write_policy_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ unsigned int ci_attr = this_leaf->attributes; ++ int n = 0; ++ ++ if (ci_attr & CACHE_WRITE_THROUGH) ++ n = sprintf(buf, "WriteThrough\n"); ++ else if (ci_attr & CACHE_WRITE_BACK) ++ n = sprintf(buf, "WriteBack\n"); ++ return n; ++} ++ ++static DEVICE_ATTR_RO(id); ++static DEVICE_ATTR_RO(level); ++static DEVICE_ATTR_RO(type); ++static DEVICE_ATTR_RO(coherency_line_size); ++static DEVICE_ATTR_RO(ways_of_associativity); ++static DEVICE_ATTR_RO(number_of_sets); ++static DEVICE_ATTR_RO(size); ++static DEVICE_ATTR_RO(allocation_policy); ++static DEVICE_ATTR_RO(write_policy); ++static DEVICE_ATTR_RO(shared_cpu_map); ++static DEVICE_ATTR_RO(shared_cpu_list); ++static DEVICE_ATTR_RO(physical_line_partition); ++ ++static struct attribute *cache_default_attrs[] = { ++ &dev_attr_id.attr, ++ &dev_attr_type.attr, ++ &dev_attr_level.attr, ++ &dev_attr_shared_cpu_map.attr, ++ &dev_attr_shared_cpu_list.attr, ++ &dev_attr_coherency_line_size.attr, ++ &dev_attr_ways_of_associativity.attr, ++ &dev_attr_number_of_sets.attr, ++ &dev_attr_size.attr, ++ &dev_attr_allocation_policy.attr, ++ &dev_attr_write_policy.attr, ++ &dev_attr_physical_line_partition.attr, ++ NULL ++}; ++ ++static umode_t ++cache_default_attrs_is_visible(struct kobject *kobj, ++ struct attribute *attr, int unused) ++{ ++ struct device *dev = kobj_to_dev(kobj); ++ struct cacheinfo *this_leaf = dev_get_drvdata(dev); ++ const struct cpumask *mask = &this_leaf->shared_cpu_map; ++ umode_t mode = attr->mode; ++ ++ if ((attr == &dev_attr_id.attr) && (this_leaf->attributes & CACHE_ID)) ++ return mode; ++ if ((attr == &dev_attr_type.attr) && this_leaf->type) ++ return mode; ++ if ((attr == &dev_attr_level.attr) && this_leaf->level) ++ return mode; ++ if ((attr == &dev_attr_shared_cpu_map.attr) && !cpumask_empty(mask)) ++ return mode; ++ if ((attr == &dev_attr_shared_cpu_list.attr) && !cpumask_empty(mask)) ++ return mode; ++ if ((attr == &dev_attr_coherency_line_size.attr) && ++ this_leaf->coherency_line_size) ++ return mode; ++ if ((attr == &dev_attr_ways_of_associativity.attr) && ++ this_leaf->size) /* allow 0 = full associativity */ ++ return mode; ++ if ((attr == &dev_attr_number_of_sets.attr) && ++ this_leaf->number_of_sets) ++ return mode; ++ if ((attr == &dev_attr_size.attr) && this_leaf->size) ++ return mode; ++ if ((attr == &dev_attr_write_policy.attr) && ++ (this_leaf->attributes & CACHE_WRITE_POLICY_MASK)) ++ return mode; ++ if ((attr == &dev_attr_allocation_policy.attr) && ++ (this_leaf->attributes & CACHE_ALLOCATE_POLICY_MASK)) ++ return mode; ++ if ((attr == &dev_attr_physical_line_partition.attr) && ++ this_leaf->physical_line_partition) ++ return mode; ++ ++ return 0; ++} ++ ++static const struct attribute_group cache_default_group = { ++ .attrs = cache_default_attrs, ++ .is_visible = cache_default_attrs_is_visible, ++}; ++ ++static const struct attribute_group *cache_default_groups[] = { ++ &cache_default_group, ++ NULL, ++}; ++ ++static const struct attribute_group *cache_private_groups[] = { ++ &cache_default_group, ++ NULL, /* Place holder for private group */ ++ NULL, ++}; ++ ++const struct attribute_group * ++__weak cache_get_priv_group(struct cacheinfo *this_leaf) ++{ ++ return NULL; ++} ++ ++static const struct attribute_group ** ++cache_get_attribute_groups(struct cacheinfo *this_leaf) ++{ ++ const struct attribute_group *priv_group = ++ cache_get_priv_group(this_leaf); ++ ++ if (!priv_group) ++ return cache_default_groups; ++ ++ if (!cache_private_groups[1]) ++ cache_private_groups[1] = priv_group; ++ ++ return cache_private_groups; ++} ++ ++/* Add/Remove cache interface for CPU device */ ++static void cpu_cache_sysfs_exit(unsigned int cpu) ++{ ++ int i; ++ struct device *ci_dev; ++ ++ if (per_cpu_index_dev(cpu)) { ++ for (i = 0; i < cache_leaves(cpu); i++) { ++ ci_dev = per_cache_index_dev(cpu, i); ++ if (!ci_dev) ++ continue; ++ device_unregister(ci_dev); ++ } ++ kfree(per_cpu_index_dev(cpu)); ++ per_cpu_index_dev(cpu) = NULL; ++ } ++ device_unregister(per_cpu_cache_dev(cpu)); ++ per_cpu_cache_dev(cpu) = NULL; ++} ++ ++static int cpu_cache_sysfs_init(unsigned int cpu) ++{ ++ struct device *dev = get_cpu_device(cpu); ++ ++ if (per_cpu_cacheinfo(cpu) == NULL) ++ return -ENOENT; ++ ++ per_cpu_cache_dev(cpu) = cpu_device_create(dev, NULL, NULL, "cache"); ++ if (IS_ERR(per_cpu_cache_dev(cpu))) ++ return PTR_ERR(per_cpu_cache_dev(cpu)); ++ ++ /* Allocate all required memory */ ++ per_cpu_index_dev(cpu) = kcalloc(cache_leaves(cpu), ++ sizeof(struct device *), GFP_KERNEL); ++ if (unlikely(per_cpu_index_dev(cpu) == NULL)) ++ goto err_out; ++ ++ return 0; ++ ++err_out: ++ cpu_cache_sysfs_exit(cpu); ++ return -ENOMEM; ++} ++ ++static int cache_add_dev(unsigned int cpu) ++{ ++ unsigned int i; ++ int rc; ++ struct device *ci_dev, *parent; ++ struct cacheinfo *this_leaf; ++ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); ++ const struct attribute_group **cache_groups; ++ ++ rc = cpu_cache_sysfs_init(cpu); ++ if (unlikely(rc < 0)) ++ return rc; ++ ++ parent = per_cpu_cache_dev(cpu); ++ for (i = 0; i < cache_leaves(cpu); i++) { ++ this_leaf = this_cpu_ci->info_list + i; ++ if (this_leaf->disable_sysfs) ++ continue; ++ cache_groups = cache_get_attribute_groups(this_leaf); ++ ci_dev = cpu_device_create(parent, this_leaf, cache_groups, ++ "index%1u", i); ++ if (IS_ERR(ci_dev)) { ++ rc = PTR_ERR(ci_dev); ++ goto err; ++ } ++ per_cache_index_dev(cpu, i) = ci_dev; ++ } ++ cpumask_set_cpu(cpu, &cache_dev_map); ++ ++ return 0; ++err: ++ cpu_cache_sysfs_exit(cpu); ++ return rc; ++} ++ ++static int cacheinfo_cpu_online(unsigned int cpu) ++{ ++ int rc = detect_cache_attributes(cpu); ++ ++ if (rc) ++ return rc; ++ rc = cache_add_dev(cpu); ++ if (rc) ++ free_cache_attributes(cpu); ++ return rc; ++} ++ ++static int cacheinfo_cpu_pre_down(unsigned int cpu) ++{ ++ if (cpumask_test_and_clear_cpu(cpu, &cache_dev_map)) ++ cpu_cache_sysfs_exit(cpu); ++ ++ free_cache_attributes(cpu); ++ return 0; ++} ++ ++static int __init cacheinfo_sysfs_init(void) ++{ ++ return cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "base/cacheinfo:online", ++ cacheinfo_cpu_online, cacheinfo_cpu_pre_down); ++} ++device_initcall(cacheinfo_sysfs_init); +diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c +index bd82212..803d2a0 100644 +--- a/drivers/base/cpu.c ++++ b/drivers/base/cpu.c +@@ -338,6 +338,60 @@ struct device *get_cpu_device(unsigned cpu) + } + EXPORT_SYMBOL_GPL(get_cpu_device); + ++static void device_create_release(struct device *dev) ++{ ++ kfree(dev); ++} ++ ++static struct device * ++__cpu_device_create(struct device *parent, void *drvdata, ++ const struct attribute_group **groups, ++ const char *fmt, va_list args) ++{ ++ struct device *dev = NULL; ++ int retval = -ENODEV; ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (!dev) { ++ retval = -ENOMEM; ++ goto error; ++ } ++ ++ device_initialize(dev); ++ dev->parent = parent; ++ dev->groups = groups; ++ dev->release = device_create_release; ++ dev_set_drvdata(dev, drvdata); ++ ++ retval = kobject_set_name_vargs(&dev->kobj, fmt, args); ++ if (retval) ++ goto error; ++ ++ retval = device_add(dev); ++ if (retval) ++ goto error; ++ ++ return dev; ++ ++error: ++ put_device(dev); ++ return ERR_PTR(retval); ++} ++ ++struct device *cpu_device_create(struct device *parent, void *drvdata, ++ const struct attribute_group **groups, ++ const char *fmt, ...) ++{ ++ va_list vargs; ++ struct device *dev; ++ ++ va_start(vargs, fmt); ++ dev = __cpu_device_create(parent, drvdata, groups, fmt, vargs); ++ va_end(vargs); ++ return dev; ++} ++EXPORT_SYMBOL_GPL(cpu_device_create); ++ + #ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE + static DEVICE_ATTR(modalias, 0444, arch_print_cpu_modalias, NULL); + #endif +diff --git a/include/linux/cacheinfo.h b/include/linux/cacheinfo.h +new file mode 100644 +index 0000000..6a524bf +--- /dev/null ++++ b/include/linux/cacheinfo.h +@@ -0,0 +1,104 @@ ++#ifndef _LINUX_CACHEINFO_H ++#define _LINUX_CACHEINFO_H ++ ++#include ++#include ++#include ++ ++struct device_node; ++struct attribute; ++ ++enum cache_type { ++ CACHE_TYPE_NOCACHE = 0, ++ CACHE_TYPE_INST = BIT(0), ++ CACHE_TYPE_DATA = BIT(1), ++ CACHE_TYPE_SEPARATE = CACHE_TYPE_INST | CACHE_TYPE_DATA, ++ CACHE_TYPE_UNIFIED = BIT(2), ++}; ++ ++/** ++ * struct cacheinfo - represent a cache leaf node ++ * @id: This cache's id. It is unique among caches with the same (type, level). ++ * @type: type of the cache - data, inst or unified ++ * @level: represents the hierarchy in the multi-level cache ++ * @coherency_line_size: size of each cache line usually representing ++ * the minimum amount of data that gets transferred from memory ++ * @number_of_sets: total number of sets, a set is a collection of cache ++ * lines sharing the same index ++ * @ways_of_associativity: number of ways in which a particular memory ++ * block can be placed in the cache ++ * @physical_line_partition: number of physical cache lines sharing the ++ * same cachetag ++ * @size: Total size of the cache ++ * @shared_cpu_map: logical cpumask representing all the cpus sharing ++ * this cache node ++ * @attributes: bitfield representing various cache attributes ++ * @of_node: if devicetree is used, this represents either the cpu node in ++ * case there's no explicit cache node or the cache node itself in the ++ * device tree ++ * @disable_sysfs: indicates whether this node is visible to the user via ++ * sysfs or not ++ * @priv: pointer to any private data structure specific to particular ++ * cache design ++ * ++ * While @of_node, @disable_sysfs and @priv are used for internal book ++ * keeping, the remaining members form the core properties of the cache ++ */ ++struct cacheinfo { ++ unsigned int id; ++ enum cache_type type; ++ unsigned int level; ++ unsigned int coherency_line_size; ++ unsigned int number_of_sets; ++ unsigned int ways_of_associativity; ++ unsigned int physical_line_partition; ++ unsigned int size; ++ cpumask_t shared_cpu_map; ++ unsigned int attributes; ++#define CACHE_WRITE_THROUGH BIT(0) ++#define CACHE_WRITE_BACK BIT(1) ++#define CACHE_WRITE_POLICY_MASK \ ++ (CACHE_WRITE_THROUGH | CACHE_WRITE_BACK) ++#define CACHE_READ_ALLOCATE BIT(2) ++#define CACHE_WRITE_ALLOCATE BIT(3) ++#define CACHE_ALLOCATE_POLICY_MASK \ ++ (CACHE_READ_ALLOCATE | CACHE_WRITE_ALLOCATE) ++#define CACHE_ID BIT(4) ++ ++ struct device_node *of_node; ++ bool disable_sysfs; ++ void *priv; ++}; ++ ++struct cpu_cacheinfo { ++ struct cacheinfo *info_list; ++ unsigned int num_levels; ++ unsigned int num_leaves; ++ bool cpu_map_populated; ++}; ++ ++/* ++ * Helpers to make sure "func" is executed on the cpu whose cache ++ * attributes are being detected ++ */ ++#define DEFINE_SMP_CALL_CACHE_FUNCTION(func) \ ++static inline void _##func(void *ret) \ ++{ \ ++ int cpu = smp_processor_id(); \ ++ *(int *)ret = __##func(cpu); \ ++} \ ++ \ ++int func(unsigned int cpu) \ ++{ \ ++ int ret; \ ++ smp_call_function_single(cpu, _##func, &ret, true); \ ++ return ret; \ ++} ++ ++struct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu); ++int init_cache_level(unsigned int cpu); ++int populate_cache_leaves(unsigned int cpu); ++ ++const struct attribute_group *cache_get_priv_group(struct cacheinfo *this_leaf); ++ ++#endif /* _LINUX_CACHEINFO_H */ +diff --git a/include/linux/cpu.h b/include/linux/cpu.h +index 1cc4c78..8354cc0 100644 +--- a/include/linux/cpu.h ++++ b/include/linux/cpu.h +@@ -43,6 +43,9 @@ extern ssize_t cpu_show_spectre_v1(struct device *dev, + extern ssize_t cpu_show_spectre_v2(struct device *dev, + struct device_attribute *attr, char *buf); + ++extern struct device *cpu_device_create(struct device *parent, void *drvdata, ++ const struct attribute_group **groups, ++ const char *fmt, ...); + #ifdef CONFIG_HOTPLUG_CPU + extern void unregister_cpu(struct cpu *cpu); + extern ssize_t arch_cpu_probe(const char *, size_t); +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/US101216-IMA-support-in-Titanium-kernel.patch b/kernel-std/centos/patches/US101216-IMA-support-in-Titanium-kernel.patch new file mode 100644 index 00000000..54440381 --- /dev/null +++ b/kernel-std/centos/patches/US101216-IMA-support-in-Titanium-kernel.patch @@ -0,0 +1,373 @@ +From 1d197374f04642f9a5e71cc013d65d5438c35dc3 Mon Sep 17 00:00:00 2001 +Message-Id: <1d197374f04642f9a5e71cc013d65d5438c35dc3.1527544850.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Kam Nasim +Date: Wed, 23 Aug 2017 17:58:12 -0400 +Subject: [PATCH 22/26] US101216: IMA support in Titanium kernel + +facilitate building the IMA subsytem out-of-the-kernel tree as a Kernel +module (for which CONFIG_IMA and CONFIG_INTEGRITY will be undefined) by: +- exporting certain function symbols which will be linked to the kernel + module. This includes redefining the export symbols for kernel +functions such that when the kernel module loads, it dynamically points +to those new function definations and reverts to Kernel default +definitions on module deinit +- enabling inode readcount +- modification to ima_file_check to pass in file OPEN status + +Signed-off-by: Jim Somerville +--- + fs/namei.c | 2 +- + fs/nfsd/vfs.c | 2 +- + fs/xattr.c | 1 + + include/linux/fs.h | 15 +------ + include/linux/ima.h | 77 +++++++------------------------- + include/linux/integrity.h | 22 ++++----- + security/security.c | 111 +++++++++++++++++++++++++++++++++++++++++++++- + 7 files changed, 140 insertions(+), 90 deletions(-) + +diff --git a/fs/namei.c b/fs/namei.c +index 9f90b63..bf91ea0 100644 +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -3236,7 +3236,7 @@ opened: + error = open_check_o_direct(file); + if (error) + goto exit_fput; +- error = ima_file_check(file, op->acc_mode); ++ error = ima_file_check(file, op->acc_mode, *opened); + if (error) + goto exit_fput; + +diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c +index 00e98c3..cb9250e 100644 +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -883,7 +883,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, + goto out_nfserr; + } + +- host_err = ima_file_check(file, may_flags); ++ host_err = ima_file_check(file, may_flags, 0); + if (host_err) { + fput(file); + goto out_nfserr; +diff --git a/fs/xattr.c b/fs/xattr.c +index e540aca..cc307ec 100644 +--- a/fs/xattr.c ++++ b/fs/xattr.c +@@ -207,6 +207,7 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value, + *xattr_value = value; + return error; + } ++EXPORT_SYMBOL_GPL(vfs_getxattr_alloc); + + /* Compare an extended attribute value with the given value */ + int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name, +diff --git a/include/linux/fs.h b/include/linux/fs.h +index eb6f994..2dbaf80 100644 +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -677,9 +677,8 @@ struct inode { + struct fsnotify_mark_connector __rcu *i_fsnotify_marks) + #endif + +-#ifdef CONFIG_IMA + atomic_t i_readcount; /* struct files open RO */ +-#endif ++ + void *i_private; /* fs or device private pointer */ + }; + +@@ -2827,7 +2826,6 @@ static inline bool inode_is_open_for_write(const struct inode *inode) + return atomic_read(&inode->i_writecount) > 0; + } + +-#ifdef CONFIG_IMA + static inline void i_readcount_dec(struct inode *inode) + { + BUG_ON(!atomic_read(&inode->i_readcount)); +@@ -2837,16 +2835,7 @@ static inline void i_readcount_inc(struct inode *inode) + { + atomic_inc(&inode->i_readcount); + } +-#else +-static inline void i_readcount_dec(struct inode *inode) +-{ +- return; +-} +-static inline void i_readcount_inc(struct inode *inode) +-{ +- return; +-} +-#endif ++ + extern int do_pipe_flags(int *, int); + + extern int kernel_read(struct file *, loff_t, char *, unsigned long); +diff --git a/include/linux/ima.h b/include/linux/ima.h +index 1b7f268..9fee45c 100644 +--- a/include/linux/ima.h ++++ b/include/linux/ima.h +@@ -13,64 +13,21 @@ + #include + struct linux_binprm; + +-#ifdef CONFIG_IMA +-extern int ima_bprm_check(struct linux_binprm *bprm); +-extern int ima_file_check(struct file *file, int mask); +-extern void ima_file_free(struct file *file); +-extern int ima_file_mmap(struct file *file, unsigned long prot); +-extern int ima_module_check(struct file *file); +- +-#else +-static inline int ima_bprm_check(struct linux_binprm *bprm) +-{ +- return 0; +-} +- +-static inline int ima_file_check(struct file *file, int mask) +-{ +- return 0; +-} +- +-static inline void ima_file_free(struct file *file) +-{ +- return; +-} +- +-static inline int ima_file_mmap(struct file *file, unsigned long prot) +-{ +- return 0; +-} +- +-static inline int ima_module_check(struct file *file) +-{ +- return 0; +-} +- +-#endif /* CONFIG_IMA */ +- +-#ifdef CONFIG_IMA_APPRAISE +-extern void ima_inode_post_setattr(struct dentry *dentry); +-extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, ++/* ++ * The IMA Kernel module has to redefine these symbols so that ++ * the kernel module can link a dynamic function, as a hook into ++ * the Kernel FS calls (which use these) ++ */ ++/* ifdef CONFIG_IMA */ ++extern int (*ima_bprm_check)(struct linux_binprm *bprm); ++extern int (*ima_file_check)(struct file *file, int mask, int opened); ++extern void (*ima_file_free)(struct file *file); ++extern int (*ima_file_mmap)(struct file *file, unsigned long prot); ++extern int (*ima_module_check)(struct file *file); ++ ++/* ifdef CONFIG_IMA_APPRAISE */ ++extern void (*ima_inode_post_setattr)(struct dentry *dentry); ++extern int (*ima_inode_setxattr)(struct dentry *dentry, const char *xattr_name, + const void *xattr_value, size_t xattr_value_len); +-extern int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name); +-#else +-static inline void ima_inode_post_setattr(struct dentry *dentry) +-{ +- return; +-} +- +-static inline int ima_inode_setxattr(struct dentry *dentry, +- const char *xattr_name, +- const void *xattr_value, +- size_t xattr_value_len) +-{ +- return 0; +-} +- +-static inline int ima_inode_removexattr(struct dentry *dentry, +- const char *xattr_name) +-{ +- return 0; +-} +-#endif /* CONFIG_IMA_APPRAISE */ +-#endif /* _LINUX_IMA_H */ ++extern int (*ima_inode_removexattr)(struct dentry *dentry, const char *xattr_name); ++#endif +diff --git a/include/linux/integrity.h b/include/linux/integrity.h +index 83222ce..a5040b6 100644 +--- a/include/linux/integrity.h ++++ b/include/linux/integrity.h +@@ -21,20 +21,14 @@ enum integrity_status { + }; + + /* List of EVM protected security xattrs */ +-#ifdef CONFIG_INTEGRITY +-extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode); +-extern void integrity_inode_free(struct inode *inode); ++/* ++ * The Integrity Kernel module has to redefine these symbols so that ++ * the kernel module can link a dynamic function, as a hook into ++ * the Kernel Security subsystem (which use these) ++ */ + +-#else +-static inline struct integrity_iint_cache * +- integrity_inode_get(struct inode *inode) +-{ +- return NULL; +-} ++/* #ifdef CONFIG_INTEGRITY */ ++extern struct integrity_iint_cache *(*integrity_inode_get)(struct inode *inode); ++extern void (*integrity_inode_free)(struct inode *inode); + +-static inline void integrity_inode_free(struct inode *inode) +-{ +- return; +-} +-#endif /* CONFIG_INTEGRITY */ + #endif /* _LINUX_INTEGRITY_H */ +diff --git a/security/security.c b/security/security.c +index f069482..646a0e3 100644 +--- a/security/security.c ++++ b/security/security.c +@@ -156,6 +156,110 @@ EXPORT_SYMBOL(unregister_lsm_notifier); + + /* Security operations */ + ++/* ++ * Export these symbols since the IMA and Integrity ++ * modules will redefine it. We do this EXPORT in ++ * the security endpoint as this is the last Kernel ++ * hook into the Integrity / IMA modules ++ */ ++#ifndef CONFIG_INTEGRITY ++static struct integrity_iint_cache* integrity_inode_get_kmod(struct inode *inode) ++{ ++ return NULL; ++} ++ ++static void integrity_inode_free_kmod(struct inode *inode) ++{ ++ return; ++} ++ ++struct integrity_iint_cache * ++ (*integrity_inode_get)(struct inode *) = &integrity_inode_get_kmod; ++void ++ (*integrity_inode_free)(struct inode*) = &integrity_inode_free_kmod; ++ ++EXPORT_SYMBOL_GPL(integrity_inode_get); ++EXPORT_SYMBOL_GPL(integrity_inode_free); ++#endif ++ ++#ifndef CONFIG_IMA ++static int ima_bprm_check_kmod(struct linux_binprm *bprm) ++{ ++ return 0; ++} ++ ++static int ima_file_check_kmod(struct file *file, int mask, int opened) ++{ ++ return 0; ++} ++ ++static void ima_file_free_kmod(struct file *file) ++{ ++ return; ++} ++ ++static int ima_file_mmap_kmod(struct file *file, unsigned long prot) ++{ ++ return 0; ++} ++ ++static int ima_module_check_kmod(struct file *file) ++{ ++ return 0; ++} ++ ++int ++ (*ima_bprm_check)(struct linux_binprm *) = &ima_bprm_check_kmod; ++int ++ (*ima_file_check)(struct file *, int, int) = &ima_file_check_kmod; ++void ++ (*ima_file_free)(struct file *) = &ima_file_free_kmod; ++int ++ (*ima_file_mmap)(struct file*, unsigned long) = &ima_file_mmap_kmod; ++int ++ (*ima_module_check)(struct file *) = &ima_module_check_kmod; ++ ++EXPORT_SYMBOL_GPL(ima_bprm_check); ++EXPORT_SYMBOL_GPL(ima_file_check); ++EXPORT_SYMBOL_GPL(ima_file_free); ++EXPORT_SYMBOL_GPL(ima_file_mmap); ++EXPORT_SYMBOL_GPL(ima_module_check); ++#endif ++ ++#ifndef CONFIG_IMA_APPRAISE ++static void ima_inode_post_setattr_kmod(struct dentry *dentry) ++{ ++ return; ++} ++ ++static int ima_inode_setxattr_kmod(struct dentry *dentry, ++ const char *xattr_name, ++ const void *xattr_value, ++ size_t xattr_value_len) ++{ ++ return 0; ++} ++ ++static int ima_inode_removexattr_kmod(struct dentry *dentry, ++ const char *xattr_name) ++{ ++ return 0; ++} ++ ++void ++ (*ima_inode_post_setattr)(struct dentry *) = &ima_inode_post_setattr_kmod; ++int ++ (*ima_inode_setxattr)(struct dentry *, const char *, ++ const void *, size_t) = &ima_inode_setxattr_kmod; ++int ++ (*ima_inode_removexattr)(struct dentry *, ++ const char *) = &ima_inode_removexattr_kmod; ++ ++EXPORT_SYMBOL_GPL(ima_inode_post_setattr); ++EXPORT_SYMBOL_GPL(ima_inode_setxattr); ++EXPORT_SYMBOL_GPL(ima_inode_removexattr); ++#endif ++ + int security_ptrace_access_check(struct task_struct *child, unsigned int mode) + { + #ifdef CONFIG_SECURITY_YAMA_STACKED +@@ -715,8 +819,11 @@ EXPORT_SYMBOL(security_inode_listsecurity); + + void security_inode_getsecid(struct inode *inode, u32 *secid) + { +- security_ops->inode_getsecid(inode, secid); ++ if (unlikely(IS_PRIVATE(inode))) ++ return; ++ security_ops->inode_getsecid(inode, secid); + } ++EXPORT_SYMBOL_GPL(security_inode_getsecid); + + int security_inode_copy_up(struct dentry *src, struct cred **new) + { +@@ -1525,6 +1632,7 @@ int security_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule) + { + return security_ops->audit_rule_init(field, op, rulestr, lsmrule); + } ++EXPORT_SYMBOL_GPL(security_audit_rule_init); + + int security_audit_rule_known(struct audit_krule *krule) + { +@@ -1541,5 +1649,6 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule, + { + return security_ops->audit_rule_match(secid, field, op, lsmrule, actx); + } ++EXPORT_SYMBOL_GPL(security_audit_rule_match); + + #endif /* CONFIG_AUDIT */ +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/US103091-IMA-System-Configuration.patch b/kernel-std/centos/patches/US103091-IMA-System-Configuration.patch new file mode 100644 index 00000000..16b4e4f0 --- /dev/null +++ b/kernel-std/centos/patches/US103091-IMA-System-Configuration.patch @@ -0,0 +1,246 @@ +From 43dfb08aa6e053bcf1cf5696ea6bc3b9b2aa3d53 Mon Sep 17 00:00:00 2001 +Message-Id: <43dfb08aa6e053bcf1cf5696ea6bc3b9b2aa3d53.1527544850.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Kam Nasim +Date: Wed, 4 Oct 2017 14:02:10 -0400 +Subject: [PATCH 23/26] US103091: IMA: System Configuration + +Normally (if trusted integrity keyring is disabled), the _ima keyring +needs to be created by user space (specifically systemd), but that has +the added disadvantage of requiring the IMA public key to reside on the +file system as opposed to being compiled in. Somebody could render some +serious Grade A damage by corrupting this public key on the FS. +Crippling the system if IMA 'enforce' action is enabled. + +We will therefore create the IMA keyring inside the kernel and load the +IMA public key as a compiled data blob, similar to how the Kernel loads +trusted X509 keys into the system truststore (.system_keyring) + +Signed-off-by: Jim Somerville +--- + .gitignore | 1 + + include/keys/system_keyring.h | 2 ++ + kernel/Makefile | 42 ++++++++++++++++++++-- + kernel/ima_certificate.S | 20 +++++++++++ + kernel/system_keyring.c | 82 +++++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 145 insertions(+), 2 deletions(-) + create mode 100644 kernel/ima_certificate.S + +diff --git a/.gitignore b/.gitignore +index f73f35f..7148219 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -106,3 +106,4 @@ localversion + + # Red Hat key security + kernel/x509_certificate_list ++kernel/ima_x509_certificate +diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h +index 0e49b3c..6b2da90 100644 +--- a/include/keys/system_keyring.h ++++ b/include/keys/system_keyring.h +@@ -34,4 +34,6 @@ static inline struct key *get_system_trusted_keyring(void) + + #endif /* CONFIG_SYSTEM_TRUSTED_KEYRING */ + ++extern struct key *ima_keyring; ++ + #endif /* _KEYS_SYSTEM_KEYRING_H */ +diff --git a/kernel/Makefile b/kernel/Makefile +index 44a82c1..000b9a8 100644 +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -57,7 +57,7 @@ obj-$(CONFIG_QUEUED_SPINLOCKS) += qspinlock.o + obj-$(CONFIG_QUEUED_RWLOCKS) += qrwlock.o qrwlock_gen.o + obj-$(CONFIG_LOCK_SPIN_ON_OWNER) += osq_lock.o + obj-$(CONFIG_UID16) += uid16.o +-obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o ++obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o ima_certificate.o + obj-$(CONFIG_MODULES) += module.o + obj-$(CONFIG_MODULE_SIG) += module_signing.o + obj-$(CONFIG_MODULE_SIG_UEFI) += modsign_uefi.o +@@ -197,7 +197,45 @@ targets += $(obj)/.x509.list + $(obj)/.x509.list: + @echo $(X509_CERTIFICATES) >$@ + +-clean-files := x509_certificate_list .x509.list ++ ++############################################################################### ++# ++# We will roll in the IMA X.509 certificate and pull it in the the kernel ++# so that it gets loaded into the _ima keyring during boot. ++# ++# Ideally, this should have been treated similar to other .x509 certificates ++# (X509_CERTIFICATES), but those all get loaded into the system trusted keyring ++# and since the canonical pathnames are not available in the x509_certificate_list ++# compiled data blob, there is no way to isolate the IMA certificate from the ++# rest. Therefore we treat the IMA certificate as a seperate blob all together. ++# ++# We look in the source root for the IMA certificate, of name "ima_signing_key.pub" ++# ++############################################################################### ++IMA_X509_CERTIFICATE := $(srctree)/ima_signing_key.pub ++ ++ifneq ($(wildcard $(obj)/.x509.ima),) ++ifneq ($(shell cat $(obj)/.x509.ima),$(IMA_X509_CERTIFICATE)) ++$(info IMA: X.509 certificate changed) ++$(shell rm $(obj)/.x509.ima) ++endif ++endif ++ ++kernel/ima_certificate.o: $(obj)/ima_x509_certificate ++ ++quiet_cmd_imacert = CERTS $@ ++ cmd_imacert = cat $(IMA_X509_CERTIFICATE) >$@ $(foreach IMA_X509,$(IMA_X509_CERTIFICATE),; echo " - Including cert $(IMA_X509)") ++ ++targets += $(obj)/ima_x509_certificate ++$(obj)/ima_x509_certificate: $(IMA_X509_CERTIFICATE) $(obj)/.x509.ima ++ $(call if_changed,imacert) ++ ++targets += $(obj)/.x509.ima ++$(obj)/.x509.ima: ++ @echo $(IMA_X509_CERTIFICATE) >$@ ++ ++ ++clean-files := x509_certificate_list .x509.list ima_x509_certificate .x509.ima + endif + + ifeq ($(CONFIG_MODULE_SIG),y) +diff --git a/kernel/ima_certificate.S b/kernel/ima_certificate.S +new file mode 100644 +index 0000000..0c665dd +--- /dev/null ++++ b/kernel/ima_certificate.S +@@ -0,0 +1,20 @@ ++#include ++#include ++ ++ __INITRODATA ++ ++ .align 8 ++ .globl VMLINUX_SYMBOL(ima_system_certificate) ++VMLINUX_SYMBOL(ima_system_certificate): ++__cert_list_start: ++ .incbin "kernel/ima_x509_certificate" ++__cert_list_end: ++ ++ .align 8 ++ .globl VMLINUX_SYMBOL(ima_system_certificate_size) ++VMLINUX_SYMBOL(ima_system_certificate_size): ++#ifdef CONFIG_64BIT ++ .quad __cert_list_end - __cert_list_start ++#else ++ .long __cert_list_end - __cert_list_start ++#endif +diff --git a/kernel/system_keyring.c b/kernel/system_keyring.c +index c15e93f..92beb15 100644 +--- a/kernel/system_keyring.c ++++ b/kernel/system_keyring.c +@@ -23,10 +23,15 @@ EXPORT_SYMBOL_GPL(system_trusted_keyring); + #ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING + struct key *system_blacklist_keyring; + #endif ++struct key *ima_keyring; ++EXPORT_SYMBOL_GPL(ima_keyring); + + extern __initconst const u8 system_certificate_list[]; + extern __initconst const unsigned long system_certificate_list_size; + ++extern __initconst const u8 ima_system_certificate[]; ++extern __initconst const unsigned long ima_system_certificate_size; ++ + /* + * Load the compiled-in keys + */ +@@ -57,6 +62,27 @@ static __init int system_trusted_keyring_init(void) + + set_bit(KEY_FLAG_TRUSTED_ONLY, &system_blacklist_keyring->flags); + #endif ++ /* Normally (if trusted integrity keyring is disabled), the _ima ++ * keyring needs to be created by user space but that has the ++ * added disadvantage of requiring the IMA public key to reside on ++ * the file system as opposed to being compiled in. ++ * We will therefore form a _ima keyring here and load build ++ * the IMA X.509 certificate ++ * ++ * N.B: The IMA keyring only allows root userspace view & read ops ++ */ ++ pr_notice("Initializing system IMA keyring\n"); ++ ++ ima_keyring = keyring_alloc("_ima", ++ KUIDT_INIT(0), KGIDT_INIT(0), ++ current_cred(), ++ ((KEY_POS_ALL & ~KEY_POS_SETATTR) | ++ KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH), ++ KEY_ALLOC_NOT_IN_QUOTA, NULL); ++ if (IS_ERR(ima_keyring)) ++ panic("Can't allocate system IMA keyring\n"); ++ ++ set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_keyring->flags); + + return 0; + } +@@ -121,3 +147,59 @@ dodgy_cert: + return 0; + } + late_initcall(load_system_certificate_list); ++ ++/* ++ * Load the compiled-in IMA certificate. ++ */ ++static __init int load_ima_system_certificate(void) ++{ ++ key_ref_t key; ++ const u8 *p, *end; ++ size_t plen; ++ ++ pr_notice("Loading compiled-in X.509 IMA certificate\n"); ++ ++ p = ima_system_certificate; ++ end = p + ima_system_certificate_size; ++ while (p < end) { ++ /* Each cert begins with an ASN.1 SEQUENCE tag and must be more ++ * than 256 bytes in size. ++ */ ++ if (end - p < 4) ++ goto dodgy_cert; ++ if (p[0] != 0x30 && ++ p[1] != 0x82) ++ goto dodgy_cert; ++ plen = (p[2] << 8) | p[3]; ++ plen += 4; ++ if (plen > end - p) ++ goto dodgy_cert; ++ ++ key = key_create_or_update(make_key_ref(ima_keyring, 1), ++ "asymmetric", ++ NULL, ++ p, ++ plen, ++ ((KEY_POS_ALL & ~KEY_POS_SETATTR) | ++ KEY_USR_VIEW | KEY_USR_READ), ++ KEY_ALLOC_NOT_IN_QUOTA | ++ KEY_ALLOC_TRUSTED); ++ if (IS_ERR(key)) { ++ pr_err("Problem loading in-kernel X.509 certificate (%ld)\n", ++ PTR_ERR(key)); ++ } else { ++ set_bit(KEY_FLAG_BUILTIN, &key_ref_to_ptr(key)->flags); ++ pr_notice("Loaded X.509 cert '%s'\n", ++ key_ref_to_ptr(key)->description); ++ key_ref_put(key); ++ } ++ p += plen; ++ } ++ ++ return 0; ++ ++dodgy_cert: ++ pr_err("Problem parsing in-kernel X.509 IMA certificate\n"); ++ return 0; ++} ++late_initcall(load_ima_system_certificate); +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/affine-compute-kernel-threads.patch b/kernel-std/centos/patches/affine-compute-kernel-threads.patch new file mode 100644 index 00000000..1997d3d4 --- /dev/null +++ b/kernel-std/centos/patches/affine-compute-kernel-threads.patch @@ -0,0 +1,168 @@ +From 6b6edb5a389c03e208e6a123a7807af66283237d Mon Sep 17 00:00:00 2001 +Message-Id: <6b6edb5a389c03e208e6a123a7807af66283237d.1527544850.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Chris Friesen +Date: Tue, 24 Nov 2015 16:27:28 -0500 +Subject: [PATCH 05/26] affine compute kernel threads + +This is a kernel enhancement to configure the cpu affinity of kernel +threads via kernel boot option kthread_cpus=. The compute +kickstart file and compute-huge.sh scripts will update grub with the +new option. + +With kthread_cpus specified, the cpumask is immediately applied upon +thread launch. This does not affect kernel threads that specify cpu +and node. + +Note: this is based off of Christoph Lameter's patch at +https://lwn.net/Articles/565932/ with the only difference being +the kernel parameter changed from kthread to kthread_cpus. + +Signed-off-by: Christoph Lameter +Signed-off-by: Chris Friesen +[VT: The existing "isolcpus" + kernel bootarg, cgroup/cpuset, and taskset might provide the some + way to have cpu isolation. However none of them satisfies the requirements. + Replacing spaces with tabs. Combine two calls of set_cpus_allowed_ptr() + in kernel_init_freeable() in init/main.c into one. Performed tests] +Signed-off-by: Vu Tran + +Signed-off-by: Jim Somerville +--- + Documentation/kernel-parameters.txt | 10 ++++++++++ + include/linux/cpumask.h | 2 ++ + init/main.c | 6 ++---- + kernel/cpu.c | 13 +++++++++++++ + kernel/kmod.c | 3 +++ + kernel/kthread.c | 4 ++-- + 6 files changed, 32 insertions(+), 6 deletions(-) + +diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt +index 1806170..2f7feb0 100644 +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -1539,6 +1539,16 @@ bytes respectively. Such letter suffixes can also be entirely omitted. + + kpti [X86-64] Enable kernel page table isolation. + ++ kthread_cpus= [KNL, SMP] Only run kernel threads on the specified ++ list of processors. The kernel will start threads ++ on the indicated processors only (unless there ++ are specific reasons to run a thread with ++ different affinities). This can be used to make ++ init start on certain processors and also to ++ control where kmod and other user space threads ++ are being spawned. Allows to keep kernel threads ++ away from certain cores unless absoluteluy necessary. ++ + kvm.ignore_msrs=[KVM] Ignore guest accesses to unhandled MSRs. + Default is 0 (don't ignore, but inject #GP) + +diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h +index 14b4972..37a6adf 100644 +--- a/include/linux/cpumask.h ++++ b/include/linux/cpumask.h +@@ -52,6 +52,7 @@ extern int nr_cpu_ids; + * cpu_present_mask - has bit 'cpu' set iff cpu is populated + * cpu_online_mask - has bit 'cpu' set iff cpu available to scheduler + * cpu_active_mask - has bit 'cpu' set iff cpu available to migration ++ * cpu_kthread_mask - has bit 'cpu' set iff general kernel threads allowed + * + * If !CONFIG_HOTPLUG_CPU, present == possible, and active == online. + * +@@ -88,6 +89,7 @@ extern const struct cpumask *const cpu_possible_mask; + extern const struct cpumask *const cpu_online_mask; + extern const struct cpumask *const cpu_present_mask; + extern const struct cpumask *const cpu_active_mask; ++extern const struct cpumask *const cpu_kthread_mask; + + #if NR_CPUS > 1 + #define num_online_cpus() cpumask_weight(cpu_online_mask) +diff --git a/init/main.c b/init/main.c +index 085c9c5..089f83d 100644 +--- a/init/main.c ++++ b/init/main.c +@@ -956,10 +956,6 @@ static noinline void __init kernel_init_freeable(void) + * init can allocate pages on any node + */ + set_mems_allowed(node_states[N_MEMORY]); +- /* +- * init can run on any cpu. +- */ +- set_cpus_allowed_ptr(current, cpu_all_mask); + + cad_pid = task_pid(current); + +@@ -975,6 +971,8 @@ static noinline void __init kernel_init_freeable(void) + + do_basic_setup(); + ++ set_cpus_allowed_ptr(current, cpu_kthread_mask); ++ + /* Open the /dev/console on the rootfs, this should never fail */ + if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) + pr_err("Warning: unable to open an initial console.\n"); +diff --git a/kernel/cpu.c b/kernel/cpu.c +index 0d9e250..6c156bd 100644 +--- a/kernel/cpu.c ++++ b/kernel/cpu.c +@@ -756,6 +756,19 @@ static DECLARE_BITMAP(cpu_active_bits, CONFIG_NR_CPUS) __read_mostly; + const struct cpumask *const cpu_active_mask = to_cpumask(cpu_active_bits); + EXPORT_SYMBOL(cpu_active_mask); + ++static DECLARE_BITMAP(cpu_kthread_bits, CONFIG_NR_CPUS) __read_mostly ++ = CPU_BITS_ALL; ++const struct cpumask *const cpu_kthread_mask = to_cpumask(cpu_kthread_bits); ++EXPORT_SYMBOL(cpu_kthread_mask); ++ ++static int __init kthread_setup(char *str) ++{ ++ cpulist_parse(str, (struct cpumask *)&cpu_kthread_bits); ++ return 1; ++} ++__setup("kthread_cpus=", kthread_setup); ++ ++ + void set_cpu_possible(unsigned int cpu, bool possible) + { + if (possible) +diff --git a/kernel/kmod.c b/kernel/kmod.c +index 86ab754..4bf584b 100644 +--- a/kernel/kmod.c ++++ b/kernel/kmod.c +@@ -204,6 +204,9 @@ static int ____call_usermodehelper(void *data) + flush_signal_handlers(current, 1); + spin_unlock_irq(¤t->sighand->siglock); + ++ /* We can run only where init is allowed to run. */ ++ set_cpus_allowed_ptr(current, cpu_kthread_mask); ++ + /* + * Our parent is keventd, which runs with elevated scheduling priority. + * Avoid propagating that into the userspace child. +diff --git a/kernel/kthread.c b/kernel/kthread.c +index 703d910..7ea32eb 100644 +--- a/kernel/kthread.c ++++ b/kernel/kthread.c +@@ -284,7 +284,7 @@ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data), + * The kernel thread should not inherit these properties. + */ + sched_setscheduler_nocheck(create.result, SCHED_NORMAL, ¶m); +- set_cpus_allowed_ptr(create.result, cpu_all_mask); ++ set_cpus_allowed_ptr(create.result, cpu_kthread_mask); + } + return create.result; + } +@@ -454,7 +454,7 @@ int kthreadd(void *unused) + /* Setup a clean context for our children to inherit. */ + set_task_comm(tsk, "kthreadd"); + ignore_signals(tsk); +- set_cpus_allowed_ptr(tsk, cpu_all_mask); ++ set_cpus_allowed_ptr(tsk, cpu_kthread_mask); + set_mems_allowed(node_states[N_MEMORY]); + + current->flags |= PF_NOFREEZE; +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/aic94xx-Skip-reading-user-settings-if-flash-is-not-f.patch b/kernel-std/centos/patches/aic94xx-Skip-reading-user-settings-if-flash-is-not-f.patch new file mode 100644 index 00000000..0bdea252 --- /dev/null +++ b/kernel-std/centos/patches/aic94xx-Skip-reading-user-settings-if-flash-is-not-f.patch @@ -0,0 +1,55 @@ +From 6771ec5b24042ea92c2a872d28b41e39c4445c68 Mon Sep 17 00:00:00 2001 +Message-Id: <6771ec5b24042ea92c2a872d28b41e39c4445c68.1527544850.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Hannes Reinecke +Date: Mon, 6 Jul 2015 13:07:58 +0200 +Subject: [PATCH 24/26] aic94xx: Skip reading user settings if flash is not + found + +If no user settings are found it's pointless trying to +read them from flash. So skip that step. +This also fixes a compilation warning about uninitialized variables in +aic94xx. + +Signed-off-by: Hannes Reinecke +Reviewed-by: Christoph Hellwig +Signed-off-by: James Bottomley +Signed-off-by: Jim Somerville +--- + drivers/scsi/aic94xx/aic94xx_sds.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/scsi/aic94xx/aic94xx_sds.c b/drivers/scsi/aic94xx/aic94xx_sds.c +index edb43fd..c831e30 100644 +--- a/drivers/scsi/aic94xx/aic94xx_sds.c ++++ b/drivers/scsi/aic94xx/aic94xx_sds.c +@@ -983,7 +983,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, + { + int err, i; + u32 offs, size; +- struct asd_ll_el *el; ++ struct asd_ll_el *el = NULL; + struct asd_ctrla_phy_settings *ps; + struct asd_ctrla_phy_settings dflt_ps; + +@@ -1004,6 +1004,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, + + size = sizeof(struct asd_ctrla_phy_settings); + ps = &dflt_ps; ++ goto out_process; + } + + if (size == 0) +@@ -1028,7 +1029,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, + ASD_DPRINTK("couldn't find ctrla phy settings struct\n"); + goto out2; + } +- ++out_process: + err = asd_process_ctrla_phy_settings(asd_ha, ps); + if (err) { + ASD_DPRINTK("couldn't process ctrla phy settings\n"); +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/cma-add-placement-specifier-for-cma-kernel-parameter.patch b/kernel-std/centos/patches/cma-add-placement-specifier-for-cma-kernel-parameter.patch new file mode 100644 index 00000000..5c41754d --- /dev/null +++ b/kernel-std/centos/patches/cma-add-placement-specifier-for-cma-kernel-parameter.patch @@ -0,0 +1,224 @@ +From b6eb39977df83c5ceca5b35c041bb2923e5a47d8 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Akinobu Mita +Date: Tue, 31 May 2016 16:09:04 -0400 +Subject: [PATCH 10/26] cma: add placement specifier for "cma=" kernel + parameter + +Commit 5ea3b1b2f8ad9162684431ce6188102ca4c64b7a upstream +Backported-by: Nam Ninh + +Currently, "cma=" kernel parameter is used to specify the size of CMA, +but we can't specify where it is located. We want to locate CMA below +4GB for devices only supporting 32-bit addressing on 64-bit systems +without iommu. + +This enables to specify the placement of CMA by extending "cma=" kernel +parameter. + +Examples: + 1. locate 64MB CMA below 4GB by "cma=64M@0-4G" + 2. locate 64MB CMA exact at 512MB by "cma=64M@512M" + +Note that the DMA contiguous memory allocator on x86 assumes that +page_address() works for the pages to allocate. So this change requires +to limit end address of contiguous memory area upto max_pfn_mapped to +prevent from locating it on highmem area by the argument of +dma_contiguous_reserve(). + +Signed-off-by: Akinobu Mita +Cc: Marek Szyprowski +Cc: Konrad Rzeszutek Wilk +Cc: David Woodhouse +Cc: Don Dutile +Cc: Thomas Gleixner +Cc: Ingo Molnar +Cc: "H. Peter Anvin" +Cc: Andi Kleen +Cc: Yinghai Lu +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +Signed-off-by: Jim Somerville +--- + Documentation/kernel-parameters.txt | 7 +++++-- + arch/x86/kernel/setup.c | 2 +- + drivers/base/dma-contiguous.c | 42 ++++++++++++++++++++++++++++--------- + include/linux/dma-contiguous.h | 9 +++++--- + 4 files changed, 44 insertions(+), 16 deletions(-) + +diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt +index 590c8c2..c8f8f82 100644 +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -579,8 +579,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. + Also note the kernel might malfunction if you disable + some critical bits. + +- cma=nn[MG] [ARM,KNL] +- Sets the size of kernel global memory area for contiguous ++ cma=nn[MG]@[start[MG][-end[MG]]] ++ [ARM,X86,KNL] ++ Sets the size of kernel global memory area for ++ contiguous memory allocations and optionally the ++ placement constraint by the physical address range of + memory allocations. For more information, see + include/linux/dma-contiguous.h + +diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c +index 9eca4ac..4e39287 100644 +--- a/arch/x86/kernel/setup.c ++++ b/arch/x86/kernel/setup.c +@@ -1283,7 +1283,7 @@ void __init setup_arch(char **cmdline_p) + setup_real_mode(); + + memblock_set_current_limit(get_max_mapped()); +- dma_contiguous_reserve(0); ++ dma_contiguous_reserve(max_pfn_mapped << PAGE_SHIFT); + + /* + * NOTE: On x86-32, only from this point on, fixmaps are ready for use. +diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c +index a0f89fc..a7d5bda 100644 +--- a/drivers/base/dma-contiguous.c ++++ b/drivers/base/dma-contiguous.c +@@ -59,11 +59,22 @@ struct cma *dma_contiguous_default_area; + */ + static const phys_addr_t size_bytes = CMA_SIZE_MBYTES * SZ_1M; + static phys_addr_t size_cmdline = -1; ++static phys_addr_t base_cmdline; ++static phys_addr_t limit_cmdline; + + static int __init early_cma(char *p) + { + pr_debug("%s(%s)\n", __func__, p); + size_cmdline = memparse(p, &p); ++ if (*p != '@') ++ return 0; ++ base_cmdline = memparse(p + 1, &p); ++ if (*p != '-') { ++ limit_cmdline = base_cmdline + size_cmdline; ++ return 0; ++ } ++ limit_cmdline = memparse(p + 1, &p); ++ + return 0; + } + early_param("cma", early_cma); +@@ -107,11 +118,18 @@ static inline __maybe_unused phys_addr_t cma_early_percent_memory(void) + void __init dma_contiguous_reserve(phys_addr_t limit) + { + phys_addr_t selected_size = 0; ++ phys_addr_t selected_base = 0; ++ phys_addr_t selected_limit = limit; ++ bool fixed = false; + + pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit); + + if (size_cmdline != -1) { + selected_size = size_cmdline; ++ selected_base = base_cmdline; ++ selected_limit = min_not_zero(limit_cmdline, limit); ++ if (base_cmdline + size_cmdline == limit_cmdline) ++ fixed = true; + } else { + #ifdef CONFIG_CMA_SIZE_SEL_MBYTES + selected_size = size_bytes; +@@ -128,10 +146,12 @@ void __init dma_contiguous_reserve(phys_addr_t limit) + pr_debug("%s: reserving %ld MiB for global area\n", __func__, + (unsigned long)selected_size / SZ_1M); + +- dma_contiguous_reserve_area(selected_size, 0, limit, +- &dma_contiguous_default_area); ++ dma_contiguous_reserve_area(selected_size, selected_base, ++ selected_limit, ++ &dma_contiguous_default_area, ++ fixed); + } +-}; ++} + + static DEFINE_MUTEX(cma_mutex); + +@@ -187,15 +207,20 @@ core_initcall(cma_init_reserved_areas); + * @base: Base address of the reserved area optional, use 0 for any + * @limit: End address of the reserved memory (optional, 0 for any). + * @res_cma: Pointer to store the created cma region. ++ * @fixed: hint about where to place the reserved area + * + * This function reserves memory from early allocator. It should be + * called by arch specific code once the early allocator (memblock or bootmem) + * has been activated and all other subsystems have already allocated/reserved + * memory. This function allows to create custom reserved areas for specific + * devices. ++ * ++ * If @fixed is true, reserve contiguous area at exactly @base. If false, ++ * reserve in range from @base to @limit. + */ + int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, +- phys_addr_t limit, struct cma **res_cma) ++ phys_addr_t limit, struct cma **res_cma, ++ bool fixed) + { + struct cma *cma = &cma_areas[cma_area_count]; + phys_addr_t alignment; +@@ -221,18 +246,15 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, + limit &= ~(alignment - 1); + + /* Reserve memory */ +- if (base) { ++ if (base && fixed) { + if (memblock_is_region_reserved(base, size) || + memblock_reserve(base, size) < 0) { + ret = -EBUSY; + goto err; + } + } else { +- /* +- * Use __memblock_alloc_base() since +- * memblock_alloc_base() panic()s. +- */ +- phys_addr_t addr = __memblock_alloc_base(size, alignment, limit); ++ phys_addr_t addr = memblock_alloc_range(size, alignment, base, ++ limit); + if (!addr) { + ret = -ENOMEM; + goto err; +diff --git a/include/linux/dma-contiguous.h b/include/linux/dma-contiguous.h +index 1421a95..5e3f586 100644 +--- a/include/linux/dma-contiguous.h ++++ b/include/linux/dma-contiguous.h +@@ -88,7 +88,8 @@ static inline void dma_contiguous_set_default(struct cma *cma) + void dma_contiguous_reserve(phys_addr_t addr_limit); + + int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, +- phys_addr_t limit, struct cma **res_cma); ++ phys_addr_t limit, struct cma **res_cma, ++ bool fixed); + + /** + * dma_declare_contiguous() - reserve area for contiguous memory handling +@@ -108,7 +109,7 @@ static inline int dma_declare_contiguous(struct device *dev, phys_addr_t size, + { + struct cma *cma; + int ret; +- ret = dma_contiguous_reserve_area(size, base, limit, &cma); ++ ret = dma_contiguous_reserve_area(size, base, limit, &cma, true); + if (ret == 0) + dev_set_cma_area(dev, cma); + +@@ -136,7 +137,9 @@ static inline void dma_contiguous_set_default(struct cma *cma) { } + static inline void dma_contiguous_reserve(phys_addr_t limit) { } + + static inline int dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, +- phys_addr_t limit, struct cma **res_cma) { ++ phys_addr_t limit, struct cma **res_cma, ++ bool fixed) ++{ + return -ENOSYS; + } + +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/cpuidle-menu-Avoid-taking-spinlock-for-accessing-QoS.patch b/kernel-std/centos/patches/cpuidle-menu-Avoid-taking-spinlock-for-accessing-QoS.patch new file mode 100644 index 00000000..c1616108 --- /dev/null +++ b/kernel-std/centos/patches/cpuidle-menu-Avoid-taking-spinlock-for-accessing-QoS.patch @@ -0,0 +1,92 @@ +From c900922b59bc9f2b680f9d1846bf7599d8410de8 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: "Rafael J. Wysocki" +Date: Fri, 24 Feb 2017 13:25:14 +0100 +Subject: [PATCH 21/26] cpuidle: menu: Avoid taking spinlock for accessing QoS + values + +[commit 6dbf5cea05a7098a69f294c96b6d76f08562cae5 from linux-stable ] + +After commit 9908859acaa9 (cpuidle/menu: add per CPU PM QoS resume +latency consideration) the cpuidle menu governor calls +dev_pm_qos_read_value() on CPU devices to read the current resume latency QoS +constraint values for them. That function takes a spinlock to prevent the +device's power.qos pointer from becoming NULL during the access which is a +problem for the RT patchset where spinlocks are converted into mutexes and +the idle loop stops working. + +However, it is not even necessary for the menu governor to take +that spinlock, because the power.qos pointer accessed under it +cannot be modified during the access anyway. + +For this reason, introduce a "raw" routine for accessing device +QoS resume latency constraints without locking and use it in the +menu governor. + +Fixes: 9908859acaa9 (cpuidle/menu: add per CPU PM QoS resume latency consideration) +Acked-by: Alex Shi +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Alex Kozyrev +Signed-off-by: Jim Somerville +--- + drivers/base/power/qos.c | 3 +-- + drivers/cpuidle/governors/menu.c | 2 +- + include/linux/pm_qos.h | 6 ++++++ + 3 files changed, 8 insertions(+), 3 deletions(-) + +diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c +index b2ca302..0cc2a13 100644 +--- a/drivers/base/power/qos.c ++++ b/drivers/base/power/qos.c +@@ -104,8 +104,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_flags); + */ + s32 __dev_pm_qos_read_value(struct device *dev) + { +- return IS_ERR_OR_NULL(dev->power.qos) ? +- 0 : pm_qos_read_value(&dev->power.qos->latency); ++ return dev_pm_qos_raw_read_value(dev); + } + + /** +diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c +index fe2dcb8..f9861fd 100644 +--- a/drivers/cpuidle/governors/menu.c ++++ b/drivers/cpuidle/governors/menu.c +@@ -265,7 +265,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) + int i; + int multiplier; + struct timespec t; +- int resume_latency = dev_pm_qos_read_value(device); ++ int resume_latency = dev_pm_qos_raw_read_value(device); + + if (data->needs_update) { + menu_update(drv, dev); +diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h +index 5281e7f..1d8b629 100644 +--- a/include/linux/pm_qos.h ++++ b/include/linux/pm_qos.h +@@ -217,6 +217,11 @@ static inline s32 dev_pm_qos_requested_flags(struct device *dev) + { + return dev->power.qos->flags_req->data.flr.flags; + } ++static inline s32 dev_pm_qos_raw_read_value(struct device *dev) ++{ ++ return IS_ERR_OR_NULL(dev->power.qos) ? ++ 0 : pm_qos_read_value(&dev->power.qos->latency); ++} + #else + static inline int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) + { return 0; } +@@ -236,6 +241,7 @@ static inline void dev_pm_qos_hide_latency_tolerance(struct device *dev) {} + + static inline s32 dev_pm_qos_requested_latency(struct device *dev) { return 0; } + static inline s32 dev_pm_qos_requested_flags(struct device *dev) { return 0; } ++static inline s32 dev_pm_qos_raw_read_value(struct device *dev) { return 0; } + #endif + + #endif +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/cpuidle-menu-add-per-CPU-PM-QoS-resume-latency-consi.patch b/kernel-std/centos/patches/cpuidle-menu-add-per-CPU-PM-QoS-resume-latency-consi.patch new file mode 100644 index 00000000..87e99897 --- /dev/null +++ b/kernel-std/centos/patches/cpuidle-menu-add-per-CPU-PM-QoS-resume-latency-consi.patch @@ -0,0 +1,71 @@ +From e3d73746230638fd50670d67843346ccaebb885b Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Alex Shi +Date: Thu, 12 Jan 2017 21:27:04 +0800 +Subject: [PATCH 19/26] cpuidle/menu: add per CPU PM QoS resume latency + consideration + +[ commit 9908859acaa95640d4a07991a93f7cd5bfc18e02 from linux-stable ] + +There may be special requirements on CPU response time, like if +a interrupt is pinned to a CPU, that CPU should not go into excessively deep +idle states. For this reason, add a mechanism for adding PM QoS resume +latency constraints for individual CPUs and modify the menu governor to take +them into account. + +To that end, extend the device PM QoS pm_qos_resume_latency attribute +to CPUs, which is possible, because the exit latency for CPUs is +effectively equivalent to the resume latency for devices. + +Signed-off-by: Alex Shi +Acked-by: Rik van Riel +[ rjw : Subject & changelog ] +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Alex Kozyrev + +Signed-off-by: Jim Somerville +--- + drivers/cpuidle/governors/menu.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c +index eb9fb0e..fe2dcb8 100644 +--- a/drivers/cpuidle/governors/menu.c ++++ b/drivers/cpuidle/governors/menu.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include + + #define BUCKETS 12 +@@ -259,10 +260,12 @@ again: + static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) + { + struct menu_device *data = &__get_cpu_var(menu_devices); ++ struct device *device = get_cpu_device(dev->cpu); + int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); + int i; + int multiplier; + struct timespec t; ++ int resume_latency = dev_pm_qos_read_value(device); + + if (data->needs_update) { + menu_update(drv, dev); +@@ -271,6 +274,10 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) + + data->exit_us = 0; + ++ /* resume_latency is 0 means no restriction */ ++ if (resume_latency && resume_latency < latency_req) ++ latency_req = resume_latency; ++ + /* Special case when user has set very strict latency requirement */ + if (unlikely(latency_req == 0)) + return 0; +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/cpuidle-menu-stop-seeking-deeper-idle-if-current-sta.patch b/kernel-std/centos/patches/cpuidle-menu-stop-seeking-deeper-idle-if-current-sta.patch new file mode 100644 index 00000000..be34489b --- /dev/null +++ b/kernel-std/centos/patches/cpuidle-menu-stop-seeking-deeper-idle-if-current-sta.patch @@ -0,0 +1,52 @@ +From 3735f7414216e00f72e08c5d85a98e3b649fc085 Mon Sep 17 00:00:00 2001 +Message-Id: <3735f7414216e00f72e08c5d85a98e3b649fc085.1527544850.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Alex Shi +Date: Thu, 12 Jan 2017 21:27:02 +0800 +Subject: [PATCH 18/26] cpuidle/menu: stop seeking deeper idle if current state + is deep enough + +[ commit 8e37e1a2a3295f5d99e6dbe99eca24eca7a034ef from linux-stable ] + +Obsolete commit 71abbbf856a0 (cpuidle: extend cpuidle and menu +governor to handle dynamic states) wanted to introduce dynamic C-states, but +that idea was dropped long ago. The nonsense deeper C-state checking +remained, though. + +Since both target_residency and exit_latency are longer for deeper +idle state, there's no need to waste CPU time on useless checks. + +Signed-off-by: Alex Shi +Acked-by: Rik van Riel +[ rjw: Subject & changelog ] +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Alex Kozyrev + +Signed-off-by: Jim Somerville +--- + drivers/cpuidle/governors/menu.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c +index c99fee9..eb9fb0e 100644 +--- a/drivers/cpuidle/governors/menu.c ++++ b/drivers/cpuidle/governors/menu.c +@@ -323,11 +323,11 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) + if (s->disabled || su->disable) + continue; + if (s->target_residency > data->predicted_us) +- continue; ++ break; + if (s->exit_latency > latency_req) +- continue; ++ break; + if (s->exit_latency * multiplier > data->predicted_us) +- continue; ++ break; + + data->last_state_idx = i; + data->exit_us = s->exit_latency; +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/dpt_i2o-fix-build-warning.patch b/kernel-std/centos/patches/dpt_i2o-fix-build-warning.patch new file mode 100644 index 00000000..e0777e7e --- /dev/null +++ b/kernel-std/centos/patches/dpt_i2o-fix-build-warning.patch @@ -0,0 +1,44 @@ +From 47b0bf6b68f473392fe04755721f57990a4b111d Mon Sep 17 00:00:00 2001 +Message-Id: <47b0bf6b68f473392fe04755721f57990a4b111d.1527544850.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Sudip Mukherjee +Date: Thu, 18 Feb 2016 13:59:13 +0530 +Subject: [PATCH 25/26] dpt_i2o: fix build warning + +We were getting build warning about: +drivers/scsi/dpt_i2o.c:183:29: warning: 'dptids' defined but not used + +dptids[] is only used in the MODULE_DEVICE_TABLE so when MODULE is not +defined then dptids[] becomes unused. + +Signed-off-by: Sudip Mukherjee +Reviewed-by: Johannes Thumshirn +Signed-off-by: Martin K. Petersen +Signed-off-by: Jim Somerville +--- + drivers/scsi/dpt_i2o.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c +index 2bce881..cb62223 100644 +--- a/drivers/scsi/dpt_i2o.c ++++ b/drivers/scsi/dpt_i2o.c +@@ -180,11 +180,14 @@ static u8 adpt_read_blink_led(adpt_hba* host) + *============================================================================ + */ + ++#ifdef MODULE + static struct pci_device_id dptids[] = { + { PCI_DPT_VENDOR_ID, PCI_DPT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + { PCI_DPT_VENDOR_ID, PCI_DPT_RAPTOR_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + { 0, } + }; ++#endif ++ + MODULE_DEVICE_TABLE(pci,dptids); + + static int adpt_detect(struct scsi_host_template* sht) +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/fix-compilation-issues.patch b/kernel-std/centos/patches/fix-compilation-issues.patch new file mode 100644 index 00000000..ce0e5ea7 --- /dev/null +++ b/kernel-std/centos/patches/fix-compilation-issues.patch @@ -0,0 +1,96 @@ +From d4187124ed859c36b9055cc240cc0c8181e54725 Mon Sep 17 00:00:00 2001 +Message-Id: +From: Jim Somerville +Date: Thu, 31 May 2018 17:47:26 -0400 +Subject: [PATCH 1/1] fix compilation issues + +Signed-off-by: Jim Somerville +--- + arch/x86/mm/kaiser.c | 2 +- + drivers/base/dma-contiguous.c | 2 +- + include/asm-generic/pgtable.h | 20 -------------------- + include/linux/huge_mm.h | 2 +- + 4 files changed, 3 insertions(+), 23 deletions(-) + +diff --git a/arch/x86/mm/kaiser.c b/arch/x86/mm/kaiser.c +index e233c88..5564c8d 100644 +--- a/arch/x86/mm/kaiser.c ++++ b/arch/x86/mm/kaiser.c +@@ -620,7 +620,7 @@ static const struct file_operations fops_kaiser_enabled = { + + static int __init create_kpti_enabled(void) + { +- if (!xen_pv_domain()) ++ if (!is_xen_pv_domain()) + debugfs_create_file("pti_enabled", S_IRUSR | S_IWUSR, + arch_debugfs_dir, NULL, &fops_kaiser_enabled); + return 0; +diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c +index a7d5bda..403101d 100644 +--- a/drivers/base/dma-contiguous.c ++++ b/drivers/base/dma-contiguous.c +@@ -309,7 +309,7 @@ struct page *dma_alloc_from_contiguous(struct device *dev, size_t count, + if (align > CONFIG_CMA_ALIGNMENT) + align = CONFIG_CMA_ALIGNMENT; + +- pr_debug("%s(cma %p, count %d, align %d)\n", __func__, (void *)cma, ++ pr_debug("%s(cma %p, count %zu, align %d)\n", __func__, (void *)cma, + count, align); + + if (!count) +diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h +index 8aa445d..57e6b74 100644 +--- a/include/asm-generic/pgtable.h ++++ b/include/asm-generic/pgtable.h +@@ -629,10 +629,6 @@ static inline int pmd_trans_splitting(pmd_t pmd) + { + return 0; + } +-static inline int pud_trans_huge(pud_t pud) +-{ +- return 0; +-} + static inline int pud_trans_splitting(pud_t pud) + { + return 0; +@@ -644,24 +640,8 @@ static inline int pmd_write(pmd_t pmd) + return 0; + } + #endif /* __HAVE_ARCH_PMD_WRITE */ +-#ifndef __HAVE_ARCH_PUD_WRITE +-static inline int pud_write(pud_t pud) +-{ +- BUG(); +- return 0; +-} +-#endif /* __HAVE_ARCH_PUD_WRITE */ + #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ + +-#if !defined(CONFIG_TRANSPARENT_HUGEPAGE) || \ +- (defined(CONFIG_TRANSPARENT_HUGEPAGE) && \ +- !defined(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD)) +-static inline int pud_trans_huge(pud_t pud) +-{ +- return 0; +-} +-#endif +- + #ifndef pmd_read_atomic + static inline pmd_t pmd_read_atomic(pmd_t *pmdp) + { +diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h +index 26ec982..45af11b 100644 +--- a/include/linux/huge_mm.h ++++ b/include/linux/huge_mm.h +@@ -362,7 +362,7 @@ static inline int pud_trans_huge_lock(pud_t *pud, struct vm_area_struct *vma, + return 0; + } + +-static inline int do_huge_pmd_numa_page(struct vm_fault *vmf, pmd_t orig_pmd); ++static inline int do_huge_pmd_numa_page(struct vm_fault *vmf, pmd_t orig_pmd) + { + return 0; + } +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/intel-iommu-allow-ignoring-Ethernet-device-RMRR-with.patch b/kernel-std/centos/patches/intel-iommu-allow-ignoring-Ethernet-device-RMRR-with.patch new file mode 100644 index 00000000..320a6b74 --- /dev/null +++ b/kernel-std/centos/patches/intel-iommu-allow-ignoring-Ethernet-device-RMRR-with.patch @@ -0,0 +1,120 @@ +From c449e4c490ac85ccf04e8aab67c8120aa48f8ad0 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Matt Peters +Date: Mon, 30 May 2016 10:51:02 -0400 +Subject: [PATCH 08/26] intel-iommu: allow ignoring Ethernet device RMRR with + IOMMU passthrough + +Some BIOS's are reporting DMAR RMRR entries for Ethernet devices +which is causing problems when PCI passthrough is enabled. These +devices should be able to use the static identity map since the +host should not be enforcing specific address ranges when IOMMU +passthrough is enabled. + +Originally-by: Matt Peters +[PG: Added bootarg wrapper and documentation entries.] +Signed-off-by: Paul Gortmaker +Signed-off-by: Nam Ninh + +Signed-off-by: Nam Ninh +Signed-off-by: Jim Somerville +--- + Documentation/Intel-IOMMU.txt | 18 ++++++++++++++++++ + Documentation/kernel-parameters.txt | 5 +++++ + drivers/iommu/intel-iommu.c | 19 +++++++++++++++++++ + 3 files changed, 42 insertions(+) + +diff --git a/Documentation/Intel-IOMMU.txt b/Documentation/Intel-IOMMU.txt +index cf9431d..1dcc349 100644 +--- a/Documentation/Intel-IOMMU.txt ++++ b/Documentation/Intel-IOMMU.txt +@@ -32,6 +32,24 @@ regions will fail. Hence BIOS uses RMRR to specify these regions along with + devices that need to access these regions. OS is expected to setup + unity mappings for these regions for these devices to access these regions. + ++RMRR for other devices? ++----------------------- ++ ++There are reports of BIOS out there that indicate RMRR regions for things ++like ethernet devices. As per mainline commit c875d2c1b8083 ("iommu/vt-d: ++ Exclude devices using RMRRs from IOMMU API domains") such a device is ++"fundamentally incompatible" with the IOMMU API and "we must prevent such ++devices from being used by the IOMMU API." However, in the event that ++the RMRR indicated by the BIOS is assumed to be just a reporting error, ++there is an additional iommu boot arg that can be used to ignore RMRR ++settings for ethernet, i.e. "intel_iommu=on,eth_no_rmrr iommu=pt". ++Note that iommu=pt is required in order to eth_no_rmrr to have effect. ++ ++If you use this setting, you should consult with your hardware vendor to ++confirm that it is just a reporting error, and that it truly is not ++actively using any DMA to/from RMRR, as otherwise system instability ++may result. ++ + How is IOVA generated? + --------------------- + +diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt +index 2f7feb0..590c8c2 100644 +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -1303,6 +1303,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. + than 32-bit addressing. The default is to look + for translation below 32-bit and if not available + then look in the higher range. ++ eth_no_rmrr [Default Off] ++ With this option provided, the kernel will ignore ++ any specified RMRR regions specified by the BIOS ++ for PCI ethernet devices. Confirm with your hardware ++ vendor the RMRR regions are indeed invalid first. + strict [Default Off] + With this option on every unmap_single operation will + result in a hardware IOTLB flush operation as opposed +diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c +index 260597e..6c16b68 100644 +--- a/drivers/iommu/intel-iommu.c ++++ b/drivers/iommu/intel-iommu.c +@@ -504,6 +504,7 @@ static int dmar_forcedac; + static int intel_iommu_strict; + static int intel_iommu_superpage = 1; + static int intel_iommu_ecs = 1; ++static int intel_iommu_ethrmrr = 1; + + /* We only actually use ECS when PASID support (on the new bit 40) + * is also advertised. Some early implementations — the ones with +@@ -563,6 +564,15 @@ static int __init intel_iommu_setup(char *str) + } else if (!strncmp(str, "forcedac", 8)) { + pr_info("Forcing DAC for PCI devices\n"); + dmar_forcedac = 1; ++ } else if (!strncmp(str, "eth_no_rmrr", 11)) { ++ if (!iommu_pass_through) { ++ printk(KERN_WARNING ++ "Intel-IOMMU: error - eth_no_rmrr requires iommu=pt\n"); ++ } else { ++ printk(KERN_INFO ++ "Intel-IOMMU: ignoring ethernet RMRR values\n"); ++ intel_iommu_ethrmrr = 0; ++ } + } else if (!strncmp(str, "strict", 6)) { + pr_info("Disable batched IOTLB flush\n"); + intel_iommu_strict = 1; +@@ -2733,6 +2743,15 @@ static bool device_is_rmrr_locked(struct device *dev) + + if (IS_USB_DEVICE(pdev) || IS_GFX_DEVICE(pdev)) + return false; ++ /* As a temporary workaround for issues seen on ProLiant DL380p, ++ * allow the operator to ignore the RMRR settings for ethernet ++ * devices. Ideally the end user should contact their vendor ++ * regarding why there are RMRR, as per mainline c875d2c1b8083 ++ * ("iommu/vt-d: Exclude devices using RMRRs from IOMMU API domains") ++ * it seems that these make no sense at all. ++ */ ++ if ((pdev->class >> 8) == PCI_CLASS_NETWORK_ETHERNET && !intel_iommu_ethrmrr) ++ return false; + } + + return true; +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/kernel-3.10.0-x86_64.config.tis_extra b/kernel-std/centos/patches/kernel-3.10.0-x86_64.config.tis_extra new file mode 100644 index 00000000..b31cefe3 --- /dev/null +++ b/kernel-std/centos/patches/kernel-3.10.0-x86_64.config.tis_extra @@ -0,0 +1,807 @@ +# Add builtin +CONFIG_MAXSMP=n +CONFIG_NR_CPUS=256 +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_SR=y +CONFIG_SCSI_SAS_ATTRS=y +CONFIG_ISCSI_TCP=y +# SCSI Related Drivers +# Let's enable lots of them, pretty much anything RAID capable +CONFIG_ATA=y +CONFIG_SCSI_SAS_LIBSAS=y +CONFIG_SCSI_SAS_ATA=y +CONFIG_BLK_DEV_3W_XXXX_RAID=y +CONFIG_SCSI_3W_9XXX=y +CONFIG_SCSI_3W_SAS=y +CONFIG_SCSI_AACRAID=y +CONFIG_SCSI_DPT_I2O=y +CONFIG_SCSI_ARCMSR=y +CONFIG_SCSI_HPTIOP=y +CONFIG_SCSI_GDTH=y +CONFIG_SCSI_IPS=y +CONFIG_SCSI_STEX=y +CONFIG_SCSI_PMCRAID=y +CONFIG_SCSI_HPSA=y +CONFIG_MEGARAID_SAS=y +CONFIG_SCSI_SMARTPQI=y +CONFIG_SCSI_MPT2SAS=y +CONFIG_SCSI_MPT3SAS=y +CONFIG_SCSI_VIRTIO=y +CONFIG_FUSION_SAS=y +CONFIG_SCSI_AIC94XX=y +CONFIG_SCSI_MVSAS=y +# +CONFIG_SOFT_WATCHDOG=y +CONFIG_VIRTIO=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_MMIO=y +CONFIG_ISO9660_FS=y +CONFIG_VFAT_FS=y +CONFIG_NLS_ISO8859_1=y +CONFIG_BLK_DEV_DRBD=m +CONFIG_DRBD_FAULT_INJECTION=y +CONFIG_MCORE2=y +CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=0 +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_SIGEXIT=y +CONFIG_SCHEDSTATS=y + +# Enable runtime Huge TLB support +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=16 +CONFIG_CMA_SIZE_SEL_MBYTES=y +CONFIG_CMA_ALIGNMENT=8 +CONFIG_CMA_AREAS=7 + +# Turn on Intel IOMMU +CONFIG_INTEL_IOMMU_DEFAULT_ON=y + +# Turn off network drivers that we want +# to build out-of-tree +CONFIG_E1000E=n +CONFIG_I40E=n +CONFIG_I40EVF=n +CONFIG_IXGB=n +CONFIG_IXGBE=n +CONFIG_IXGBEVF=n + +# Turn off TPM drivers that we want +# to build out-of-tree. This will +# disable the TPM HW-RandomNUmberGenerator(RNG) +# and TrustedKeys modules as well, since +# they require the in-kernel TPM driver. +# Both these modules will also need to be +# built out-of-tree when needed. +CONFIG_TCG_TPM=n +CONFIG_TCG_TIS=n +CONFIG_HW_RANDOM_TPM=n +CONFIG_TRUSTED_KEYS=n +CONFIG_TCG_TIS_I2C_ATMEL=n +CONFIG_TCG_TIS_I2C_INFINEON=n +CONFIG_TCG_TIS_I2C_NUVOTON=n +CONFIG_TCG_NSC=n +CONFIG_TCG_ATMEL=n +CONFIG_TCG_INFINEON=n +CONFIG_TCG_CRB=n +CONFIG_TCG_TIS_ST33ZP24=n +CONFIG_TCG_TIS_ST33ZP24_I2C=n +# Also disable TPM Integrity Measurement Architecture +# (IMA), as this will be built out of tree but ensure +# that all their dependencies (that comes from the base kernel) +# that are marked as "select" within the Integrity / IMA +# Kconfigs are still enabled +CONFIG_INTEGRITY=n +CONFIG_IMA=n +CONFIG_EVM=n +CONFIG_SIGNATURE=y +CONFIG_KEYS=y +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_PUBLIC_KEY_ALGO_RSA=y +CONFIG_X509_CERTIFICATE_PARSER=y +CONFIG_SECURITYFS=y +CONFIG_CRYPTO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA1=y + +# Remove unneeded stuff (including stuff exposed +# by saying y to new options above. +CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=n +CONFIG_PANIC_ON_OOPS=n +CONFIG_SOUND=n +CONFIG_FIREWIRE=n +CONFIG_KPROBES=n +CONFIG_XEN=n +CONFIG_BT=n +CONFIG_INTEL_MEI=n +CONFIG_USB_USBNET=n +CONFIG_MLX4_EN=n +CONFIG_MLX4_CORE=n +CONFIG_MLX5_EN=n +CONFIG_MLX5_CORE=n +CONFIG_RTL8187=n +CONFIG_MWL8K=n +CONFIG_CFG80211=n +CONFIG_WAN=n +CONFIG_ISDN=n +CONFIG_INPUT_TOUCHSCREEN=n +CONFIG_SSB=n +CONFIG_BCMA=n +CONFIG_MEDIA_SUPPORT=n +CONFIG_ACPI_PROCESSOR_AGGREGATOR=n +CONFIG_ACPI_EXTLOG=n +CONFIG_NET_FOU=n +CONFIG_NET_FOU_IP_TUNNELS=n +CONFIG_GENEVE=n +CONFIG_IPV6_VTI=n +CONFIG_NETFILTER_XT_TARGET_TPROXY=n +CONFIG_NETFILTER_XT_MATCH_CGROUP=n +CONFIG_NETFILTER_XT_MATCH_SOCKET=n +CONFIG_NET_SCH_FQ=n +CONFIG_NET_CLS_BPF=n +CONFIG_BLK_DEV_NULL_BLK=n +CONFIG_GENWQE=n +CONFIG_CXL_BASE=n +CONFIG_DM_ERA=n +CONFIG_DM_RAID=n +CONFIG_DM_SWITCH=n +CONFIG_NLMON=n +CONFIG_FM10K=n +CONFIG_CRASH=n +CONFIG_IPMI_SSIF=n +CONFIG_POWERCAP=n +CONFIG_DRM_BOCHS=n +CONFIG_HID_RMI=n +CONFIG_NET_DMA_RH_KABI=n +CONFIG_HP_WIRELESS=n +CONFIG_NFSD_V4_SECURITY_LABEL=n +CONFIG_DEBUG_SHIRQ=n +CONFIG_PERSISTENT_KEYRINGS=n +CONFIG_BIG_KEYS=n +CONFIG_SECURITY_SECURELEVEL=n +CONFIG_CRYPTO_MCRYPTD=n +CONFIG_CRYPTO_SHA1_MB=n +CONFIG_CRYPTO_DRBG_MENU=n +CONFIG_CRYPTO_DEV_QAT=n +CONFIG_CRYPTO_DEV_QAT_DH895xCCVF=n +CONFIG_CRYPTO_DEV_QAT_DH895xCC=n +CONFIG_CRYPTO_DEV_QAT_C3XXX=n +CONFIG_CRYPTO_DEV_QAT_C62X=n +CONFIG_CRYPTO_DEV_QAT_C3XXXVF=n +CONFIG_CRYPTO_DEV_QAT_C62XVF=n +CONFIG_BPF_JIT=n +CONFIG_PARPORT=n +CONFIG_CDROM_PKTCDVD=n +CONFIG_SENSORS_LIS3LV02D=n +CONFIG_SGI_IOC4=n +CONFIG_TIFM_CORE=n +CONFIG_ENCLOSURE_SERVICES=n +CONFIG_APDS9802ALS=n +CONFIG_ISL29003=n +CONFIG_ISL29020=n +CONFIG_SENSORS_TSL2550=n +CONFIG_SENSORS_BH1770=n +CONFIG_SENSORS_APDS990X=n +CONFIG_PCH_PHUB=n +CONFIG_EEPROM_AT24=n +CONFIG_EEPROM_MAX6875=n +CONFIG_EEPROM_93CX6=n +CONFIG_CB710_CORE=n +CONFIG_SENSORS_LIS3_I2C=n +CONFIG_ALTERA_STAPL=n +CONFIG_VMWARE_VMCI=n +CONFIG_CHR_DEV_ST=n +CONFIG_CHR_DEV_OSST=n +CONFIG_CHR_DEV_SG=n +CONFIG_CHR_DEV_SCH=n +CONFIG_SCSI_CONSTANTS=n +CONFIG_SCSI_LOGGING=n +CONFIG_SCSI_CXGB3_ISCSI=n +CONFIG_SCSI_CXGB4_ISCSI=n +CONFIG_SCSI_AIC79XX=n +CONFIG_SCSI_MVUMI=n +CONFIG_SCSI_MPT2SAS_LOGGING=n +CONFIG_SCSI_MPT3SAS_LOGGING=n +CONFIG_SCSI_UFSHCD=n +CONFIG_VMWARE_PVSCSI=n +CONFIG_FCOE=n +CONFIG_FCOE_FNIC=n +CONFIG_SCSI_INITIO=n +CONFIG_SCSI_PM8001=n +CONFIG_SCSI_SRP=n +CONFIG_SATA_ACARD_AHCI=n +CONFIG_SATA_SIL24=n +CONFIG_PDC_ADMA=n +CONFIG_SATA_QSTOR=n +CONFIG_SATA_SX4=n +CONFIG_SATA_MV=n +CONFIG_SATA_NV=n +CONFIG_SATA_PROMISE=n +CONFIG_SATA_SIL=n +CONFIG_SATA_SIS=n +CONFIG_SATA_SVW=n +CONFIG_SATA_ULI=n +CONFIG_SATA_VIA=n +CONFIG_SATA_VITESSE=n +CONFIG_PATA_ALI=n +CONFIG_PATA_AMD=n +CONFIG_PATA_ARASAN_CF=n +CONFIG_PATA_ARTOP=n +CONFIG_PATA_ATIIXP=n +CONFIG_PATA_ATP867X=n +CONFIG_PATA_CMD64X=n +CONFIG_PATA_CS5536=n +CONFIG_PATA_HPT366=n +CONFIG_PATA_HPT37X=n +CONFIG_PATA_HPT3X2N=n +CONFIG_PATA_HPT3X3=n +CONFIG_PATA_IT8213=n +CONFIG_PATA_IT821X=n +CONFIG_PATA_JMICRON=n +CONFIG_PATA_MARVELL=n +CONFIG_PATA_NETCELL=n +CONFIG_PATA_NINJA32=n +CONFIG_PATA_OLDPIIX=n +CONFIG_PATA_PDC2027X=n +CONFIG_PATA_PDC_OLD=n +CONFIG_PATA_RDC=n +CONFIG_PATA_SCH=n +CONFIG_PATA_SERVERWORKS=n +CONFIG_PATA_SIL680=n +CONFIG_PATA_SIS=n +CONFIG_PATA_TOSHIBA=n +CONFIG_PATA_VIA=n +CONFIG_DM_DEBUG=n +CONFIG_MACINTOSH_DRIVERS=n +CONFIG_NET_FC=n +CONFIG_NET_TEAM=n +CONFIG_ATL2=n +CONFIG_ATL1=n +CONFIG_ATL1E=n +CONFIG_ATL1C=n +CONFIG_ALX=n +CONFIG_ARM_AT91_ETHER=n +CONFIG_MACB=n +CONFIG_B44=n +CONFIG_BNA=n +CONFIG_NET_CALXEDA_XGMAC=n +CONFIG_CHELSIO_T3=n +CONFIG_CHELSIO_T4=n +CONFIG_CHELSIO_T4VF=n +CONFIG_NET_TULIP=n +CONFIG_IP1000=n +CONFIG_JME=n +CONFIG_MVMDIO=n +CONFIG_SKGE=n +CONFIG_SKY2=n +CONFIG_MYRI10GE=n +CONFIG_PCH_GBE=n +CONFIG_ETHOC=n +CONFIG_QLCNIC=n +CONFIG_QLGE=n +CONFIG_NETXEN_NIC=n +CONFIG_SFC=n +CONFIG_EPIC100=n +CONFIG_SMSC9420=n +CONFIG_AT803X_PHY=n +CONFIG_DAVICOM_PHY=n +CONFIG_QSEMI_PHY=n +CONFIG_LXT_PHY=n +CONFIG_CICADA_PHY=n +CONFIG_VITESSE_PHY=n +CONFIG_SMSC_PHY=n +CONFIG_BCM87XX_PHY=n +CONFIG_ICPLUS_PHY=n +CONFIG_REALTEK_PHY=n +CONFIG_NATIONAL_PHY=n +CONFIG_STE10XP=n +CONFIG_LSI_ET1011C_PHY=n +CONFIG_MICREL_PHY=n +CONFIG_MDIO_BITBANG=n +CONFIG_RT_GROUP_SCHED=n +CONFIG_OPROFILE=n +CONFIG_SYSTEM_BLACKLIST_KEYRING=n +CONFIG_OSF_PARTITION=n +CONFIG_AMIGA_PARTITION=n +CONFIG_MAC_PARTITION=n +CONFIG_BSD_DISKLABEL=n +CONFIG_MINIX_SUBPARTITION=n +CONFIG_SOLARIS_X86_PARTITION=n +CONFIG_UNIXWARE_DISKLABEL=n +CONFIG_SGI_PARTITION=n +CONFIG_SUN_PARTITION=n +CONFIG_KARMA_PARTITION=n +CONFIG_X86_UV=n +CONFIG_I8K=n +CONFIG_MICROCODE_AMD=n +CONFIG_MICROCODE_AMD_EARLY=n +CONFIG_MOVABLE_NODE=n +CONFIG_MEMORY_HOTPLUG=n +CONFIG_BOOTPARAM_HOTPLUG_CPU0=n +CONFIG_X86_POWERNOW_K8=n +CONFIG_X86_AMD_FREQ_SENSITIVITY=n +CONFIG_X86_P4_CLOCKMOD=n +CONFIG_PCIE_ECRC=n +CONFIG_PCIEAER_INJECT=n +CONFIG_PCCARD=n +CONFIG_HOTPLUG_PCI_ACPI_IBM=n +CONFIG_HOTPLUG_PCI_SHPC=n +CONFIG_XFRM_STATISTICS=n +CONFIG_IP_FIB_TRIE_STATS=n +CONFIG_PPP_MPPE=n +CONFIG_USB_CATC=n +CONFIG_USB_KAWETH=n +CONFIG_USB_PEGASUS=n +CONFIG_USB_RTL8150=n +CONFIG_USB_RTL8152=n +CONFIG_USB_HSO=n +CONFIG_USB_IPHETH=n +CONFIG_INPUT_FF_MEMLESS=n +CONFIG_INPUT_POLLDEV=n +CONFIG_INPUT_SPARSEKMAP=n +CONFIG_MOUSE_PS2_ELANTECH=n +CONFIG_MOUSE_PS2_SENTELIC=n +CONFIG_MOUSE_APPLETOUCH=n +CONFIG_MOUSE_BCM5974=n +CONFIG_MOUSE_CYAPA=n +CONFIG_MOUSE_VSXXXAA=n +CONFIG_MOUSE_SYNAPTICS_I2C=n +CONFIG_MOUSE_SYNAPTICS_USB=n +CONFIG_INPUT_TABLET=n +CONFIG_INPUT_PCSPKR=n +CONFIG_INPUT_APANEL=n +CONFIG_INPUT_ATLAS_BTNS=n +CONFIG_INPUT_ATI_REMOTE2=n +CONFIG_INPUT_KEYSPAN_REMOTE=n +CONFIG_INPUT_POWERMATE=n +CONFIG_INPUT_YEALINK=n +CONFIG_INPUT_CM109=n +CONFIG_INPUT_UINPUT=n +CONFIG_SERIO_ALTERA_PS2=n +CONFIG_SERIO_ARC_PS2=n +CONFIG_NOZOMI=n +CONFIG_N_GSM=n +CONFIG_SERIAL_JSM=n +CONFIG_SERIAL_ARC=n +CONFIG_HANGCHECK_TIMER=n +CONFIG_TELCLOCK=n +CONFIG_I2C_AMD756=n +CONFIG_I2C_AMD8111=n +CONFIG_I2C_PIIX4=n +CONFIG_I2C_NFORCE2=n +CONFIG_I2C_SIS96X=n +CONFIG_I2C_VIA=n +CONFIG_I2C_VIAPRO=n +CONFIG_I2C_PCA_PLATFORM=n +CONFIG_I2C_SIMTEC=n +CONFIG_I2C_DIOLAN_U2C=n +CONFIG_I2C_PARPORT_LIGHT=n +CONFIG_PPS_CLIENT_LDISC=n +CONFIG_PPS_CLIENT_GPIO=n +CONFIG_PTP_1588_CLOCK_PCH=n +CONFIG_CHARGER_SMB347=n +CONFIG_SENSORS_ABITUGURU=n +CONFIG_SENSORS_ABITUGURU3=n +CONFIG_SENSORS_AD7414=n +CONFIG_SENSORS_AD7418=n +CONFIG_SENSORS_ADM1021=n +CONFIG_SENSORS_ADM1025=n +CONFIG_SENSORS_ADM1026=n +CONFIG_SENSORS_ADM1029=n +CONFIG_SENSORS_ADM1031=n +CONFIG_SENSORS_ADM9240=n +CONFIG_SENSORS_ADT7410=n +CONFIG_SENSORS_ADT7411=n +CONFIG_SENSORS_ADT7462=n +CONFIG_SENSORS_ADT7470=n +CONFIG_SENSORS_ADT7475=n +CONFIG_SENSORS_ASC7621=n +CONFIG_SENSORS_K8TEMP=n +CONFIG_SENSORS_K10TEMP=n +CONFIG_SENSORS_FAM15H_POWER=n +CONFIG_SENSORS_ASB100=n +CONFIG_SENSORS_ATXP1=n +CONFIG_SENSORS_DS620=n +CONFIG_SENSORS_DS1621=n +CONFIG_SENSORS_I5K_AMB=n +CONFIG_SENSORS_F71805F=n +CONFIG_SENSORS_F71882FG=n +CONFIG_SENSORS_F75375S=n +CONFIG_SENSORS_FSCHMD=n +CONFIG_SENSORS_G760A=n +CONFIG_SENSORS_GL518SM=n +CONFIG_SENSORS_GL520SM=n +CONFIG_SENSORS_IBMAEM=n +CONFIG_SENSORS_IBMPEX=n +CONFIG_SENSORS_IT87=n +CONFIG_SENSORS_LINEAGE=n +CONFIG_SENSORS_LM63=n +CONFIG_SENSORS_LM73=n +CONFIG_SENSORS_LM75=n +CONFIG_SENSORS_LM77=n +CONFIG_SENSORS_LM78=n +CONFIG_SENSORS_LM80=n +CONFIG_SENSORS_LM83=n +CONFIG_SENSORS_LM85=n +CONFIG_SENSORS_LM87=n +CONFIG_SENSORS_LM90=n +CONFIG_SENSORS_LM92=n +CONFIG_SENSORS_LM93=n +CONFIG_SENSORS_LTC4151=n +CONFIG_SENSORS_LTC4215=n +CONFIG_SENSORS_LTC4245=n +CONFIG_SENSORS_LTC4261=n +CONFIG_SENSORS_LM95234=n +CONFIG_SENSORS_LM95241=n +CONFIG_SENSORS_LM95245=n +CONFIG_SENSORS_MAX16065=n +CONFIG_SENSORS_MAX1619=n +CONFIG_SENSORS_MAX1668=n +CONFIG_SENSORS_MAX197=n +CONFIG_SENSORS_MAX6639=n +CONFIG_SENSORS_MAX6642=n +CONFIG_SENSORS_MAX6650=n +CONFIG_SENSORS_MAX6697=n +CONFIG_SENSORS_MCP3021=n +CONFIG_SENSORS_NCT6775=n +CONFIG_SENSORS_NTC_THERMISTOR=n +CONFIG_SENSORS_PC87360=n +CONFIG_SENSORS_PC87427=n +CONFIG_SENSORS_PCF8591=n +CONFIG_SENSORS_PMBUS=n +CONFIG_SENSORS_ADM1275=n +CONFIG_SENSORS_LM25066=n +CONFIG_SENSORS_LTC2978=n +CONFIG_SENSORS_MAX16064=n +CONFIG_SENSORS_MAX34440=n +CONFIG_SENSORS_MAX8688=n +CONFIG_SENSORS_UCD9000=n +CONFIG_SENSORS_UCD9200=n +CONFIG_SENSORS_ZL6100=n +CONFIG_SENSORS_SHT21=n +CONFIG_SENSORS_SIS5595=n +CONFIG_SENSORS_DME1737=n +CONFIG_SENSORS_EMC1403=n +CONFIG_SENSORS_EMC6W201=n +CONFIG_SENSORS_SMSC47M1=n +CONFIG_SENSORS_SMSC47M192=n +CONFIG_SENSORS_SMSC47B397=n +CONFIG_SENSORS_SCH56XX_COMMON=n +CONFIG_SENSORS_SCH5627=n +CONFIG_SENSORS_SCH5636=n +CONFIG_SENSORS_ADS1015=n +CONFIG_SENSORS_ADS7828=n +CONFIG_SENSORS_AMC6821=n +CONFIG_SENSORS_INA209=n +CONFIG_SENSORS_INA2XX=n +CONFIG_SENSORS_THMC50=n +CONFIG_SENSORS_TMP102=n +CONFIG_SENSORS_TMP401=n +CONFIG_SENSORS_TMP421=n +CONFIG_SENSORS_VIA_CPUTEMP=n +CONFIG_SENSORS_VIA686A=n +CONFIG_SENSORS_VT1211=n +CONFIG_SENSORS_VT8231=n +CONFIG_SENSORS_W83781D=n +CONFIG_SENSORS_W83791D=n +CONFIG_SENSORS_W83792D=n +CONFIG_SENSORS_W83793=n +CONFIG_SENSORS_W83795=n +CONFIG_SENSORS_W83L785TS=n +CONFIG_SENSORS_W83L786NG=n +CONFIG_SENSORS_W83627HF=n +CONFIG_SENSORS_W83627EHF=n +CONFIG_SENSORS_APPLESMC=n +CONFIG_SENSORS_ATK0110=n +CONFIG_ALIM1535_WDT=n +CONFIG_ALIM7101_WDT=n +CONFIG_F71808E_WDT=n +CONFIG_SP5100_TCO=n +CONFIG_SBC_FITPC2_WATCHDOG=n +CONFIG_IB700_WDT=n +CONFIG_IBMASR=n +CONFIG_IT8712F_WDT=n +CONFIG_IT87_WDT=n +CONFIG_NV_TCO=n +CONFIG_SMSC_SCH311X_WDT=n +CONFIG_VIA_WDT=n +CONFIG_W83627HF_WDT=n +CONFIG_W83697HF_WDT=n +CONFIG_W83697UG_WDT=n +CONFIG_W83877F_WDT=n +CONFIG_W83977F_WDT=n +CONFIG_MACHZ_WDT=n +CONFIG_PCIPCWATCHDOG=n +CONFIG_WDTPCI=n +CONFIG_USBPCWATCHDOG=n +CONFIG_MFD_VIPERBOARD=n +CONFIG_MFD_SM501=n +CONFIG_MFD_VX855=n +CONFIG_AGP_AMD64=n +CONFIG_AGP_SIS=n +CONFIG_AGP_VIA=n +CONFIG_VGA_SWITCHEROO=n +CONFIG_DRM_LOAD_EDID_FIRMWARE=n +CONFIG_DRM_I2C_CH7006=n +CONFIG_DRM_I2C_SIL164=n +CONFIG_DRM_I2C_NXP_TDA998X=n +CONFIG_DRM_RADEON=n +CONFIG_DRM_NOUVEAU=n +CONFIG_DRM_VMWGFX=n +CONFIG_DRM_GMA500=n +CONFIG_DRM_GMA600=n +CONFIG_DRM_GMA3600=n +CONFIG_DRM_UDL=n +CONFIG_DRM_AST=n +CONFIG_DRM_MGAG200=n +CONFIG_DRM_CIRRUS_QEMU=n +CONFIG_DRM_QXL=n +CONFIG_DRM_BOCHS=n +CONFIG_FB_SYS_FILLRECT=n +CONFIG_FB_SYS_COPYAREA=n +CONFIG_FB_SYS_IMAGEBLIT=n +CONFIG_FB_SYS_FOPS=n +CONFIG_FB_BACKLIGHT=n +CONFIG_LCD_PLATFORM=n +CONFIG_BACKLIGHT_APPLE=n +CONFIG_BACKLIGHT_LP855X=n +CONFIG_VGACON_SOFT_SCROLLBACK=n +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=n +CONFIG_LOGO=n +CONFIG_HID_BATTERY_STRENGTH=n +CONFIG_HIDRAW=n +CONFIG_UHID=n +CONFIG_HID_ACRUX=n +CONFIG_HID_APPLEIR=n +CONFIG_HID_AUREAL=n +CONFIG_HID_DRAGONRISE=n +CONFIG_HID_ELECOM=n +CONFIG_HID_HOLTEK=n +CONFIG_HID_KEYTOUCH=n +CONFIG_HID_KYE=n +CONFIG_HID_UCLOGIC=n +CONFIG_HID_WALTOP=n +CONFIG_HID_GYRATION=n +CONFIG_HID_ICADE=n +CONFIG_HID_TWINHAN=n +CONFIG_HID_LCPOWER=n +CONFIG_HID_LENOVO_TPKBD=n +CONFIG_HID_MAGICMOUSE=n +CONFIG_HID_NTRIG=n +CONFIG_HID_ORTEK=n +CONFIG_HID_PANTHERLORD=n +CONFIG_HID_PETALYNX=n +CONFIG_HID_PICOLCD=n +CONFIG_HID_PRIMAX=n +CONFIG_HID_PS3REMOTE=n +CONFIG_HID_ROCCAT=n +CONFIG_HID_SAITEK=n +CONFIG_HID_SAMSUNG=n +CONFIG_HID_SONY=n +CONFIG_HID_SPEEDLINK=n +CONFIG_HID_STEELSERIES=n +CONFIG_HID_SUNPLUS=n +CONFIG_HID_RMI=n +CONFIG_HID_GREENASIA=n +CONFIG_HID_SMARTJOYPLUS=n +CONFIG_HID_TIVO=n +CONFIG_HID_TOPSEED=n +CONFIG_HID_THINGM=n +CONFIG_HID_THRUSTMASTER=n +CONFIG_HID_WACOM=n +CONFIG_HID_WIIMOTE=n +CONFIG_HID_ZEROPLUS=n +CONFIG_HID_ZYDACRON=n +CONFIG_HID_PID=n +CONFIG_USB_HIDDEV=n +CONFIG_USB_ANNOUNCE_NEW_DEVICES=n +CONFIG_USB_MON=n +CONFIG_USB_WUSB_CBAF=n +CONFIG_USB_OHCI_HCD=n +CONFIG_USB_ACM=n +CONFIG_USB_PRINTER=n +CONFIG_USB_WDM=n +CONFIG_USB_TMC=n +CONFIG_USB_STORAGE_REALTEK=n +CONFIG_USB_STORAGE_DATAFAB=n +CONFIG_USB_STORAGE_FREECOM=n +CONFIG_USB_STORAGE_ISD200=n +CONFIG_USB_STORAGE_USBAT=n +CONFIG_USB_STORAGE_SDDR09=n +CONFIG_USB_STORAGE_SDDR55=n +CONFIG_USB_STORAGE_JUMPSHOT=n +CONFIG_USB_STORAGE_ALAUDA=n +CONFIG_USB_STORAGE_ONETOUCH=n +CONFIG_USB_STORAGE_KARMA=n +CONFIG_USB_STORAGE_CYPRESS_ATACB=n +CONFIG_USB_STORAGE_ENE_UB6250=n +CONFIG_USB_MDC800=n +CONFIG_USB_MICROTEK=n +CONFIG_USB_SERIAL_AIRCABLE=n +CONFIG_USB_SERIAL_ARK3116=n +CONFIG_USB_SERIAL_BELKIN=n +CONFIG_USB_SERIAL_WHITEHEAT=n +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=n +CONFIG_USB_SERIAL_CYPRESS_M8=n +CONFIG_USB_SERIAL_EMPEG=n +CONFIG_USB_SERIAL_VISOR=n +CONFIG_USB_SERIAL_IPAQ=n +CONFIG_USB_SERIAL_IR=n +CONFIG_USB_SERIAL_EDGEPORT=n +CONFIG_USB_SERIAL_EDGEPORT_TI=n +CONFIG_USB_SERIAL_GARMIN=n +CONFIG_USB_SERIAL_IPW=n +CONFIG_USB_SERIAL_IUU=n +CONFIG_USB_SERIAL_KEYSPAN_PDA=n +CONFIG_USB_SERIAL_KEYSPAN=n +CONFIG_USB_SERIAL_KLSI=n +CONFIG_USB_SERIAL_KOBIL_SCT=n +CONFIG_USB_SERIAL_MCT_U232=n +CONFIG_USB_SERIAL_MOS7720=n +CONFIG_USB_SERIAL_MOS7840=n +CONFIG_USB_SERIAL_NAVMAN=n +CONFIG_USB_SERIAL_OTI6858=n +CONFIG_USB_SERIAL_QCAUX=n +CONFIG_USB_SERIAL_QUALCOMM=n +CONFIG_USB_SERIAL_SPCP8X5=n +CONFIG_USB_SERIAL_SAFE=n +CONFIG_USB_SERIAL_SIERRAWIRELESS=n +CONFIG_USB_SERIAL_SYMBOL=n +CONFIG_USB_SERIAL_TI=n +CONFIG_USB_SERIAL_CYBERJACK=n +CONFIG_USB_SERIAL_XIRCOM=n +CONFIG_USB_SERIAL_OPTION=n +CONFIG_USB_SERIAL_OMNINET=n +CONFIG_USB_SERIAL_OPTICON=n +CONFIG_USB_SERIAL_XSENS_MT=n +CONFIG_USB_SERIAL_SSU100=n +CONFIG_USB_SERIAL_QT2=n +CONFIG_USB_SERIAL_DEBUG=n +CONFIG_USB_EMI62=n +CONFIG_USB_EMI26=n +CONFIG_USB_ADUTUX=n +CONFIG_USB_SEVSEG=n +CONFIG_USB_LEGOTOWER=n +CONFIG_USB_LCD=n +CONFIG_USB_LED=n +CONFIG_USB_IDMOUSE=n +CONFIG_USB_FTDI_ELAN=n +CONFIG_USB_APPLEDISPLAY=n +CONFIG_USB_SISUSBVGA=n +CONFIG_USB_LD=n +CONFIG_USB_IOWARRIOR=n +CONFIG_USB_ISIGHTFW=n +CONFIG_USB_EZUSB_FX2=n +CONFIG_USB_HSIC_USB3503=n +CONFIG_USB_ATM=n +CONFIG_UWB=n +CONFIG_MMC_RICOH_MMC=n +CONFIG_MMC_TIFM_SD=n +CONFIG_MMC_CB710=n +CONFIG_MMC_VIA_SDMMC=n +CONFIG_MMC_VUB300=n +CONFIG_MMC_USHC=n +CONFIG_MEMSTICK=n +CONFIG_LEDS_LM3530=n +CONFIG_LEDS_LP3944=n +CONFIG_LEDS_LP5521=n +CONFIG_LEDS_LP5523=n +CONFIG_LEDS_LP5562=n +CONFIG_LEDS_CLEVO_MAIL=n +CONFIG_LEDS_INTEL_SS4200=n +CONFIG_LEDS_BLINKM=n +CONFIG_LEDS_TRIGGER_TIMER=n +CONFIG_LEDS_TRIGGER_ONESHOT=n +CONFIG_LEDS_TRIGGER_HEARTBEAT=n +CONFIG_LEDS_TRIGGER_BACKLIGHT=n +CONFIG_LEDS_TRIGGER_TRANSIENT=n +CONFIG_LEDS_TRIGGER_CAMERA=n +CONFIG_INFINIBAND=n +CONFIG_EDAC=n +CONFIG_NET_DMA_RH_KABI=n +CONFIG_UIO_CIF=n +CONFIG_UIO_AEC=n +CONFIG_UIO_SERCOS3=n +CONFIG_STAGING=n +CONFIG_ACERHDF=n +CONFIG_ASUS_LAPTOP=n +CONFIG_CHROMEOS_LAPTOP=n +CONFIG_FUJITSU_LAPTOP=n +CONFIG_FUJITSU_TABLET=n +CONFIG_AMILO_RFKILL=n +CONFIG_HP_ACCEL=n +CONFIG_HP_WIRELESS=n +CONFIG_MSI_LAPTOP=n +CONFIG_PANASONIC_LAPTOP=n +CONFIG_COMPAL_LAPTOP=n +CONFIG_SONY_LAPTOP=n +CONFIG_IDEAPAD_LAPTOP=n +CONFIG_THINKPAD_ACPI=n +CONFIG_SENSORS_HDAPS=n +CONFIG_EEEPC_LAPTOP=n +CONFIG_ACPI_WMI=n +CONFIG_TOPSTAR_LAPTOP=n +CONFIG_TOSHIBA_BT_RFKILL=n +CONFIG_ACPI_CMPC=n +CONFIG_SAMSUNG_LAPTOP=n +CONFIG_INTEL_OAKTRAIL=n +CONFIG_SAMSUNG_Q10=n +CONFIG_APPLE_GMUX=n +CONFIG_PVPANIC=n +CONFIG_EDD=n +CONFIG_DELL_RBU=n +CONFIG_DCDBAS=n +CONFIG_JBD_DEBUG=n +CONFIG_FANOTIFY=n +CONFIG_JOLIET=n +CONFIG_UDF_FS=n +CONFIG_CRAMFS=n +CONFIG_SQUASHFS=y +CONFIG_EFIVAR_FS=n +CONFIG_SUNRPC_DEBUG=n +CONFIG_NLS_MAC_ROMAN=n +CONFIG_NLS_MAC_CELTIC=n +CONFIG_NLS_MAC_CENTEURO=n +CONFIG_NLS_MAC_CROATIAN=n +CONFIG_NLS_MAC_CYRILLIC=n +CONFIG_NLS_MAC_GAELIC=n +CONFIG_NLS_MAC_GREEK=n +CONFIG_NLS_MAC_ICELAND=n +CONFIG_NLS_MAC_INUIT=n +CONFIG_NLS_MAC_ROMANIAN=n +CONFIG_NLS_MAC_TURKISH=n +CONFIG_SCHED_TRACER=n +CONFIG_TRACER_SNAPSHOT=n +CONFIG_UPROBE_EVENT=n +CONFIG_PROBE_EVENTS=n +CONFIG_FUNCTION_PROFILER=n +CONFIG_RING_BUFFER_BENCHMARK=n +CONFIG_ATOMIC64_SELFTEST=n +CONFIG_ASYNC_RAID6_TEST=n +CONFIG_KGDB=n +CONFIG_TEST_KSTRTOX=n +CONFIG_STRICT_DEVMEM=n +CONFIG_DEBUG_SET_MODULE_RONX=n +CONFIG_DEBUG_NX_TEST=n +CONFIG_CRYPTO_BLOWFISH_X86_64=n +CONFIG_CRYPTO_CAMELLIA_X86_64=n +CONFIG_CRYPTO_CAMELLIA_AESNI_AVX_X86_64=n +CONFIG_CRYPTO_CAMELLIA_AESNI_AVX2_X86_64=n +CONFIG_CRYPTO_CAST5_AVX_X86_64=n +CONFIG_CRYPTO_CAST6_AVX_X86_64=n +CONFIG_CRYPTO_SALSA20=n +CONFIG_CRYPTO_SALSA20_X86_64=n +CONFIG_CRYPTO_SERPENT_SSE2_X86_64=n +CONFIG_CRYPTO_SERPENT_AVX_X86_64=n +CONFIG_CRYPTO_SERPENT_AVX2_X86_64=n +CONFIG_CRYPTO_TWOFISH_X86_64=n +CONFIG_CRYPTO_TWOFISH_X86_64_3WAY=n +CONFIG_CRYPTO_TWOFISH_AVX_X86_64=n +CONFIG_CRYPTO_DRBG_MENU=n +CONFIG_CRYPTO_USER_API_HASH=n +CONFIG_CRYPTO_USER_API_SKCIPHER=n +CONFIG_CRYPTO_DEV_PADLOCK=n +CONFIG_BLK_DEV_NBD=m +CONFIG_AIC94XX_DEBUG=n + +# Turn on kernel preemption +CONFIG_PREEMPT=y +CONFIG_RCU_BOOST=n +CONFIG_DEBUG_PREEMPT=n +CONFIG_PROVE_RCU_DELAY=n +CONFIG_RCU_CPU_STALL_VERBOSE=n +CONFIG_PREEMPT_TRACER=n + +# Disable transparent huge pages +CONFIG_TRANSPARENT_HUGEPAGE=n +CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=n + +# Make performance default governor +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=n +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y + +CONFIG_GPIO_BT8XX=n + +CONFIG_KERNFS=y +CONFIG_INTEL_RDT_A=y diff --git a/kernel-std/centos/patches/memblock-introduce-memblock_alloc_range.patch b/kernel-std/centos/patches/memblock-introduce-memblock_alloc_range.patch new file mode 100644 index 00000000..a4be2b02 --- /dev/null +++ b/kernel-std/centos/patches/memblock-introduce-memblock_alloc_range.patch @@ -0,0 +1,96 @@ +From b4007dff3d6d7fc27d8b3431213202fb9c34793d Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Akinobu Mita +Date: Tue, 31 May 2016 16:07:55 -0400 +Subject: [PATCH 09/26] memblock: introduce memblock_alloc_range() + +Commit 2bfc2862c4fe38379a2fb2cfba33fad32ccb4ff4 upstream +Backported-by: Nam Ninh + +This introduces memblock_alloc_range() which allocates memblock from the +specified range of physical address. I would like to use this function +to specify the location of CMA. + +Signed-off-by: Akinobu Mita +Cc: Marek Szyprowski +Cc: Konrad Rzeszutek Wilk +Cc: David Woodhouse +Cc: Don Dutile +Cc: Thomas Gleixner +Cc: Ingo Molnar +Cc: "H. Peter Anvin" +Cc: Andi Kleen +Cc: Yinghai Lu +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +Signed-off-by: Jim Somerville +--- + include/linux/memblock.h | 2 ++ + mm/memblock.c | 22 ++++++++++++++++++---- + 2 files changed, 20 insertions(+), 4 deletions(-) + +diff --git a/include/linux/memblock.h b/include/linux/memblock.h +index 5a439c9..d6bcbef 100644 +--- a/include/linux/memblock.h ++++ b/include/linux/memblock.h +@@ -304,6 +304,8 @@ static inline bool memblock_bottom_up(void) { return false; } + #define MEMBLOCK_ALLOC_ANYWHERE (~(phys_addr_t)0) + #define MEMBLOCK_ALLOC_ACCESSIBLE 0 + ++phys_addr_t __init memblock_alloc_range(phys_addr_t size, phys_addr_t align, ++ phys_addr_t start, phys_addr_t end); + phys_addr_t memblock_alloc_base(phys_addr_t size, phys_addr_t align, + phys_addr_t max_addr); + phys_addr_t __memblock_alloc_base(phys_addr_t size, phys_addr_t align, +diff --git a/mm/memblock.c b/mm/memblock.c +index fbc8071..ff910a4 100644 +--- a/mm/memblock.c ++++ b/mm/memblock.c +@@ -1120,9 +1120,9 @@ int __init_memblock memblock_set_node(phys_addr_t base, phys_addr_t size, + } + #endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */ + +-static phys_addr_t __init memblock_alloc_base_nid(phys_addr_t size, +- phys_addr_t align, phys_addr_t max_addr, +- int nid, ulong flags) ++static phys_addr_t __init memblock_alloc_range_nid(phys_addr_t size, ++ phys_addr_t align, phys_addr_t start, ++ phys_addr_t end, int nid, ulong flags) + { + phys_addr_t found; + +@@ -1132,7 +1132,7 @@ static phys_addr_t __init memblock_alloc_base_nid(phys_addr_t size, + /* align @size to avoid excessive fragmentation on reserved array */ + size = round_up(size, align); + +- found = memblock_find_in_range_node(size, align, 0, max_addr, nid, ++ found = memblock_find_in_range_node(size, align, start, end, nid, + flags); + if (found && !memblock_reserve(found, size)) + return found; +@@ -1140,6 +1140,20 @@ static phys_addr_t __init memblock_alloc_base_nid(phys_addr_t size, + return 0; + } + ++phys_addr_t __init memblock_alloc_range(phys_addr_t size, phys_addr_t align, ++ phys_addr_t start, phys_addr_t end) ++{ ++ ulong flags = choose_memblock_flags(); ++ return memblock_alloc_range_nid(size, align, start, end, NUMA_NO_NODE, flags); ++} ++ ++static phys_addr_t __init memblock_alloc_base_nid(phys_addr_t size, ++ phys_addr_t align, phys_addr_t max_addr, ++ int nid, ulong flags) ++{ ++ return memblock_alloc_range_nid(size, align, 0, max_addr, nid, flags); ++} ++ + phys_addr_t __init memblock_alloc_nid(phys_addr_t size, phys_addr_t align, int nid) + { + ulong flags = choose_memblock_flags(); +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/rcu-Don-t-wake-rcuc-X-kthreads-on-NOCB-CPUs.patch b/kernel-std/centos/patches/rcu-Don-t-wake-rcuc-X-kthreads-on-NOCB-CPUs.patch new file mode 100644 index 00000000..d0991fa8 --- /dev/null +++ b/kernel-std/centos/patches/rcu-Don-t-wake-rcuc-X-kthreads-on-NOCB-CPUs.patch @@ -0,0 +1,47 @@ +From fe5869e78860a9150e24ea32c1a131da6af057c8 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: "Paul E. McKenney" +Date: Thu, 15 Dec 2016 15:37:47 -0800 +Subject: [PATCH 15/26] rcu: Don't wake rcuc/X kthreads on NOCB CPUs + +[ upstream 630c7ed9ca0608912fa7c8591d05dfc8742dc9e6 in tip repo ] + +Chris Friesen notice that rcuc/X kthreads were consuming CPU even on +NOCB CPUs. This makes no sense because the only purpose or these +kthreads is to invoke normal (non-offloaded) callbacks, of which there +will never be any on NOCB CPUs. This problem was due to a bug in +cpu_has_callbacks_ready_to_invoke(), which should have been checking +->nxttail[RCU_NEXT_TAIL] for NULL, but which was instead (incorrectly) +checking ->nxttail[RCU_DONE_TAIL]. Because ->nxttail[RCU_DONE_TAIL] is +never NULL, the only effect is to cause the rcuc/X kthread to execute +when it should not do so. + +This commit therefore checks ->nxttail[RCU_NEXT_TAIL], which is NULL +for NOCB CPUs. + +Reported-by: Chris Friesen +Signed-off-by: Paul E. McKenney +Reviewed-by: Josh Triplett +Signed-off-by: Jim Somerville +--- + kernel/rcutree.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/kernel/rcutree.c b/kernel/rcutree.c +index cd68fe3..3276ac1 100644 +--- a/kernel/rcutree.c ++++ b/kernel/rcutree.c +@@ -300,7 +300,7 @@ static int + cpu_has_callbacks_ready_to_invoke(struct rcu_data *rdp) + { + return &rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL] && +- rdp->nxttail[RCU_DONE_TAIL] != NULL; ++ rdp->nxttail[RCU_NEXT_TAIL] != NULL; + } + + /* +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/turn-off-write-same-in-smartqpi-driver.patch b/kernel-std/centos/patches/turn-off-write-same-in-smartqpi-driver.patch new file mode 100644 index 00000000..9587d4f6 --- /dev/null +++ b/kernel-std/centos/patches/turn-off-write-same-in-smartqpi-driver.patch @@ -0,0 +1,28 @@ +From 533c1cd1909a81bd027435dd934a194983c9e9b8 Mon Sep 17 00:00:00 2001 +Message-Id: <533c1cd1909a81bd027435dd934a194983c9e9b8.1527544850.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Jim Somerville +Date: Tue, 6 Mar 2018 12:54:40 -0500 +Subject: [PATCH 26/26] turn off write same in smartqpi driver + +Signed-off-by: Jim Somerville +--- + drivers/scsi/smartpqi/smartpqi_init.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c +index 2c6b546..6968c48 100644 +--- a/drivers/scsi/smartpqi/smartpqi_init.c ++++ b/drivers/scsi/smartpqi/smartpqi_init.c +@@ -5843,6 +5843,7 @@ static struct scsi_host_template pqi_driver_template = { + .slave_alloc = pqi_slave_alloc, + .sdev_attrs = pqi_sdev_attrs, + .shost_attrs = pqi_shost_attrs, ++ .no_write_same = 1, + }; + + static int pqi_register_scsi(struct pqi_ctrl_info *ctrl_info) +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/x86-enable-DMA-CMA-with-swiotlb.patch b/kernel-std/centos/patches/x86-enable-DMA-CMA-with-swiotlb.patch new file mode 100644 index 00000000..506d84a6 --- /dev/null +++ b/kernel-std/centos/patches/x86-enable-DMA-CMA-with-swiotlb.patch @@ -0,0 +1,182 @@ +From e27598227a12485c787a57581b1797531941bf51 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Akinobu Mita +Date: Wed, 4 Jun 2014 16:06:50 -0700 +Subject: [PATCH 11/26] x86: enable DMA CMA with swiotlb + +commit 9c5a3621427da68afe6a078cadf807d2c8cc1d12 upstream. +Ported-by: Nam Ninh + +The DMA Contiguous Memory Allocator support on x86 is disabled when +swiotlb config option is enabled. So DMA CMA is always disabled on +x86_64 because swiotlb is always enabled. This attempts to support for +DMA CMA with enabling swiotlb config option. + +The contiguous memory allocator on x86 is integrated in the function +dma_generic_alloc_coherent() which is .alloc callback in nommu_dma_ops +for dma_alloc_coherent(). + +x86_swiotlb_alloc_coherent() which is .alloc callback in swiotlb_dma_ops +tries to allocate with dma_generic_alloc_coherent() firstly and then +swiotlb_alloc_coherent() is called as a fallback. + +The main part of supporting DMA CMA with swiotlb is that changing +x86_swiotlb_free_coherent() which is .free callback in swiotlb_dma_ops +for dma_free_coherent() so that it can distinguish memory allocated by +dma_generic_alloc_coherent() from one allocated by +swiotlb_alloc_coherent() and release it with dma_generic_free_coherent() +which can handle contiguous memory. This change requires making +is_swiotlb_buffer() global function. + +This also needs to change .free callback in the dma_map_ops for amd_gart +and sta2x11, because these dma_ops are also using +dma_generic_alloc_coherent(). + +Signed-off-by: Akinobu Mita +Acked-by: Marek Szyprowski +Acked-by: Konrad Rzeszutek Wilk +Cc: David Woodhouse +Cc: Don Dutile +Cc: Thomas Gleixner +Cc: Ingo Molnar +Cc: "H. Peter Anvin" +Cc: Andi Kleen +Cc: Yinghai Lu +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +Signed-off-by: Jim Somerville +--- + arch/x86/Kconfig | 2 +- + arch/x86/include/asm/swiotlb.h | 7 +++++++ + arch/x86/kernel/amd_gart_64.c | 2 +- + arch/x86/kernel/pci-swiotlb.c | 9 ++++++--- + arch/x86/pci/sta2x11-fixup.c | 6 ++---- + include/linux/swiotlb.h | 2 ++ + lib/swiotlb.c | 2 +- + 7 files changed, 20 insertions(+), 10 deletions(-) + +diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig +index 48ae099..9e841a5 100644 +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -44,7 +44,7 @@ config X86 + select ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH if SMP + select ARCH_WANT_OPTIONAL_GPIOLIB + select ARCH_WANT_FRAME_POINTERS +- select HAVE_DMA_CONTIGUOUS if !SWIOTLB ++ select HAVE_DMA_CONTIGUOUS + select HAVE_KRETPROBES + select HAVE_OPTPROBES + select HAVE_KPROBES_ON_FTRACE +diff --git a/arch/x86/include/asm/swiotlb.h b/arch/x86/include/asm/swiotlb.h +index 977f176..ab05d73 100644 +--- a/arch/x86/include/asm/swiotlb.h ++++ b/arch/x86/include/asm/swiotlb.h +@@ -29,4 +29,11 @@ static inline void pci_swiotlb_late_init(void) + + static inline void dma_mark_clean(void *addr, size_t size) {} + ++extern void *x86_swiotlb_alloc_coherent(struct device *hwdev, size_t size, ++ dma_addr_t *dma_handle, gfp_t flags, ++ struct dma_attrs *attrs); ++extern void x86_swiotlb_free_coherent(struct device *dev, size_t size, ++ void *vaddr, dma_addr_t dma_addr, ++ struct dma_attrs *attrs); ++ + #endif /* _ASM_X86_SWIOTLB_H */ +diff --git a/arch/x86/kernel/amd_gart_64.c b/arch/x86/kernel/amd_gart_64.c +index b574b29..8e3842f 100644 +--- a/arch/x86/kernel/amd_gart_64.c ++++ b/arch/x86/kernel/amd_gart_64.c +@@ -512,7 +512,7 @@ gart_free_coherent(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_addr, struct dma_attrs *attrs) + { + gart_unmap_page(dev, dma_addr, size, DMA_BIDIRECTIONAL, NULL); +- free_pages((unsigned long)vaddr, get_order(size)); ++ dma_generic_free_coherent(dev, size, vaddr, dma_addr, attrs); + } + + static int gart_mapping_error(struct device *dev, dma_addr_t dma_addr) +diff --git a/arch/x86/kernel/pci-swiotlb.c b/arch/x86/kernel/pci-swiotlb.c +index 4853440..284d506 100644 +--- a/arch/x86/kernel/pci-swiotlb.c ++++ b/arch/x86/kernel/pci-swiotlb.c +@@ -16,7 +16,7 @@ + + int swiotlb __read_mostly; + +-static void *x86_swiotlb_alloc_coherent(struct device *hwdev, size_t size, ++void *x86_swiotlb_alloc_coherent(struct device *hwdev, size_t size, + dma_addr_t *dma_handle, gfp_t flags, + struct dma_attrs *attrs) + { +@@ -30,11 +30,14 @@ static void *x86_swiotlb_alloc_coherent(struct device *hwdev, size_t size, + return swiotlb_alloc_coherent(hwdev, size, dma_handle, flags); + } + +-static void x86_swiotlb_free_coherent(struct device *dev, size_t size, ++void x86_swiotlb_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_addr, + struct dma_attrs *attrs) + { +- swiotlb_free_coherent(dev, size, vaddr, dma_addr); ++ if (is_swiotlb_buffer(dma_to_phys(dev, dma_addr))) ++ swiotlb_free_coherent(dev, size, vaddr, dma_addr); ++ else ++ dma_generic_free_coherent(dev, size, vaddr, dma_addr, attrs); + } + + static struct dma_map_ops swiotlb_dma_ops = { +diff --git a/arch/x86/pci/sta2x11-fixup.c b/arch/x86/pci/sta2x11-fixup.c +index 9d8a509..5ceda85 100644 +--- a/arch/x86/pci/sta2x11-fixup.c ++++ b/arch/x86/pci/sta2x11-fixup.c +@@ -173,9 +173,7 @@ static void *sta2x11_swiotlb_alloc_coherent(struct device *dev, + { + void *vaddr; + +- vaddr = dma_generic_alloc_coherent(dev, size, dma_handle, flags, attrs); +- if (!vaddr) +- vaddr = swiotlb_alloc_coherent(dev, size, dma_handle, flags); ++ vaddr = x86_swiotlb_alloc_coherent(dev, size, dma_handle, flags, attrs); + *dma_handle = p2a(*dma_handle, to_pci_dev(dev)); + return vaddr; + } +@@ -183,7 +181,7 @@ static void *sta2x11_swiotlb_alloc_coherent(struct device *dev, + /* We have our own dma_ops: the same as swiotlb but from alloc (above) */ + static struct dma_map_ops sta2x11_dma_ops = { + .alloc = sta2x11_swiotlb_alloc_coherent, +- .free = swiotlb_free_coherent, ++ .free = x86_swiotlb_free_coherent, + .map_page = swiotlb_map_page, + .unmap_page = swiotlb_unmap_page, + .map_sg = swiotlb_map_sg_attrs, +diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h +index 16c296a..65c4a7a 100644 +--- a/include/linux/swiotlb.h ++++ b/include/linux/swiotlb.h +@@ -117,4 +117,6 @@ static inline void swiotlb_free(void) { } + #endif + + extern void swiotlb_print_info(void); ++extern int is_swiotlb_buffer(phys_addr_t paddr); ++ + #endif /* __LINUX_SWIOTLB_H */ +diff --git a/lib/swiotlb.c b/lib/swiotlb.c +index ffcaff5..d89c82a 100644 +--- a/lib/swiotlb.c ++++ b/lib/swiotlb.c +@@ -404,7 +404,7 @@ void __init swiotlb_free(void) + io_tlb_nslabs = 0; + } + +-static int is_swiotlb_buffer(phys_addr_t paddr) ++int is_swiotlb_buffer(phys_addr_t paddr) + { + return paddr >= io_tlb_start && paddr < io_tlb_end; + } +-- +1.8.3.1 + diff --git a/kernel-std/centos/patches/x86-make-dma_alloc_coherent-return-zeroed-memory-if-.patch b/kernel-std/centos/patches/x86-make-dma_alloc_coherent-return-zeroed-memory-if-.patch new file mode 100644 index 00000000..10d02ed8 --- /dev/null +++ b/kernel-std/centos/patches/x86-make-dma_alloc_coherent-return-zeroed-memory-if-.patch @@ -0,0 +1,87 @@ +From 2d627fa0c465b3146191ffa7e336bb1eca5d1879 Mon Sep 17 00:00:00 2001 +Message-Id: <2d627fa0c465b3146191ffa7e336bb1eca5d1879.1527544850.git.Jim.Somerville@windriver.com> +In-Reply-To: +References: +From: Akinobu Mita +Date: Wed, 4 Jun 2014 16:06:48 -0700 +Subject: [PATCH 14/26] x86: make dma_alloc_coherent() return zeroed memory if + CMA is enabled + +This patchset enhances the DMA Contiguous Memory Allocator on x86. + +Currently the DMA CMA is only supported with pci-nommu dma_map_ops and +furthermore it can't be enabled on x86_64. But I would like to allocate +big contiguous memory with dma_alloc_coherent() and tell it to the device +that requires it, regardless of which dma mapping implementation is +actually used in the system. + +So this makes it work with swiotlb and intel-iommu dma_map_ops, too. And +this also extends "cma=" kernel parameter to specify placement constraint +by the physical address range of memory allocations. For example, CMA +allocates memory below 4GB by "cma=64M@0-4G", it is required for the +devices only supporting 32-bit addressing on 64-bit systems without iommu. + +This patch (of 5): + +Calling dma_alloc_coherent() with __GFP_ZERO must return zeroed memory. + +But when the contiguous memory allocator (CMA) is enabled on x86 and the +memory region is allocated by dma_alloc_from_contiguous(), it doesn't +return zeroed memory. Because dma_generic_alloc_coherent() forgot to fill +the memory region with zero if it was allocated by +dma_alloc_from_contiguous() + +Most implementations of dma_alloc_coherent() return zeroed memory +regardless of whether __GFP_ZERO is specified. So this fixes it by +unconditionally zeroing the allocated memory region. + +Alternatively, we could fix dma_alloc_from_contiguous() to return zeroed +out memory and remove memset() from all caller of it. But we can't simply +remove the memset on arm because __dma_clear_buffer() is used there for +ensuring cache flushing and it is used in many places. Of course we can +do redundant memset in dma_alloc_from_contiguous(), but I think this patch +is less impact for fixing this problem. + +Signed-off-by: Akinobu Mita +Cc: Marek Szyprowski +Cc: Konrad Rzeszutek Wilk +Cc: David Woodhouse +Cc: Don Dutile +Cc: Thomas Gleixner +Cc: Ingo Molnar +Cc: "H. Peter Anvin" +Cc: Andi Kleen +Cc: Yinghai Lu +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +(cherry picked from commit d92ef66c4f8fdf7a24736b1ab6c48d32de9bfc07) +Signed-off-by: Jim Somerville +--- + arch/x86/kernel/pci-dma.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c +index 77a4e62..c84ffe7 100644 +--- a/arch/x86/kernel/pci-dma.c ++++ b/arch/x86/kernel/pci-dma.c +@@ -99,7 +99,7 @@ void *dma_generic_alloc_coherent(struct device *dev, size_t size, + + dma_mask = dma_alloc_coherent_mask(dev, flag); + +- flag |= __GFP_ZERO; ++ flag &= ~__GFP_ZERO; + again: + page = NULL; + /* CMA can be used only in the context which permits sleeping */ +@@ -130,7 +130,7 @@ again: + + return NULL; + } +- ++ memset(page_address(page), 0, size); + *dma_addr = addr; + return page_address(page); + } +-- +1.8.3.1 + diff --git a/kernel-std/centos/srpm_path b/kernel-std/centos/srpm_path new file mode 100644 index 00000000..0d81c12a --- /dev/null +++ b/kernel-std/centos/srpm_path @@ -0,0 +1,2 @@ +mirror:Source/kernel-3.10.0-862.6.3.el7.src.rpm + diff --git a/kernel-std/files/ima_signing_key.pub b/kernel-std/files/ima_signing_key.pub new file mode 100644 index 00000000..dfffc0c4 Binary files /dev/null and b/kernel-std/files/ima_signing_key.pub differ