From: Petr Tesarik <ptesarik@suse.cz>
Subject: Allow makedump parallelism
References: bnc#783592

Signed-off-by: Petr Tesarik <ptesarik@suse.cz>

---
 doc/man/kdump.5.txt.in     |   21 ++++++++++
 init/boot.kdump            |    2 -
 kdumptool/configuration.cc |   15 +++++++
 kdumptool/configuration.h  |   10 +++++
 kdumptool/dataprovider.cc  |   12 ++++--
 kdumptool/dataprovider.h   |    6 +--
 kdumptool/savedump.cc      |   15 +++++++
 kdumptool/transfer.cc      |   89 +++++++++++++++++++++++++++------------------
 kdumptool/transfer.h       |   34 +++++++++++------
 sysconfig.kdump            |   14 +++++++
 10 files changed, 162 insertions(+), 56 deletions(-)

--- a/doc/man/kdump.5.txt.in
+++ b/doc/man/kdump.5.txt.in
@@ -124,6 +124,27 @@ load the normal initrd that would not sa
 
 
 
+KDUMP_CPUS
+~~~~~~~~~~
+
+If dumping is CPU-bound on your system, you can parallelize it on an SMP
+system. Keep in mind that this increases the stress on the I/O subsystem,
+so you probably don't get the maximum speed if you set this parameter to
+the number of CPUs in your system.
+
+This parameter modifies the _maxcpus_ parameter of the kdump kernel and
+the number of *makedumpfile*(8) processes created.
+
+*Note:* This parameter does not work properly for the _ELF_ format,
+because *makedumpfile*(8) does not support split _ELF_ dump files.
+
+WARNING: SETTING THIS NUMBER TO A NUMBER HIGHER THAN 1 IS UNSUPPORTED!
+SOME MACHINES ARE KNOWN TO HANG OR REBOOT RANDOMLY DURING INITIALISATION
+OF THE DUMP KERNEL IF KDUMP_CPUS > 1.
+
+Default is 1.
+
+
 KDUMP_COMMANDLINE
 ~~~~~~~~~~~~~~~~~
 
--- a/init/boot.kdump
+++ b/init/boot.kdump
@@ -91,7 +91,7 @@ function build_kdump_commandline()
         commandline="$commandline elevator=deadline sysrq=yes reset_devices"
         case `uname -i` in
         i?86|x86_64|ia64)
-            commandline="$commandline irqpoll maxcpus=1"
+            commandline="$commandline irqpoll maxcpus=${KDUMP_CPUS:-1}"
             ;;
         esac
 
--- a/kdumptool/configuration.cc
+++ b/kdumptool/configuration.cc
@@ -39,7 +39,7 @@ Configuration *Configuration::config()
 // -----------------------------------------------------------------------------
 Configuration::Configuration()
     throw ()
-    : m_readConfig(false), m_kernelVersion(""), m_commandLine(""),
+    : m_readConfig(false), m_kernelVersion(""), m_CPUs(1), m_commandLine(""),
       m_commandLineAppend(""), m_kexecOptions(""), m_makedumpfileOptions(""),
       m_immediateReboot(true), m_customTransfer(""), m_savedir("/var/log/dump"),
       m_keepOldDumps(0), m_freeDiskSize(64), m_verbose(0), m_dumpLevel(0),
