Logo Search packages:      
Sourcecode: s390-tools version File versions  Download package

zfcpdump.c

/*
 * zfcpdump userspace tool
 *
 * This tool should be used in an intitramfs together with a kernel with
 * enabled CONFIG_ZFCPDUMP kernel build option. The tool is able to write
 * standalone system dumps on SCSI disks.
 *
 * See Documentation/s390/zfcpdump.txt for more information!
 *
 * Copyright IBM Corp. 2003, 2007.
 * Author(s): Michael Holzheu
 */

#define _GNU_SOURCE

#include <errno.h>
#include <string.h>
#include <dirent.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/reboot.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/time.h>
#include <linux/reboot.h>
#include <asm/types.h>
#include "zfcpdump.h"
#ifdef GZIP_SUPPORT
#include <zlib.h>
#endif

static struct globals g;
static char *module_list[] = {"zfcp", "sd_mod", "ext2", "ext3", "ext4",
                              "zcore_mod", NULL};

/*
 * parse one kernel parameter in the form keyword=value
 */
static int parse_parameter(char *parameter)
{
      char *token;
      char *end_ptr;

      token = strtok(parameter, "=");
      if (token == NULL)
            return 0;

      if (strcmp(token, PARM_DIR) == 0) {
            /* Dump Dir */
            g.parm_dir = strtok(NULL, "=");
            if (g.parm_dir == NULL) {
                  PRINT_WARN("No value for '%s' parameter specified\n",
                            PARM_DIR);
                  PRINT_WARN("Using default: %s\n", PARM_DIR_DFLT);
                  g.parm_dir = PARM_DIR_DFLT;
            }
      } else if (strcmp(token, PARM_PART) == 0) {
            /* Dump Partition */
            g.parm_part = strtok(NULL, "=");
            if (g.parm_part == NULL) {
                  PRINT_ERR("No value for '%s' parameter "
                          "specified\n", PARM_PART);
                  return -1;
            }
      } else if (strcmp(token, PARM_MEM) == 0) {
            /* Dump mem */
            char *mem_str = strtok(NULL, "=");
            if (mem_str == NULL) {
                  PRINT_ERR("No value for '%s' parameter "
                          "specified\n", PARM_MEM);
                  return -1;
            }
            g.parm_mem = strtoll(mem_str, &end_ptr, 0);
            if (*end_ptr != 0) {
                  PRINT_ERR("Invalid value for '%s' parameter "
                          "specified\n", PARM_MEM);
                  return -1;
            }
      } else if (strcmp(token, PARM_COMP) == 0) {
            /* Dump Compression */
            g.parm_compress = strtok(NULL, "=");
            if (g.parm_compress == NULL) {
                  PRINT_WARN("No value for '%s' parameter "
                           "specified\n", PARM_COMP);
                  PRINT_WARN("Using default: %s\n", PARM_COMP_DFLT);
                  g.parm_compress = PARM_COMP_DFLT;
            } else if ((strcmp(g.parm_compress, PARM_COMP_GZIP) != 0) &&
                     (strcmp(g.parm_compress, PARM_COMP_NONE) != 0)) {
                  PRINT_WARN("Unknown dump compression '%s' "
                           "specified!\n", g.parm_compress);
                  PRINT_WARN("Using default: %s\n", PARM_COMP_DFLT);
                  g.parm_compress = PARM_COMP_DFLT;
            }
      } else if (strcmp(token, PARM_DEBUG) == 0) {
            /* Dump Debug */
            char *s = strtok(NULL, "=");
            if (s == NULL) {
                  PRINT_WARN("No value for '%s' parameter "
                        "specified\n", PARM_DEBUG);
                  PRINT_WARN("Using default: %d\n", PARM_DEBUG_DFLT);
            } else {
                  g.parm_debug = atoi(s);
                  if ((g.parm_debug < PARM_DEBUG_MIN) ||
                      (g.parm_debug > PARM_DEBUG_MAX)) {
                        PRINT_WARN("Invalid value (%i) for %s "
                        "parameter specified (allowed range is "
                        "%i - %i)\n", g.parm_debug, PARM_DEBUG,
                        PARM_DEBUG_MIN, PARM_DEBUG_MAX);
                        PRINT_WARN("Using default: %i\n",
                        PARM_DEBUG_DFLT);
                        g.parm_debug = PARM_DEBUG_DFLT;
                  }
            }
      } else if (strcmp(token, PARM_MODE) == 0) {
            /* Dump Mode */
            char *s = strtok(NULL, "=");
            if (s == NULL) {
                  PRINT_WARN("No value for '%s' parameter "
                        "specified\n", PARM_MODE);
                  PRINT_WARN("Using default: %s\n", PARM_MODE_DFLT);
            } else if (strcmp(s, PARM_MODE_INTERACT) == 0) {
                  g.parm_mode = PARM_MODE_INTERACT_NUM;
            } else if (strcmp(s, PARM_MODE_AUTO) == 0) {
                  g.parm_mode = PARM_MODE_AUTO_NUM;
            } else {
                  PRINT_WARN("Unknown dump mode: %s\n", s);
                  PRINT_WARN("Using default: %s\n", PARM_MODE_DFLT);
            }
      }
      return 0;
}

