From bfc31b2807b1877f8d957b586a75095347862c49 Mon Sep 17 00:00:00 2001
From: ROBBE Patrick <robbe@lal.in2p3.fr>
Date: Sun, 27 Jan 2019 21:50:27 +0100
Subject: [PATCH] Add pcie40_reload executable

---
 .gitignore                            |   2 +-
 Driver/pcie40_reload/Makefile         |  28 +++
 Driver/pcie40_reload/main.c           |  25 +++
 Driver/pcie40_reload/pcie40_reload.sh | 257 ++++++++++++++++++++++++++
 4 files changed, 311 insertions(+), 1 deletion(-)
 create mode 100644 Driver/pcie40_reload/Makefile
 create mode 100644 Driver/pcie40_reload/main.c
 create mode 100755 Driver/pcie40_reload/pcie40_reload.sh

diff --git a/.gitignore b/.gitignore
index eef29c1..2c5323c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
 *~
 *.pyc
-
+*.log
diff --git a/Driver/pcie40_reload/Makefile b/Driver/pcie40_reload/Makefile
new file mode 100644
index 0000000..f67cdc3
--- /dev/null
+++ b/Driver/pcie40_reload/Makefile
@@ -0,0 +1,28 @@
+HERE :=$(strip $(realpath $(dir $(lastword $(MAKEFILE_LIST)))))
+TOP :=$(realpath $(HERE)/..)
+
+include $(TOP)/common/flags.mk
+
+SCRIPTS :=pcie40_reload.sh
+SCRIPTS_INSTALL =$(DAQ40_PREFIX)/pcie40_reload
+
+PCIE40_RELOAD_SUID :=pcie40_reload_suid
+PCIE40_RELOAD_SUID_OBJS =main.o
+PCIE40_RELOAD_SUID_CFLAGS =$(CFLAGS)
+PCIE40_RELOAD_SUID_INSTALL =$(SCRIPTS_INSTALL)
+
+PCIE40_RELOAD :=pcie40_reload
+PCIE40_RELOAD_LINK =$(SCRIPTS_INSTALL)/pcie40_reload_suid
+PCIE40_RELOAD_INSTALL =$(PREFIX)/bin
+
+PCIE40_RELOAD_MAN_DCRT =$(HERE)/man.dcrt
+PCIE40_RELOAD_MAN_INSTALL =$(PREFIX)/share/man
+
+include $(TOP)/common/rules.mk
+ifeq ($(ENABLE_PCIE40), true)
+$(eval $(call COPY_template,SCRIPTS,755))
+$(eval $(call LINK_template,PCIE40_RELOAD))
+$(eval $(call ODIR_template,PCIE40_RELOAD_SUID))
+$(eval $(call MAN_template,PCIE40_RELOAD,1))
+endif
+$(eval $(call DEFAULT_template))
diff --git a/Driver/pcie40_reload/main.c b/Driver/pcie40_reload/main.c
new file mode 100644
index 0000000..34c1611
--- /dev/null
+++ b/Driver/pcie40_reload/main.c
@@ -0,0 +1,25 @@
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <limits.h>
+#include <libgen.h>
+
+int main(int argc, char *argv[])
+{
+  char here_path[PATH_MAX];
+  char script_path[PATH_MAX];
+
+  if (readlink("/proc/self/exe", here_path, PATH_MAX) == -1) {
+    perror("readlink");
+    return -1;
+  }
+  if (setresuid(0,0,0)) {
+    perror("setresuid");
+    return -1;
+  }
+  snprintf(script_path, sizeof(script_path), "%s/pcie40_reload.sh", dirname(here_path));
+  return execvp(script_path, argv);
+}
diff --git a/Driver/pcie40_reload/pcie40_reload.sh b/Driver/pcie40_reload/pcie40_reload.sh
new file mode 100755
index 0000000..5eac524
--- /dev/null
+++ b/Driver/pcie40_reload/pcie40_reload.sh
@@ -0,0 +1,257 @@
+#!/bin/bash
+
+CACHE_PATH=/tmp/pcie40_reload.cache
+
+#ug`pcie40_reload.description`
+# This command must be executed to reload the PCIe40 driver after an FPGA has been reprogrammed.
+# _ The tool performs the following steps, in order:
+
+RELOAD_MODULE=0
+RELOAD_ALL=0
+USE_CACHE=0
+FLUSH_CACHE=0
+QUIET=0
+
+#ug`pcie40_reload.synopsis`
+# *pcie40_reload* [-m] [-a] [-c] [-f] [-q]
+
+function usage {
+  echo "pcie40_reload [-m] [-a] [-c] [-f] [-q]" >&2
+  echo " -m    reload kernel module" >&2
+  echo " -a    reload all devices" >&2
+  echo " -c    always read cache" >&2
+  echo " -f    always overwrite cache" >&2
+  echo " -q    suppress dmesg output" >&2
+}
+
+while getopts "macfqh" opt; do
+  case $opt in
+
+#ug`pcie40_reload.options`mod
+# *-m*::
+# Remove and then re-insert module into kernel (this is required after a driver update).
+    m) RELOAD_MODULE=1
+      ;;
+#ug`pcie40_reload.options`all
+# *-a*::
+# Reload all interface (by default only the interfaces where the FPGA has been reprogrammed are reloaded).
+    a) RELOAD_ALL=1
+      ;;
+#ug`pcie40_reload.options`cache
+# *-c*::
+# Take the device list from the local cache, if it exists. By default the cache is used only if no PCIe40 interface appears under sysfs. With this option the cache contents are used instead. This can be useful in case a reload was issued too early (while one FPGA was still being reprogrammed) and the corresponding sysfs nodes do not exist anymore.
+    c) USE_CACHE=1
+      ;;
+#ug`pcie40_reload.options`flush
+# *-f*::
+# Forces a cache update. By default the cache is updated only if additional entries have been detected compared to the cache contents. With this option the cache will be overwritten regardless.
+    f) FLUSH_CACHE=1
+      ;;
+#ug`pcie40_reload.options`quiet
+# *-q*::
+# Suppress dmesg output. Without this option a partial dmesg log is printed for troubleshooting in case of errors.
+    q) QUIET=1
+      ;;
+    h)
+      usage
+      exit 1
+      ;;
+    \?)
+      echo "Invalid option: -${OPTARG}" >&2
+      exit 1
+      ;;
+    :)
+      echo "Option -${OPTARG} requires an argument" >&2
+      exit 1
+      ;;
+  esac
+done
+
+if [ "${RELOAD_MODULE}" -eq 0 ]; then
+  INSTALLED_SRCVERSION=`modinfo -F srcversion lhcb_pcie40`
+  LOADED_SRCVERSION=$(</sys/module/lhcb_pcie40/srcversion)
+  if [ "${LOADED_SRCVERSION}" != "${INSTALLED_SRCVERSION}" ]; then
+    echo "Warning: loaded module version does not match installed version, run \`pcie40_reload -m\` to use the installed module instead" >&2
+  fi;
+else
+  RELOAD_ALL=1
+fi;
+
+devs=($(find /sys/devices/pci0000\:* -name pcie40_interface -exec cat {} \; -exec echo -n ' ' \; -execdir pwd \; | sort -nr | cut -f 2 -d ' ' 2>/dev/null))
+
+if [ -z "${devs}" ]; then
+  if [ -f "${CACHE_PATH}" ]; then
+    echo "No PCIe40 devices appear in sysfs, using cache"
+    devs=($(<${CACHE_PATH}))
+  else
+    echo "No devices detected! Assuming the FPGA was programmed correctly, you might have to reboot"
+    exit 1
+  fi
+elif [ ${USE_CACHE} -eq 1 ]; then
+  if [ -f "${CACHE_PATH}" ]; then
+    echo "Using PCIe40 interface list from cache"
+    devs=($(<${CACHE_PATH}))
+  else
+    echo "No PCIe40 interface cache present, if not all PCIe40 boards are detected you might have to reboot"
+    exit 1
+  fi
+fi
+if [ -f "${CACHE_PATH}" ]; then
+  devs_cached=($(<${CACHE_PATH}))
+  if [ ${FLUSH_CACHE} -eq 1 ] || [ ${#devs_cached[@]} -lt ${#devs[@]} ]; then
+    echo "Updating cache"
+    echo "${devs}" > ${CACHE_PATH}
+  fi
+else
+  echo "Creating cache"
+  echo "${devs}" > ${CACHE_PATH}
+fi
+
+ok=1
+
+pids_ctrl=
+pids_bar0=
+pids_bar2=
+
+get_pids() {
+# $1: file to check
+# $2: variable to append pids to
+  pids=`lsof -t $1`
+  for pid in $pids; do
+    ps -p $pid --no-headers -o "  %U%p%c"
+    eval "${2}+=\ ${pid}"
+  done
+}
+
+if_id=${#devs[@]}
+
+for dev in ${devs[@]}; do
+  (( if_id -= 1 ))
+
+  if [ ${RELOAD_ALL} -eq 0 ] && [ -f "${dev}/pcie40_loaded" ] && [ $(< ${dev}/pcie40_loaded) -eq 1 ]; then
+    continue;
+  fi;
+  echo "PCIe40 interface ${if_id}:"
+
+  if [ $((if_id % 2)) -eq 0 ]; then
+    if [ -e /dev/pcie40_${if_id}_bar0 ]; then
+      echo " BAR0 used by:"
+      get_pids /dev/pcie40_${if_id}_bar0 pids_bar0
+    else
+      echo " BAR0 missing!"
+    fi
+    if [ -e /dev/pcie40_${if_id}_bar2 ]; then
+      echo " BAR2 used by:"
+      get_pids /dev/pcie40_${if_id}_bar2 pids_bar2
+    else
+      echo " BAR2 missing!"
+    fi
+  fi
+  if [ -e /dev/pcie40_${if_id}_ctrl ]; then
+    echo " CTRL used by:"
+    get_pids /dev/pcie40_${if_id}_ctrl pids_ctrl
+  else
+    echo " CTRL missing!"
+  fi
+done
+
+#ug`pcie40_reload.steps`1
+# . Terminates any process currently using the PCIe40 boards to be reloaded
+
+if [ -n "$pids_bar0" ] || [ -n "$pids_bar2" ] || [ -n "$pids_ctrl" ]; then
+  echo -n "Stopping processes... "
+  kill $pids_bar0 $pids_bar2 $pids_ctrl
+  echo OK
+fi
+
+#ug`pcie40_reload.steps`2
+# . For every PCIe40 interface:
+# .. Disconnects the interface via _sysfs_
+# .. Requests a PCI rescan on the upstream device
+# .. Checks if the driver detected successfully the interface
+
+devs_removed=
+
+if [ ${RELOAD_MODULE} -eq 1 ]; then
+  sudo rmmod lhcb_pcie40
+  sudo modprobe lhcb_pcie40
+else
+  for dev in ${devs[@]}; do
+    if [ ${RELOAD_ALL} -eq 0 ] && [ -f "${dev}/pcie40_loaded" ] && [ $(< ${dev}/pcie40_loaded) -eq 1 ]; then
+      continue;
+    fi;
+    if [ ! -d "${dev}" ]; then
+      devs_removed="${dev} ${devs_removed}"
+      continue;
+    fi;
+    echo "Removing device ${dev}"
+    if [ -f "${dev}/device" ] && [ $(< ${dev}/device) = 0xce40 ]; then
+      if [ -e ${dev}/driver ]; then
+        echo " Driver: OK"
+      else
+        echo " Driver: NO"
+      fi;
+      echo 1 | sudo tee ${dev}/remove > /dev/null
+      devs_removed="${dev} ${devs_removed}"
+    fi;
+  done
+
+  for dev in ${devs_removed}; do
+    echo "Reloading device ${dev}"
+    rp=`dirname ${dev}`
+    echo 1 | sudo tee ${rp}/rescan > /dev/null
+    if [ -e ${dev}/driver ]; then
+      echo " Driver: OK"
+    else
+      echo " Driver: NO"
+      ok=0
+    fi;
+  done
+fi;
+
+if_id=${#devs[@]}
+
+for dev in ${devs[@]}; do
+  (( if_id -= 1 ))
+  echo "PCIe40 interface ${if_id}:"
+
+  #ug`pcie40_reload.steps`3
+  # . Checks the presence of the ECS registers
+
+  if [ $((if_id % 2)) -eq 0 ]; then
+    echo -n " BAR0: "
+    if [ -e /dev/pcie40_${if_id}_bar0 ]; then
+      echo "OK"
+    else
+      echo "NO"
+      ok=0
+    fi
+    echo -n " BAR2: "
+    if [ -e /dev/pcie40_${if_id}_bar2 ]; then
+      echo "OK"
+    else
+      echo "NO"
+      ok=0
+    fi
+  fi
+
+  #ug`pcie40_reload.steps`4
+  # . Checks the presence of the DMA controllers
+
+  echo -n " CTRL: "
+  if [ -e /dev/pcie40_${if_id}_ctrl ]; then
+    echo "OK"
+  else
+    echo "NO"
+    ok=0
+  fi
+done
+
+if [ "$ok" -eq 0 ] && [ ${QUIET} -eq 0 ]; then
+  echo "Errors found, debug output follows:"
+  dmesg | grep -E "(P40:)|(P40DAQ:)" | tail -n 100
+  exit 1
+fi
+
+#ug`pcie40_reload.exit`
+# 0 on success, 1 if issues have been encountered. In the latter case the last diagnostic messages from the driver are also printed to standard output.
-- 
GitLab