@@ -55,6 +55,7 @@ void Configuration::readFile(const strin
 {
     ConfigParser cp(filename);
     cp.addVariable("KDUMP_KERNELVER");
+    cp.addVariable("KDUMP_CPUS");
     cp.addVariable("KDUMP_COMMANDLINE");
     cp.addVariable("KDUMP_COMMANDLINE_APPEND");
     cp.addVariable("KEXEC_OPTIONS");
@@ -82,6 +83,7 @@ void Configuration::readFile(const strin
     cp.parse();
 
     m_kernelVersion = cp.getValue("KDUMP_KERNELVER");
+    m_CPUs = cp.getIntValue("KDUMP_CPUS");
     m_commandLine = cp.getValue("KDUMP_COMMANDLINE");
     m_commandLineAppend = cp.getValue("KDUMP_COMMANDLINE_APPEND");
     m_kexecOptions = cp.getValue("KEXEC_OPTIONS");
@@ -121,6 +123,17 @@ string Configuration::getKernelVersion()
 }
 
 // -----------------------------------------------------------------------------
+int Configuration::getCPUs() const
+    throw (KError)
+{
+    if (!m_readConfig)
+        throw KError("Configuration has not been read.");
+
+    return m_CPUs;
+}
+
+
+// -----------------------------------------------------------------------------
 string Configuration::getCommandLine() const
     throw (KError)
 {
--- a/kdumptool/configuration.h
+++ b/kdumptool/configuration.h
@@ -78,6 +78,15 @@ class Configuration {
         std::string getKernelVersion() const
         throw (KError);
 
+	/**
+	 * Returns the value of KDUMP_CPUS.
+	 *
+	 * @return the desired parallelism
+	 * @exception KError if Configuration::readFile() was not called
+	 */
+	int getCPUs() const
+        throw (KError);
+
         /**
          * Returns the value of KDUMP_COMMANDLINE.
          *
@@ -322,6 +331,7 @@ class Configuration {
         static Configuration *m_instance;
         bool m_readConfig;
         std::string m_kernelVersion;
+        int m_CPUs;
         std::string m_commandLine;
         std::string m_commandLineAppend;
         std::string m_kexecOptions;
--- a/kdumptool/savedump.cc
+++ b/kdumptool/savedump.cc
@@ -44,6 +44,7 @@ using std::cout;
 using std::endl;
 using std::auto_ptr;
 using std::stringstream;
+using std::ostringstream;
 using std::ifstream;
 
 #define KERNELCOMMANDLINE "/proc/cmdline"
@@ -295,6 +296,8 @@ void SaveDump::saveDump()
             cmdline << "-c ";
         if (useLZO)
             cmdline << "-l ";
+	if (!useElf && config->getCPUs() > 1)
+	    cmdline << "--split ";
         cmdline << m_dump;
 
         string directCmdline = cmdline.str();
@@ -316,7 +319,17 @@ void SaveDump::saveDump()
             provider->setProgress(&progress);
         else
             cout << "Saving dump ..." << endl;
-        m_transfer->perform(provider, "vmcore", &m_usedDirectSave);
+	if (config->getCPUs() > 1) {
+	    StringVector targets;
+	    for (int i = 1; i <= config->getCPUs(); ++i) {
+		ostringstream ss;
+		ss << "vmcore" << i;
+		targets.push_back(ss.str());
+	    }
+	    m_transfer->perform(provider, targets, &m_usedDirectSave);
+	} else {
+	    m_transfer->perform(provider, "vmcore", &m_usedDirectSave);
+	}
         if (m_useMakedumpfile)
             terminal.printLine();
     } catch (...) {
--- a/kdumptool/transfer.cc
+++ b/kdumptool/transfer.cc
@@ -53,6 +53,18 @@ using std::endl;
 
 #define DEFAULT_MOUNTPOINT "/mnt"
 
+//{{{ Transfer -----------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+void Transfer::perform(DataProvider *dataprovider,
+		       const std::string &target_file,
+		       bool *directSave)
+    throw (KError)
+{
+    const StringVector target_files(1, target_file);
+    perform(dataprovider, target_files, directSave);
+}
+
 //{{{ URLTransfer --------------------------------------------------------------
 
 // -----------------------------------------------------------------------------
@@ -148,22 +160,26 @@ FileTransfer::~FileTransfer()
 
 // -----------------------------------------------------------------------------
 void FileTransfer::perform(DataProvider *dataprovider,
-                           const char *target_file,
+                           const StringVector &target_files,
                            bool *directSave)
     throw (KError)
 {
-    Debug::debug()->trace("FileTransfer::perform(%p, %s)",
-        dataprovider, target_file);
-
-    string fullTarget = FileUtil::pathconcat(
-        getURLParser().getPath(), target_file);
+    Debug::debug()->trace("FileTransfer::perform(%p, [ \"%s\"%s ])",
+	dataprovider, target_files.front().c_str(),
+	target_files.size() > 1 ? ", ..." : "");
+
+    StringVector full_targets;
+    StringVector::const_iterator it;
+    for (it = target_files.begin(); it != target_files.end(); ++it)
+	full_targets.push_back(FileUtil::pathconcat(
+	    getURLParser().getPath(), *it));
 
     if (dataprovider->canSaveToFile()) {
-        performFile(dataprovider, fullTarget.c_str());
+        performFile(dataprovider, full_targets);
         if (directSave)
             *directSave = true;
     } else {
-        performPipe(dataprovider, fullTarget.c_str());
+        performPipe(dataprovider, full_targets);
         if (directSave)
             *directSave = false;
     }
@@ -171,24 +187,26 @@ void FileTransfer::perform(DataProvider
 
 // -----------------------------------------------------------------------------
 void FileTransfer::performFile(DataProvider *dataprovider,
-                               const char *target_file)
+                               const StringVector &target_files)
     throw (KError)
 {
-    Debug::debug()->trace("FileTransfer::performFile(%p, %s)",
-        dataprovider, target_file);
+    Debug::debug()->trace("FileTransfer::performFile(%p, [ \"%s\"%s ])",
+	dataprovider, target_files.front().c_str(),
+	target_files.size() > 1 ? ", ..." : "");
 
-    dataprovider->saveToFile(target_file);
+    dataprovider->saveToFile(target_files);
 }
 
 // -----------------------------------------------------------------------------
 void FileTransfer::performPipe(DataProvider *dataprovider,
-                               const char *target_file)
+                               const StringVector &target_files)
     throw (KError)
 {
-    Debug::debug()->trace("FileTransfer::performPipe(%p, %s)",
-        dataprovider, target_file);
+    Debug::debug()->trace("FileTransfer::performPipe(%p, [ \"%s\"%s ])",
+	dataprovider, target_files.front().c_str(),
+	target_files.size() > 1 ? ", ..." : "");
 
-    FILE *fp = open(target_file);
+    FILE *fp = open(target_files.front());
     bool sparse = !Configuration::config()->kdumptoolContainsFlag("NOSPARSE");
     if (!sparse)
         Debug::debug()->info("Creation of sparse files disabled in "
@@ -252,14 +270,14 @@ void FileTransfer::performPipe(DataProvi
 }
 
 // -----------------------------------------------------------------------------
-FILE *FileTransfer::open(const char *target_file)
+FILE *FileTransfer::open(const string &target_file)
     throw (KError)
 {
-    Debug::debug()->trace("FileTransfer::open(%s)", target_file);
+    Debug::debug()->trace("FileTransfer::open(%s)", target_file.c_str());
 
-    FILE *fp = fopen(target_file, "w");
+    FILE *fp = fopen(target_file.c_str(), "w");
     if (!fp)
-        throw KSystemError("Error in fopen for " + string(target_file), errno);
+        throw KSystemError("Error in fopen for " + target_file, errno);
 
     return fp;
 }
@@ -389,16 +407,17 @@ FTPTransfer::~FTPTransfer()
 
 // -----------------------------------------------------------------------------
 void FTPTransfer::perform(DataProvider *dataprovider,
-                          const char *target_file,
+                          const StringVector &target_files,
                           bool *directSave)
     throw (KError)
 {
-    Debug::debug()->trace("FTPTransfer::perform(%p, %s)",
-        dataprovider, target_file);
+    Debug::debug()->trace("FTPTransfer::perform(%p, [ \"%s\"%s ])",
+	dataprovider, target_files.front().c_str(),
+	target_files.size() > 1 ? ", ..." : "");
 
     if (directSave)
         *directSave = false;
-    open(dataprovider, target_file);
+    open(dataprovider, target_files.front().c_str());
 
     try {
         dataprovider->prepare();
@@ -417,13 +436,13 @@ void FTPTransfer::perform(DataProvider *
 
 // -----------------------------------------------------------------------------
 void FTPTransfer::open(DataProvider *dataprovider,
-                        const char *target_file)
+                        const string &target_file)
     throw (KError)
 {
     CURLcode err;
 
     Debug::debug()->trace("FTPTransfer::open(%p, %s)", dataprovider,
-        target_file);
+        target_file.c_str());
 
     // set the URL
     string full_url = FileUtil::pathconcat(getURLParser().getURL(), target_file);
@@ -654,19 +673,21 @@ void SFTPTransfer::mkdir(const string &d
 
 /* -------------------------------------------------------------------------- */
 void SFTPTransfer::perform(DataProvider *dataprovider,
-                           const char *target_file,
+                           const StringVector &target_files,
                            bool *directSave)
     throw (KError)
 {
-    Debug::debug()->trace("SFTPTransfer::perform(%p, %s)",
-        dataprovider, target_file);
+    Debug::debug()->trace("SFTPTransfer::perform(%p, [ \"%s\"%s ])",
+	dataprovider, target_files.front().c_str(),
+	target_files.size() > 1 ? ", ..." : "");
 
     bool prepared = false;
     if (directSave)
         *directSave = false;
 
     LIBSSH2_SFTP_HANDLE  *handle = NULL;
-    string file = FileUtil::pathconcat(getURLParser().getPath(), target_file);
+    string file = FileUtil::pathconcat(getURLParser().getPath(),
+	target_files.front());
 
     Debug::debug()->dbg("Using target file %s.", file.c_str());
 
@@ -772,11 +793,11 @@ NFSTransfer::~NFSTransfer()
 
 // -----------------------------------------------------------------------------
 void NFSTransfer::perform(DataProvider *dataprovider,
-                          const char *target_file,
+                          const StringVector &target_files,
                           bool *directSave)
     throw (KError)
 {
-    m_fileTransfer->perform(dataprovider, target_file, directSave);
+    m_fileTransfer->perform(dataprovider, target_files, directSave);
 }
 
 // -----------------------------------------------------------------------------
@@ -851,11 +872,11 @@ CIFSTransfer::~CIFSTransfer()
 
 // -----------------------------------------------------------------------------
 void CIFSTransfer::perform(DataProvider *dataprovider,
-                          const char *target_file,
+                          const StringVector &target_files,
                           bool *directSave)
     throw (KError)
 {
-    m_fileTransfer->perform(dataprovider, target_file, directSave);
+    m_fileTransfer->perform(dataprovider, target_files, directSave);
 }
 
 // -----------------------------------------------------------------------------
--- a/kdumptool/transfer.h
+++ b/kdumptool/transfer.h
@@ -62,18 +62,28 @@ class Transfer {
          * DataProvider objects.
          *
          * @param[in] dataprovider the data provider
-         * @param[in] target_url the URL for the target directory, i.e. without
-         *            the file name
          * @param[in] target_file the actual file name for the target
          * @param[out] directSave if the transfer used
          *             DataProvider::saveToFile()
          * @exception KError on any error
          */
         virtual void perform(DataProvider *dataprovider,
-                             const char *target_file,
+                             const StringVector &target_files,
                              bool *directSave=NULL)
         throw (KError) = 0;
 
+	/**
+	 * Shorthand version of perform() if there is only one file
+	 *
+	 * @param[in] dataprovider the data provider
+	 * @param[in] target_file the file name for the target
+	 * @param[out] directSave @see Transfer::perform()
+         * @exception KError on any error
+	 */
+	void perform(DataProvider *dataprovider,
+		     const std::string &target_file,
+		     bool *directSave=NULL)
+	throw (KError);
 };
 
 //}}}
@@ -156,21 +166,21 @@ class FileTransfer : public URLTransfer
          * @see Transfer::perform()
          */
         void perform(DataProvider *dataprovider,
-                     const char *target_file,
+                     const StringVector &target_files,
                      bool *directSave)
         throw (KError);
 
     protected:
 
         void performFile(DataProvider *dataprovider,
-                         const char *target_file)
+                         const StringVector &target_files)
         throw (KError);
 
         void performPipe(DataProvider *dataprovider,
-                         const char *target_file)
+                         const StringVector &target_files)
         throw (KError);
 
-        FILE *open(const char *target_file)
+        FILE *open(const std::string &target_file)
         throw (KError);
 
         void close(FILE *fp)
@@ -211,14 +221,14 @@ class FTPTransfer : public URLTransfer {
          * @see Transfer::perform()
          */
         void perform(DataProvider *dataprovider,
-                     const char *target_file,
+                     const StringVector &target_files,
                      bool *directSave)
         throw (KError);
 
     protected:
 
         void open(DataProvider *dataprovider,
-                   const char *target_file)
+		  const std::string &target_file)
         throw (KError);
 
     private:
@@ -259,7 +269,7 @@ class SFTPTransfer : public URLTransfer
          * @see Transfer::perform()
          */
         void perform(DataProvider *dataprovider,
-                     const char *target_file,
+                     const StringVector &target_files,
                      bool *directSave)
         throw (KError);
 
@@ -313,7 +323,7 @@ class NFSTransfer : public URLTransfer {
          * @see Transfer::perform()
          */
         void perform(DataProvider *dataprovider,
-                     const char *target_file,
+                     const StringVector &target_files,
                      bool *directSave)
         throw (KError);
 
@@ -358,7 +368,7 @@ class CIFSTransfer : public URLTransfer
          * @see Transfer::perform()
          */
         void perform(DataProvider *dataprovider,
-                     const char *target_file,
+                     const StringVector &target_files,
                      bool *directSave)
         throw (KError);
 
--- a/sysconfig.kdump
+++ b/sysconfig.kdump
@@ -12,6 +12,20 @@
 #
 KDUMP_KERNELVER=""
 
+## Type:	integer
+## Default:	1
+## ServiceRestart:	kdump
+#
+# Number of CPUs to be used in the kdump environment. You may want to
+# increase the number if computing power is the bottleneck in your setup.
+# WARNING: SETTING THIS NUMBER TO A NUMBER HIGHER THAN 1 IS UNSUPPORTED!
+#          SOME MACHINES ARE KNOWN TO HANG OR REBOOT RANDOMLY DURING
+#          INITIALISATION OF THE DUMP KERNEL IF KDUMP_CPUS > 1.
+#
+# See also: kdump(5).
+#
+KDUMP_CPUS=1
+
 ## Type:	string
 ## Default:	""
 ## ServiceRestart:	kdump
--- a/kdumptool/dataprovider.cc
+++ b/kdumptool/dataprovider.cc
@@ -50,7 +50,7 @@ bool AbstractDataProvider::canSaveToFile
 }
 
 // -----------------------------------------------------------------------------
-void AbstractDataProvider::saveToFile(const char *target)
+void AbstractDataProvider::saveToFile(const StringVector &targets)
     throw (KError)
 {
     throw KError("That DataProvider cannot save to a file.");
@@ -271,12 +271,16 @@ bool ProcessDataProvider::canSaveToFile(
 }
 
 // -----------------------------------------------------------------------------
-void ProcessDataProvider::saveToFile(const char *target)
+void ProcessDataProvider::saveToFile(const StringVector &targets)
     throw (KError)
 {
-    Debug::debug()->trace("ProcessDataProvider::saveToFile(%s)", target);
+    Debug::debug()->trace("ProcessDataProvider::saveToFile([ \"%s\"%s ])",
+	targets.front().c_str(), targets.size() > 1 ? ", ...": "");
 
-    string cmdline = m_directCmdline + " " + target;
+    string cmdline = m_directCmdline;
+    StringVector::const_iterator it;
+    for (it = targets.begin(); it != targets.end(); ++it)
+	cmdline += ' ' + *it;
     Debug::debug()->trace("Executing '%s'", cmdline.c_str());
 
     int err = system(cmdline.c_str());
--- a/kdumptool/dataprovider.h
+++ b/kdumptool/dataprovider.h
@@ -72,7 +72,7 @@ class DataProvider {
          * @exception KError if saving failed or DataProvider::canSaveToFile()
          *            returns @c false.
          */
-        virtual void saveToFile(const char *target)
+        virtual void saveToFile(const StringVector &targets)
         throw (KError) = 0;
 
         /**
@@ -180,7 +180,7 @@ class AbstractDataProvider : public Data
          *            returns @c false in AbstractDataProvider.
          * @see DataProvider::saveToFile().
          */
-        void saveToFile(const char *target)
+        void saveToFile(const StringVector &targets)
         throw (KError);
 
         /**
@@ -321,7 +321,7 @@ class ProcessDataProvider : public Abstr
          * @param[in] target the target file
          * @param KError if saving to the file failed
          */
-        void saveToFile(const char *target)
+        void saveToFile(const StringVector &targets)
         throw (KError);
 
         /**