/*
 * Get dump parameters from /proc/cmdline
 * Return: 0       - ok
 *         (!= 0)  - error
 */
static int parse_parmline(void)
{
      int fh, i, count, token_cnt;
      char *token;
      char *parms[KERN_PARM_MAX];

      /* setting defaults */

      g.parm_compress = PARM_COMP_DFLT;
      g.parm_dir      = PARM_DIR_DFLT;
      g.parm_part     = PARM_PART_DFLT;
      g.parm_debug    = PARM_DEBUG_DFLT;
      g.parm_mode     = PARM_MODE_NUM_DFLT;
      g.parm_mem      = PARM_MEM_DFLT;

      fh = open(PROC_CMDLINE, O_RDONLY);
      if (fh == -1) {
            PRINT_PERR("open %s failed\n", PROC_CMDLINE);
            return -1;
      }
      count = read(fh, g.parmline, CMDLINE_MAX_LEN);
      if (count == -1) {
            PRINT_PERR("read %s failed\n", PROC_CMDLINE);
            close(fh);
            return -1;
      }
      g.parmline[count-1] = '\0'; /* remove \n */
      token_cnt = 0;
      token = strtok(g.parmline, " \t\n");
      while (token != NULL) {
            parms[token_cnt] = token;
            token = strtok(NULL, " \t\n");
            token_cnt++;
            if (token_cnt >= KERN_PARM_MAX) {
                  PRINT_WARN("More than %i kernel parmameters "
                           "specified\n", KERN_PARM_MAX);
                  break;
            }
      }
      for (i = 0; i < token_cnt; i++) {
            if (parse_parameter(parms[i])) {
                  close(fh);
                  return -1;
            }
      }
      PRINT_TRACE("dump dir  : %s\n", g.parm_dir);
      PRINT_TRACE("dump part : %s\n", g.parm_part);
      PRINT_TRACE("dump comp : %s\n", g.parm_compress);
      PRINT_TRACE("dump debug: %d\n", g.parm_debug);
      PRINT_TRACE("dump mem:   %llx\n", (unsigned long long) g.parm_mem);

      if (g.parm_mode == PARM_MODE_AUTO_NUM)
            PRINT_TRACE("dump mode : %s\n", PARM_MODE_AUTO);
      if (g.parm_mode == PARM_MODE_INTERACT_NUM)
            PRINT_TRACE("dump mode : %s\n", PARM_MODE_INTERACT);

      sprintf(g.dump_dir, "%s/%s", DUMP_DIR, g.parm_dir);
      close(fh);
      return 0;
}

static int write_to_file(const char *file, const char *command)
{
      int fh;

      PRINT_TRACE("Write: %s - %s\n", file, command);
      fh = open(file, O_WRONLY);
      if (fh == -1) {
            PRINT_PERR("Could not open %s\n", file);
            return -1;
      }
      if (write(fh, command, strlen(command)) == -1) {
            PRINT_PERR("Write to %s failed\n", file);
            close(fh);
            return -1;
      };
      close(fh);
      return 0;
}

static int read_file(const char *file, char *buf, int size)
{
      int fh;

      PRINT_TRACE("Read: %s:\n", file);
      fh = open(file, O_RDONLY);
      if (fh == -1) {
            PRINT_PERR("open %s failed\n", file);
            return -1;
      }
      if (read(fh, buf, size) < 0) {
            PRINT_PERR("read %s failed\n", file);
            close(fh);
            return -1;
      }
      if (buf[strlen(buf) - 1] == '\n')
            buf[strlen(buf) - 1] = 0; /* strip newline */
      close(fh);
      PRINT_TRACE("'%s'\n", buf);

      return 0;
}

/*
 * Enable the scsi disk for dumping
 * Return:    0 - ok
 *         != 0 - error
 */
