eeh-functions.sh 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. #!/bin/sh
  2. # SPDX-License-Identifier: GPL-2.0-only
  3. export KSELFTESTS_SKIP=4
  4. log() {
  5. echo >/dev/stderr $*
  6. }
  7. pe_ok() {
  8. local dev="$1"
  9. local path="/sys/bus/pci/devices/$dev/eeh_pe_state"
  10. # if a driver doesn't support the error handling callbacks then the
  11. # device is recovered by removing and re-probing it. This causes the
  12. # sysfs directory to disappear so read the PE state once and squash
  13. # any potential error messages
  14. local eeh_state="$(cat $path 2>/dev/null)"
  15. if [ -z "$eeh_state" ]; then
  16. return 1;
  17. fi
  18. local fw_state="$(echo $eeh_state | cut -d' ' -f1)"
  19. local sw_state="$(echo $eeh_state | cut -d' ' -f2)"
  20. # If EEH_PE_ISOLATED or EEH_PE_RECOVERING are set then the PE is in an
  21. # error state or being recovered. Either way, not ok.
  22. if [ "$((sw_state & 0x3))" -ne 0 ] ; then
  23. return 1
  24. fi
  25. # A functioning PE should have the EEH_STATE_MMIO_ACTIVE and
  26. # EEH_STATE_DMA_ACTIVE flags set. For some goddamn stupid reason
  27. # the platform backends set these when the PE is in reset. The
  28. # RECOVERING check above should stop any false positives though.
  29. if [ "$((fw_state & 0x18))" -ne "$((0x18))" ] ; then
  30. return 1
  31. fi
  32. return 0;
  33. }
  34. eeh_supported() {
  35. test -e /proc/powerpc/eeh && \
  36. grep -q 'EEH Subsystem is enabled' /proc/powerpc/eeh
  37. }
  38. eeh_test_prep() {
  39. if ! eeh_supported ; then
  40. echo "EEH not supported on this system, skipping"
  41. exit $KSELFTESTS_SKIP;
  42. fi
  43. if [ ! -e "/sys/kernel/debug/powerpc/eeh_dev_check" ] && \
  44. [ ! -e "/sys/kernel/debug/powerpc/eeh_dev_break" ] ; then
  45. log "debugfs EEH testing files are missing. Is debugfs mounted?"
  46. exit $KSELFTESTS_SKIP;
  47. fi
  48. # Bump the max freeze count to something absurd so we don't
  49. # trip over it while breaking things.
  50. echo 5000 > /sys/kernel/debug/powerpc/eeh_max_freezes
  51. }
  52. eeh_can_break() {
  53. # skip bridges since we can't recover them (yet...)
  54. if [ -e "/sys/bus/pci/devices/$dev/pci_bus" ] ; then
  55. log "$dev, Skipped: bridge"
  56. return 1;
  57. fi
  58. # The ahci driver doesn't support error recovery. If the ahci device
  59. # happens to be hosting the root filesystem, and then we go and break
  60. # it the system will generally go down. We should probably fix that
  61. # at some point
  62. if [ "ahci" = "$(basename $(realpath /sys/bus/pci/devices/$dev/driver))" ] ; then
  63. log "$dev, Skipped: ahci doesn't support recovery"
  64. return 1;
  65. fi
  66. # Don't inject errosr into an already-frozen PE. This happens with
  67. # PEs that contain multiple PCI devices (e.g. multi-function cards)
  68. # and injecting new errors during the recovery process will probably
  69. # result in the recovery failing and the device being marked as
  70. # failed.
  71. if ! pe_ok $dev ; then
  72. log "$dev, Skipped: Bad initial PE state"
  73. return 1;
  74. fi
  75. return 0
  76. }
  77. eeh_one_dev() {
  78. local dev="$1"
  79. # Using this function from the command line is sometimes useful for
  80. # testing so check that the argument is a well-formed sysfs device
  81. # name.
  82. if ! test -e /sys/bus/pci/devices/$dev/ ; then
  83. log "Error: '$dev' must be a sysfs device name (DDDD:BB:DD.F)"
  84. return 1;
  85. fi
  86. # Break it
  87. echo $dev >/sys/kernel/debug/powerpc/eeh_dev_break
  88. # Force an EEH device check. If the kernel has already
  89. # noticed the EEH (due to a driver poll or whatever), this
  90. # is a no-op.
  91. echo $dev >/sys/kernel/debug/powerpc/eeh_dev_check
  92. # Default to a 60s timeout when waiting for a device to recover. This
  93. # is an arbitrary default which can be overridden by setting the
  94. # EEH_MAX_WAIT environmental variable when required.
  95. # The current record holder for longest recovery time is:
  96. # "Adaptec Series 8 12G SAS/PCIe 3" at 39 seconds
  97. max_wait=${EEH_MAX_WAIT:=60}
  98. for i in `seq 0 ${max_wait}` ; do
  99. if pe_ok $dev ; then
  100. break;
  101. fi
  102. log "$dev, waited $i/${max_wait}"
  103. sleep 1
  104. done
  105. if ! pe_ok $dev ; then
  106. log "$dev, Failed to recover!"
  107. return 1;
  108. fi
  109. log "$dev, Recovered after $i seconds"
  110. return 0;
  111. }
  112. eeh_has_driver() {
  113. test -e /sys/bus/pci/devices/$1/driver;
  114. return $?
  115. }
  116. eeh_can_recover() {
  117. # we'll get an IO error if the device's current driver doesn't support
  118. # error recovery
  119. echo $1 > '/sys/kernel/debug/powerpc/eeh_dev_can_recover' 2>/dev/null
  120. return $?
  121. }
  122. eeh_find_all_pfs() {
  123. devices=""
  124. # SR-IOV on pseries requires hypervisor support, so check for that
  125. is_pseries=""
  126. if grep -q pSeries /proc/cpuinfo ; then
  127. if [ ! -f /proc/device-tree/rtas/ibm,open-sriov-allow-unfreeze ] ||
  128. [ ! -f /proc/device-tree/rtas/ibm,open-sriov-map-pe-number ] ; then
  129. return 1;
  130. fi
  131. is_pseries="true"
  132. fi
  133. for dev in `ls -1 /sys/bus/pci/devices/` ; do
  134. sysfs="/sys/bus/pci/devices/$dev"
  135. if [ ! -e "$sysfs/sriov_numvfs" ] ; then
  136. continue
  137. fi
  138. # skip unsupported PFs on pseries
  139. if [ -z "$is_pseries" ] &&
  140. [ ! -f "$sysfs/of_node/ibm,is-open-sriov-pf" ] &&
  141. [ ! -f "$sysfs/of_node/ibm,open-sriov-vf-bar-info" ] ; then
  142. continue;
  143. fi
  144. # no driver, no vfs
  145. if ! eeh_has_driver $dev ; then
  146. continue
  147. fi
  148. devices="$devices $dev"
  149. done
  150. if [ -z "$devices" ] ; then
  151. return 1;
  152. fi
  153. echo $devices
  154. return 0;
  155. }
  156. # attempts to enable one VF on each PF so we can do VF specific tests.
  157. # stdout: list of enabled VFs, one per line
  158. # return code: 0 if vfs are found, 1 otherwise
  159. eeh_enable_vfs() {
  160. pf_list="$(eeh_find_all_pfs)"
  161. vfs=0
  162. for dev in $pf_list ; do
  163. pf_sysfs="/sys/bus/pci/devices/$dev"
  164. # make sure we have a single VF
  165. echo 0 > "$pf_sysfs/sriov_numvfs"
  166. echo 1 > "$pf_sysfs/sriov_numvfs"
  167. if [ "$?" != 0 ] ; then
  168. log "Unable to enable VFs on $pf, skipping"
  169. continue;
  170. fi
  171. vf="$(basename $(realpath "$pf_sysfs/virtfn0"))"
  172. if [ $? != 0 ] ; then
  173. log "unable to find enabled vf on $pf"
  174. echo 0 > "$pf_sysfs/sriov_numvfs"
  175. continue;
  176. fi
  177. if ! eeh_can_break $vf ; then
  178. log "skipping "
  179. echo 0 > "$pf_sysfs/sriov_numvfs"
  180. continue;
  181. fi
  182. vfs="$((vfs + 1))"
  183. echo $vf
  184. done
  185. test "$vfs" != 0
  186. return $?
  187. }
  188. eeh_disable_vfs() {
  189. pf_list="$(eeh_find_all_pfs)"
  190. if [ -z "$pf_list" ] ; then
  191. return 1;
  192. fi
  193. for dev in $pf_list ; do
  194. echo 0 > "/sys/bus/pci/devices/$dev/sriov_numvfs"
  195. done
  196. return 0;
  197. }