static int enable_zfcp_device(void)
{
      char command[1024], file[1024];
      struct stat s;

      /* Prevent setting all LUNs online for NPIV */
      if (stat("/sys/module/zfcp/parameters/allow_lun_scan", &s) == 0)
            write_to_file("/sys/module/zfcp/parameters/allow_lun_scan",
                        "0\n");
      /* device */
      if (read_file(IPL_DEVNO, g.dump_devno, sizeof(g.dump_devno)))
            return -1;
      sprintf(file, "/sys/bus/ccw/drivers/zfcp/%s/online", g.dump_devno);
      if (write_to_file(file, "1\n"))
            return -1;

      /* wwpn */
      if (read_file(IPL_WWPN, g.dump_wwpn, sizeof(g.dump_wwpn)))
            return -1;
      sprintf(file, "/sys/bus/ccw/drivers/zfcp/%s/port_add", g.dump_devno);
      /* The port_add attribute has been removed in recent kernels */
      if (stat(file, &s) == 0) {
            sprintf(command, "%s\n", g.dump_wwpn);
            if (write_to_file(file, command))
                  return -1;
      }

      /* lun */
      if (read_file(IPL_LUN, g.dump_lun, sizeof(g.dump_lun)))
            return -1;
      sprintf(file, "/sys/bus/ccw/drivers/zfcp/%s/%s/unit_add", g.dump_devno,
            g.dump_wwpn);
      sprintf(command, "%s\n", g.dump_lun);
      if (write_to_file(file, command))
            return -1;

      /* bootprog */
      read_file("/sys/firmware/ipl/bootprog", g.dump_bootprog,
            sizeof(g.dump_bootprog));

      return 0;
}

/*
 * Mount the dump device
 * Return:    0 - ok
 *         != 0 - error
 */
static int mount_dump_device(void)
{
      int pid;
      char dump_part[16];

      PRINT_TRACE("e2fsck\n");
      sprintf(dump_part, "%s%i", DEV_SCSI, atoi(g.parm_part));

      pid = fork();
      if (pid < 0) {
            PRINT_PERR("fork failed\n");
            return -1;
      } else if (pid == 0) {
            execl("/bin/e2fsck", "e2fsck", dump_part, "-y", NULL);
            execl("/sbin/e2fsck", "e2fsck", dump_part, "-y", NULL);
            exit(1);
      } else {
            waitpid(pid, NULL, 0);
      }

      PRINT_TRACE("mount\n");
      if (mount(dump_part, DUMP_DIR, "ext4", 0, NULL) == 0)
            return 0;
      if (mount(dump_part, DUMP_DIR, "ext3", 0, NULL) == 0)
            return 0;
      if (mount(dump_part, DUMP_DIR, "ext2", 0, NULL) != 0) {
            PRINT_PERR("mount failed\n");
            return -1;
      }
      return 0;
}

/*
 * unmount the dump device
 * Return:    0 - ok
 *         != 0 - error
 */
static int umount_dump_device(void)
{
      if (umount(DUMP_DIR) != 0) {
            PRINT_PERR("umount failed\n");
            return -1;
      }
      return 0;
}

/*
 * Terminate the system dumper
 */
static void terminate(void)
{
      int fd;

      sleep(WAIT_TIME_END); /* give the messages time to be displayed */
      fd = open(DEV_ZCORE_REIPL, O_WRONLY, 0);
      if (fd == -1)
            goto no_reipl;
      write(fd, REIPL, 1);
      close(fd);
no_reipl:
      reboot(LINUX_REBOOT_CMD_POWER_OFF);
}

/*
 * Signal handler for zfcp_dumper
 */
static __sighandler_t dump_sig_handler(int sig, siginfo_t *sip, void*p)
{
      PRINT_ERR("Got signal: %i\n", sig);
      PRINT_ERR("Dump failed!\n");
      terminate();
      return 0;
}

/*
 * Setup the Signal handler for zfcp_dumper
 * Return:   0 - ok
 *         !=0 - error
 */
static int init_sig(void)
{
      g.sigact.sa_flags = (SA_NODEFER | SA_SIGINFO | SA_RESETHAND);
      g.sigact.sa_handler = (__sighandler_t)dump_sig_handler;
      if (sigemptyset(&g.sigact.sa_mask) < 0)
            return -1;
      if (sigaction(SIGINT, &g.sigact, NULL) < 0)
            return -1;
      if (sigaction(SIGTERM, &g.sigact, NULL) < 0)
            return -1;
      if (sigaction(SIGPIPE, &g.sigact, NULL) < 0)
            return -1;
      if (sigaction(SIGABRT, &g.sigact, NULL) < 0)
            return -1;
      if (sigaction(SIGSEGV, &g.sigact, NULL) < 0)
            return -1;
      if (sigaction(SIGBUS, &g.sigact, NULL) < 0)
            return -1;

      return 0;
}

/*
 * Set memory management parameters: Ensure that dirty pages are written
 * early enough! See "Documentation/filesystems/proc.txt"
 * Return:   0 - ok
 *         !=0 - error
 */
static int tune_vm(void)
{
      char *sysctl_names[] = {"/proc/sys/vm/dirty_ratio",
                        "/proc/sys/vm/dirty_background_ratio",
                        "/proc/sys/vm/dirty_writeback_centisecs",
                        "/proc/sys/vm/dirty_expire_centisecs",
                        "/proc/sys/vm/vfs_cache_pressure",
                        "/proc/sys/vm/lowmem_reserve_ratio",
                        NULL};
      char *sysctl_values[] = {"2", "5", "50", "50", "500", "32", NULL};
      int i;

      i = 0;
      while (sysctl_names[i]) {
            if (write_to_file(sysctl_names[i], sysctl_values[i]))
                  return -1;
            i++;
      }
      return 0;
}

/*
 * Get dump number of either new dump or dump to erase
 * Parameter: dumpdir - dump directory (absolute path)
 *            mode    - DUMP_FIRST: Find smallest dump number in directory
 *                    - DUMP_LAST:  Find highest dump number in directory
 * Return: >= 0 - dump number
 *         -1   - no dump found in directory
 *         <-1  - error
 */
static int get_dump_num(const char *dumpdir, int mode)
{
      DIR *dir = NULL;
      struct dirent *dir_ent;
      int dump_found, rc;

      rc = 0;
      dump_found = 0;
      dir = opendir(dumpdir);
      if (!dir) {
            PRINT_PERR("Cannot evalute dump number\n");
            return -2;
      }

      while ((dir_ent = readdir(dir))) {
            int num;
            if (sscanf(dir_ent->d_name, "dump.%ui", &num) == 1) {
                  char suffix[1024] = {};

                  /*
                   * check if we have something like dump.001
                   * this is not treated as dump, since we do not allow
                   * leading zeros.
                   * Also files like dump.-1, dump.-10 are ignored.
                   */
                  sscanf(dir_ent->d_name, "dump.%s", suffix);
                  if (suffix[0] == '-')
                        continue;
                  if ((suffix[0] == '0') && isdigit(suffix[1]))
                        continue;
                  if (!dump_found) {
                        dump_found = 1;
                        rc = num;
                  } else if (mode == DUMP_LAST) {
                        rc = MAX(num, rc);
                  } else if (mode == DUMP_FIRST) {
                        rc = MIN(num, rc);
                  }
            }
      }
      if (!dump_found)
            rc = NO_DUMP;
      closedir(dir);

      return rc;
}

/*
 * Erase oldest dump in dump directory
 * Return:    0 - ok
 *          !=0 - error
 */
static int erase_oldest_dump(void)
{
      int dump_nr;
      char dname[1024] = {};
      char answ[1024] = {};

      dump_nr = get_dump_num(g.dump_dir, DUMP_FIRST);
      if (dump_nr < 0) {
            PRINT_ERR("Internal error: dump number cannot be evaluated\n");
            return -1;
      }
      sprintf(dname, "dump.%i", dump_nr);
      if (dump_nr == g.dump_nr) {
            PRINT_ERR("Sorry, cannot delete any more dumps!\n");
            return -1;
      }
      if (g.parm_mode == PARM_MODE_AUTO_NUM) {
            PRINT("Removing oldest dump: '%s'\n", dname);
      } else {
            while ((strcmp(answ, "y") != 0) && (strcmp(answ, "n") != 0)) {
                  PRINT("Remove oldest dump: '%s' (y/n)? ", dname);
                  scanf("%s", answ);
            }
            if (strcmp(answ, "n") == 0)
                  return -1;
      }
      sprintf(dname, "%s/dump.%i", g.dump_dir, dump_nr);
      if (unlink(dname) == -1) {
            PRINT_PERR("Could not remove dump\n");
            return -1;
      }
      sync();
      /*
       * Wait some seconds in order to give ext3 time to discover that file
       * has been removed.
       */
      sleep(WAIT_TIME_ERASE);
      PRINT("Dump removed!\n");
      return 0;
}

/*
 * write buffer to dump. In case of ENOSPC try to remove oldest dump
 * Parameter: fd    - filedescriptor of dump file
 *            buf   - buffer to write
 *            count - nr of bytes to write
 *
 * Return:    size  - written bytes
 *            <0    - error
 */
static ssize_t dump_write(int fd, const void *buf, size_t count)
{
      ssize_t written, rc;

      written = 0;
      while (written != count) {
            rc = write(fd, buf + written, count - written);
            if ((rc == -1) && (errno == ENOSPC)) {
                  PRINT_ERR("No space left on device!\n");
                  /* Try to erase old dump */
                  if (erase_oldest_dump())
                        return -1;
                  continue;
            } else if (rc == -1) {
                  /* Write failed somehow */
                  return -1;
            }
            written += rc;
      }
      return written;
}

#ifdef GZIP_SUPPORT
/*
 * Wrapper to gzip compress routine
 * Parameter: old      - buffer to compress (in)
 *            old_size - size of old buffer in bytes (in)
 *            new      - buffer for compressed data (out)
 *            new_size - size of 'new' buffer in bytes (in)
 * Return:    >=0 - Size of compressed buffer
 *            < 0 - error
 */
static int compress_gzip(const unsigned char *old, __u32 old_size,
                   unsigned char *new, __u32 new_size)
{
      int rc;
      unsigned long len;

      len = old_size;
      rc = compress(new, &len, old, new_size);
      switch (rc) {
      case Z_OK:
            return len;
      case Z_MEM_ERROR:
            PRINT_ERR("Z_MEM_ERROR (not enough memory)!\n");
            return -1;
      case Z_BUF_ERROR:
            /* In this case the compressed output is bigger than
               the uncompressed */
            return -1;
      case Z_DATA_ERROR:
            PRINT_ERR("Z_DATA_ERROR (input data corrupted)!\n");
            return -1;
      default:
            PRINT_ERR("Z_UNKNOWN_ERROR (rc 0x%x unknown)!\n", rc);
            return -1;
      }
}
#endif

/*
 * Do nothing! - No compression
 */
static int compress_none(const unsigned char *old, __u32 old_size,
                   unsigned char *new, __u32 new_size)
{
      return -1; /* "-1" indicates, that compression was not done */
}

/*
 * Convert s390 standalone dump header to lkcd dump header
 * Parameter: s390_dh - s390 dump header (in)
 *            dh      - lkcd dump header (out)
 */
static void s390_to_lkcd_hdr(struct dump_hdr_s390 *s390_dh,
                       struct dump_hdr_lkcd *dh)
{
      struct timeval h_time;

      /* adjust todclock to 1970 */
      __u64 tod = s390_dh->tod;
      tod -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096);
      tod >>= 12;
      h_time.tv_sec  = tod / 1000000;
      h_time.tv_usec = tod % 1000000;

      dh->memory_size    = s390_dh->memory_size;
      dh->memory_start   = s390_dh->memory_start;
      dh->memory_end     = s390_dh->memory_end;
      dh->num_dump_pages = s390_dh->num_pages;
      dh->page_size      = s390_dh->page_size;
      dh->dump_level     = s390_dh->dump_level;

      sprintf(dh->panic_string, "zSeries-dump (CPUID = %16llx)",
            (unsigned long long) s390_dh->cpu_id);

      if (s390_dh->arch_id == DH_ARCH_ID_S390)
            strcpy(dh->utsname_machine, "s390");
      else if (s390_dh->arch_id == DH_ARCH_ID_S390X)
            strcpy(dh->utsname_machine, "s390x");
      else
            strcpy(dh->utsname_machine, "<unknown>");

      strcpy(dh->utsname_sysname, "<unknown>");
      strcpy(dh->utsname_nodename, "<unknown>");
      strcpy(dh->utsname_release, "<unknown>");
      strcpy(dh->utsname_version, "<unknown>");
      strcpy(dh->utsname_domainname, "<unknown>");

      dh->magic_number   = DUMP_MAGIC_NUMBER;
      dh->version        = DUMP_VERSION_NUMBER;
      dh->header_size    = sizeof(struct dump_hdr_lkcd);
      dh->time.tv_sec    = h_time.tv_sec;
      dh->time.tv_usec   = h_time.tv_usec;
}

/*
 * Convert s390 standalone dump header to lkcd asm dump header
 * Parameter: s390_dh - s390 dump header (in)
 *            dh_asm  - lkcd asm dump header (out)
 */
static void s390_to_lkcd_hdr_asm(struct dump_hdr_s390 *s390_dh,
                           struct dump_hdr_lkcd_asm *dh_asm)
{
      unsigned int i;

      dh_asm->magic_number = DUMP_MAGIC_NUMBER_ASM;
      dh_asm->version = 0;
      dh_asm->hdr_size = sizeof(*dh_asm);
      dh_asm->cpu_cnt = s390_dh->cpu_cnt;
      dh_asm->real_cpu_cnt = s390_dh->real_cpu_cnt;
      for (i = 0; i < dh_asm->cpu_cnt; i++)
            dh_asm->lc_vec[i] = s390_dh->lc_vec[i];
}

/*
 * Write progress information to screen
 * Parameter: written - So many bytes have been written to the dump
 *            max     - This is the whole memory to be written
 */
static void show_progress(unsigned long long written, unsigned long long max)
{
      int    time;
      struct timeval t;
      double percent;

      gettimeofday(&t, NULL);
      time = t.tv_sec;
      if ((time < g.last_progress) && (written != max) && (written != 0))
            return;
      g.last_progress = time + 10;
      percent = ((double) written / (double) max) * 100.0;
      PRINT(" %4lli MB of %4lli MB (%5.1f%% )\n", written >> 20, max >> 20,
            percent);
      fflush(stdout);
}

/*
 * create dump
 *
 * Return:   0  - ok
 *         !=0  - error
 */
static int create_dump(void)
{
      struct stat stat_buf;
      struct dump_hdr_lkcd dh;
      struct dump_hdr_lkcd_asm dh_asm;
      struct dump_hdr_s390 s390_dh;
      compress_fn_t compress_fn;
      struct dump_page dp;
      char buf[PAGE_SIZE], dpcpage[PAGE_SIZE];
      char dump_name[1024];
      __u64 mem_loc, mem_count;
      __u32 buf_loc = 0, dp_size, dp_flags;
      int size, fin, fout, fmap, rc = 0;
      char c_info[CHUNK_INFO_SIZE];
      struct mem_chunk *chunk, *chunk_first = NULL, *chunk_prev = NULL;
      char *end_ptr;
      void *page_buf;

      if (stat(g.dump_dir, &stat_buf) < 0) {
            PRINT_ERR("Specified dump dir '%s' not found!\n", g.dump_dir);
            return -1;
      } else if (!S_ISDIR(stat_buf.st_mode)) {
            PRINT_ERR("Specified dump dir '%s' is not a directory!\n",
                  g.dump_dir);
            return -1;
      }

      /* Allocate buffer for writing */
      if (posix_memalign(&page_buf, PAGE_SIZE, DUMP_BUF_SIZE)) {
            PRINT_ERR("Out of memory: Could not allocate dump buffer\n");
            return -1;
      }

      /* initialize progress time */
      g.last_progress = 0;

      /* get dump number */
      g.dump_nr = get_dump_num(g.dump_dir, DUMP_LAST);
      if (g.dump_nr == NO_DUMP)
            g.dump_nr = 0;
      else if (g.dump_nr >= 0)
            g.dump_nr += 1;
      else
            return -1;

      /* Open the memory map file - only available with kernel 2.6.25 or
      * higher. If open fails, memory holes cannot be detected and only
      * one single memory chunk is assumed */
      fmap = open(DEV_ZCORE_MAP, O_RDONLY, 0);
      if (fmap == -1) {
            chunk_first = calloc(1, sizeof(struct mem_chunk));
            if (chunk_first == NULL) {
                  PRINT_ERR("Could not allocate %d bytes of memory\n",
                          (int) sizeof(struct mem_chunk));
                  return -1;
            }
            chunk_first->size = PARM_MEM_DFLT;
      } else {
      /* read information about memory chunks (start address and size) */
            do {
                  if (read(fmap, c_info, sizeof(c_info)) != sizeof(c_info)) {
                        PRINT_ERR("read() memory map file '%s' "
                                "failed!\n", DEV_ZCORE_MAP);
                        rc = -1;
                        goto failed_close_fmap;
                  }
                  chunk = calloc(1, sizeof(struct mem_chunk));
                  if (chunk == NULL) {
                        PRINT_ERR("Could not allocate %d bytes of "
                                "memory\n",
                                (int) sizeof(struct mem_chunk));
                        rc = -1;
                        goto failed_free_chunks;
                  }
                  chunk->size = strtoul(c_info + 17, &end_ptr, 16);
                  if (end_ptr != c_info + 33 || *end_ptr != ' ') {
                        PRINT_ERR("Invalid contents of memory map "
                                "file '%s'!\n", DEV_ZCORE_MAP);
                        rc = -1;
                        goto failed_free_chunks;
                  }
                  if (chunk->size == 0)
                        break;
                  chunk->addr = strtoul(c_info, &end_ptr, 16);
                  if (end_ptr != c_info + 16 || *end_ptr != ' ') {
                        PRINT_ERR("Invalid contents of memory map "
                                "file '%s'!\n", DEV_ZCORE_MAP);
                        rc = -1;
                        goto failed_free_chunks;
                  }
                  if (!chunk_first)
                        chunk_first = chunk;
                  else
                        chunk_prev->next = chunk;
                  chunk_prev = chunk;
            } while (1);
      }

      /* try to open the source device */
      fin = open(DEV_ZCORE, O_RDONLY, 0);
      if (fin == -1) {
            PRINT_ERR("open() source device '%s' failed!\n", DEV_ZCORE);
            rc = -1;
            goto failed_free_chunks;
      }

      /* make the new filename */
      sprintf(dump_name, "%s/dump.%d", g.dump_dir, g.dump_nr);
      fout = open(dump_name, DUMP_FLAGS, DUMP_MODE);
      if (fout == -1) {
            PRINT_ERR("open() of dump file \"%s\" failed!\n", dump_name);
            rc = -1;
            goto failed_close_fin;
      }

      PRINT("dump file: dump.%d\n", g.dump_nr);
      memset(&dh, 0, sizeof(dh));

      /* get the dump header */
      if (lseek(fin, 0, SEEK_SET) < 0) {
            PRINT_ERR("Cannot lseek() to get the dump header from the "
                  "dump file!\n");
            rc = -1;
            goto failed_close_fout;
      }
      if (read(fin, &s390_dh, sizeof(s390_dh)) != sizeof(s390_dh)) {
            PRINT_ERR("Cannot read() dump header from dump file!\n");
            rc = -1;
            goto failed_close_fout;
      }

      s390_to_lkcd_hdr(&s390_dh, &dh);
      if (s390_dh.version >= 5)
            s390_to_lkcd_hdr_asm(&s390_dh, &dh_asm);

      if (strcmp(g.parm_compress, PARM_COMP_GZIP) == 0) {
#ifdef GZIP_SUPPORT
            dh.dump_compress = DUMP_COMPRESS_GZIP;
            compress_fn = compress_gzip;
#else
            PRINT_WARN("No gzip support. Compression disabled!\n");
            dh.dump_compress = DUMP_COMPRESS_NONE;
            compress_fn = compress_none;
#endif
      } else {
            dh.dump_compress = DUMP_COMPRESS_NONE;
            compress_fn = compress_none;
      }

      if (g.parm_mem < dh.memory_size) {
            /* dump_mem parameter specified: Adjust memory size */
            dh.memory_size = g.parm_mem;
            dh.memory_end  = g.parm_mem;
            dh.num_dump_pages = g.parm_mem / dh.page_size;
      }

      memset(page_buf, 0, PAGE_SIZE);
      memcpy(page_buf, &dh, sizeof(dh));
      memcpy(page_buf + sizeof(dh), &dh_asm, sizeof(dh_asm));
      if (lseek(fout, 0L, SEEK_SET) < 0) {
            PRINT_ERR("lseek() failed\n");
            rc = -1;
            goto failed_close_fout;
      }
      if (dump_write(fout, page_buf, PAGE_SIZE) != PAGE_SIZE) {
            PRINT_ERR("Error: Write dump header failed\n");
            rc = -1;
            goto failed_close_fout;
      }
      if (lseek(fout, LKCD_HDR_SIZE, SEEK_SET) < 0) {
            PRINT_ERR("lseek() failed\n");
            rc = -1;
            goto failed_close_fout;
      }

      /* write dump */

      chunk = chunk_first;
      mem_loc = 0;
      mem_count = 0;
      if (lseek(fin, DUMP_HEADER_SZ_S390SA, SEEK_SET) < 0) {
            PRINT_ERR("lseek() failed\n");
            rc = -1;
            goto failed_close_fout;
      }
      while (mem_loc < dh.memory_end) {
            if (mem_loc >= chunk->addr + chunk->size) {
                  chunk = chunk->next;
                  mem_loc = chunk->addr;
                  if (lseek(fin, DUMP_HEADER_SZ_S390SA + mem_loc,
                          SEEK_SET) < 0) {
                        PRINT_ERR("lseek() failed\n");
                        rc = -1;
                        goto failed_close_fout;
                  }
            }
            if (read(fin, buf, PAGE_SIZE) != PAGE_SIZE) {
                  if (errno == EFAULT) {
                        /* probably memory hole. Skip page */
                        mem_loc += PAGE_SIZE;
                        continue;
                  }
                  PRINT_PERR("read error\n");
                  rc = -1;
                  goto failed_close_fout;
            }
            memset(dpcpage, 0, PAGE_SIZE);
            /* get the new compressed page size */

            size = compress_fn((unsigned char *)buf, PAGE_SIZE,
                  (unsigned char *)dpcpage, PAGE_SIZE);

            /* if compression failed or compressed was ineffective,
             * we write an uncompressed page */
            if (size < 0) {
                  dp_flags = DUMP_DH_RAW;
                  dp_size = PAGE_SIZE;
            } else {
                  dp_flags = DUMP_DH_COMPRESSED;
                  dp_size = size;
            }
            dp.address = mem_loc;
            dp.size    = dp_size;
            dp.flags   = dp_flags;
            memcpy(page_buf + buf_loc, &dp, sizeof(dp));
            buf_loc += sizeof(struct dump_page);
            /* copy the page of memory */
            if (dp_flags & DUMP_DH_COMPRESSED)
                  /* copy the compressed page */
                  memcpy(page_buf + buf_loc, dpcpage, dp_size);
            else
                  /* copy directly from memory */
                  memcpy(page_buf + buf_loc, buf, dp_size);
            buf_loc += dp_size;
            mem_loc += PAGE_SIZE;
            mem_count += PAGE_SIZE;
            if (buf_loc + PAGE_SIZE + sizeof(dp) > DUMP_BUF_SIZE) {
                  unsigned long rem = buf_loc % PAGE_SIZE;
                  long size = buf_loc - rem;

                  if (dump_write(fout, page_buf, size) != size) {
                        PRINT_ERR("write error\n");
                        rc = -1;
                        goto failed_close_fout;
                  }
                  memmove(page_buf, page_buf + size, rem);
                  buf_loc = rem;
            }
            show_progress(mem_count, dh.memory_size);
      }

      /* write end marker */

      dp.address = 0x0;
      dp.size    = 0x0;
      dp.flags   = DUMP_DH_END;
      memcpy(page_buf + buf_loc, &dp, sizeof(dp));
      buf_loc += sizeof(dp);
      dump_write(fout, page_buf, buf_loc + (PAGE_SIZE - buf_loc % PAGE_SIZE));
      if (ftruncate(fout, lseek(fout, 0, SEEK_CUR) -
                  (PAGE_SIZE - buf_loc % PAGE_SIZE)) == -1) {
            PRINT_ERR("truncate error\n");
            rc = -1;
            goto failed_close_fout;
      }
      dump_write(fout, &dp, sizeof(dp));

failed_close_fout:
      close(fout);
failed_close_fin:
      close(fin);
failed_free_chunks:
      chunk = chunk_first;
      while (chunk) {
            chunk_prev = chunk;
            chunk = chunk->next;
            free(chunk_prev);
      }
failed_close_fmap:
      close(fmap);
      return rc;
}

/*
 * Load a kernel module
 */
static void modprobe(const char *module)
{
      pid_t pid;

      pid = fork();
      if (pid < 0) {
            PRINT_PERR("fork failed\n");
            return;
      } else if (pid == 0) {
            execl("/bin/modprobe", "modprobe", module, "-q", NULL);
            execl("/sbin/modprobe", "modprobe", module, "-q", NULL);
            exit(1);
      } else {
            waitpid(pid, NULL, 0);
      }
}

/*
 * Load all required kernel modules
 */
static void load_modules(void)
{
      int i;

      for (i = 0; module_list[i]; i++)
            modprobe(module_list[i]);
}

/*
 * main routine of the zfcp_dumper
 */
int main(int argc, char *argv[])
{
      char linux_version[128];

#ifdef __s390x__
      PRINT("Linux System Dumper starting\n");
      PRINT("Version %s (64 bit)\n", ZFCPDUMP_VERSION);
#else
      PRINT("Linux System Dumper starting\n");
      PRINT("Version %s (32 bit)\n", ZFCPDUMP_VERSION);
#endif

      if (init_sig()) {
            PRINT_ERR("Init Signals failed!\n");
            goto fail;
      }
      if (mount("proc", "/proc", "proc", 0, NULL)) {
            if (errno != EBUSY) {
                  PRINT_PERR("Unable to mount proc\n");
                  goto fail;
            }
      }
      read_file("/proc/version", linux_version, sizeof(linux_version));
      PRINT("%s\n", linux_version);

      if (mount("sysfs", "/sys", "sysfs", 0, NULL)) {
            if (errno != EBUSY) {
                  PRINT_PERR("Unable to mount sysfs\n");
                  goto fail;
            }
      }
      if (mount("debugfs", "/sys/kernel/debug", "debugfs", 0, NULL)) {
            if (errno != EBUSY) {
                  PRINT_PERR("Unable to mount debugfs\n");
                  goto fail;
            }
      }
      if (tune_vm()) {
            PRINT_PERR("Unable to set VM settings\n");
            goto fail;
      }
      if (parse_parmline()) {
            PRINT_ERR("Could not parse parmline\n");
            goto fail;
      }
      load_modules();
      if (enable_zfcp_device()) {
            PRINT_ERR("Could not enable dump device\n");
            goto fail;
      }
      PRINT(" \n"); /* leading blank is needed that sclp console prints
                   the newline */
      PRINT("DUMP PARAMETERS:\n");
      PRINT("================\n");
      PRINT("devno    : %s\n", g.dump_devno);
      PRINT("wwpn     : %s\n", g.dump_wwpn);
      PRINT("lun      : %s\n", g.dump_lun);
      PRINT("conf     : %s\n", g.dump_bootprog);
      PRINT("partition: %s\n", g.parm_part);
      PRINT("directory: %s\n", g.parm_dir);
      PRINT("compress : %s\n", g.parm_compress);
      PRINT(" \n");
      PRINT("MOUNT DUMP PARTITION:\n");
      PRINT("=====================\n");
      sleep(WAIT_TIME_ONLINE);
      if (mount_dump_device()) {
            PRINT_ERR("Could not mount dump device\n");
            goto fail;
      }
      PRINT("DONE.\n");
      PRINT(" \n");
      PRINT("DUMP PROCESS STARTED:\n");
      PRINT("=====================\n");

      if (create_dump())
            goto fail_umount;

      if (umount_dump_device()) {
            PRINT_ERR("Could not umount dump device\n");
            goto fail;
      }
      PRINT(" \n");
      PRINT("DUMP 'dump.%i' COMPLETE\n", g.dump_nr);
      fflush(stdout);
      terminate();
      return 0;

fail_umount:
      if (umount_dump_device()) {
            PRINT_ERR("Could not umount dump device\n");
            goto fail;
      }
fail:
      PRINT("DUMP 'dump.%i' FAILED\n", g.dump_nr);
      fflush(stdout);
      terminate();
      return 1;
}

Generated by  Doxygen 1.6.0   Back to index