oem-ibm: Modified to write system dump data on unix domain socket.

Currently pldm writes dump data on nbd device.

This commit is to enable dump offload using UNIX socket
On dump offload request, pldm setup a UNIX socket and
write data on socket, webserver connects to this socket
and reads data to offload

Tested By:
1. Offloaded system dump
2. Ran below pldmtool commands for performance test
   while offloading system dump, i don't see an delay
   pldmtool bios GetBIOSTable -t 1
   pldmtool bios GetBIOSTable -t 2

Signed-off-by: Ravi Teja <raviteja28031990@gmail.com>
Change-Id: Iad8863d87c3b04a8dd588f1f0239f65fcb59f38b
diff --git a/oem/ibm/libpldmresponder/file_io.cpp b/oem/ibm/libpldmresponder/file_io.cpp
index d0595e3..6b963d0 100644
--- a/oem/ibm/libpldmresponder/file_io.cpp
+++ b/oem/ibm/libpldmresponder/file_io.cpp
@@ -4,9 +4,9 @@
 
 #include "libpldm/base.h"
 
-#include "common/utils.hpp"
 #include "file_io_by_type.hpp"
 #include "file_table.hpp"
+#include "utils.hpp"
 #include "xyz/openbmc_project/Common/error.hpp"
 
 #include <fcntl.h>
@@ -22,7 +22,7 @@
 
 namespace pldm
 {
-
+using namespace pldm::responder::utils;
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
 
 namespace responder
@@ -49,6 +49,71 @@
 
 constexpr auto xdmaDev = "/dev/aspeed-xdma";
 
+int DMA::transferHostDataToSocket(int fd, uint32_t length, uint64_t address)
+{
+    static const size_t pageSize = getpagesize();
+    uint32_t numPages = length / pageSize;
+    uint32_t pageAlignedLength = numPages * pageSize;
+
+    if (length > pageAlignedLength)
+    {
+        pageAlignedLength += pageSize;
+    }
+
+    auto mmapCleanup = [pageAlignedLength](void* vgaMem) {
+        munmap(vgaMem, pageAlignedLength);
+    };
+
+    int dmaFd = -1;
+    int rc = 0;
+    dmaFd = open(xdmaDev, O_RDWR);
+    if (dmaFd < 0)
+    {
+        rc = -errno;
+        std::cerr << "Failed to open the XDMA device, RC=" << rc << "\n";
+        return rc;
+    }
+
+    pldm::utils::CustomFD xdmaFd(dmaFd);
+
+    void* vgaMem;
+    vgaMem =
+        mmap(nullptr, pageAlignedLength, PROT_READ, MAP_SHARED, xdmaFd(), 0);
+    if (MAP_FAILED == vgaMem)
+    {
+        rc = -errno;
+        std::cerr << "Failed to mmap the XDMA device, RC=" << rc << "\n";
+        return rc;
+    }
+
+    std::unique_ptr<void, decltype(mmapCleanup)> vgaMemPtr(vgaMem, mmapCleanup);
+
+    AspeedXdmaOp xdmaOp;
+    xdmaOp.upstream = 0;
+    xdmaOp.hostAddr = address;
+    xdmaOp.len = length;
+
+    rc = write(xdmaFd(), &xdmaOp, sizeof(xdmaOp));
+    if (rc < 0)
+    {
+        rc = -errno;
+        std::cerr << "Failed to execute the DMA operation, RC=" << rc
+                  << " ADDRESS=" << address << " LENGTH=" << length << "\n";
+        return rc;
+    }
+
+    rc = writeToUnixSocket(fd, static_cast<const char*>(vgaMemPtr.get()),
+                           length);
+    if (rc < 0)
+    {
+        rc = -errno;
+        close(fd);
+        std::cerr << "closing socket" << std::endl;
+        return rc;
+    }
+    return 0;
+}
+
 int DMA::transferDataHost(int fd, uint32_t offset, uint32_t length,
                           uint64_t address, bool upstream)
 {
diff --git a/oem/ibm/libpldmresponder/file_io.hpp b/oem/ibm/libpldmresponder/file_io.hpp
index aba70ba..771d038 100644
--- a/oem/ibm/libpldmresponder/file_io.hpp
+++ b/oem/ibm/libpldmresponder/file_io.hpp
@@ -58,6 +58,16 @@
      */
     int transferDataHost(int fd, uint32_t offset, uint32_t length,
                          uint64_t address, bool upstream);
+
+    /** @brief API to transfer data on to unix socket from host using DMA
+     *
+     * @param[in] path     - pathname of the file to transfer data from or to
+     * @param[in] length   - length of the data to transfer
+     * @param[in] address  - DMA address on the host
+     *
+     * @return returns 0 on success, negative errno on failure
+     */
+    int transferHostDataToSocket(int fd, uint32_t length, uint64_t address);
 };
 
 /** @brief Transfer the data between BMC and host using DMA.
diff --git a/oem/ibm/libpldmresponder/file_io_by_type.cpp b/oem/ibm/libpldmresponder/file_io_by_type.cpp
index d6367ba..43c2b91 100644
--- a/oem/ibm/libpldmresponder/file_io_by_type.cpp
+++ b/oem/ibm/libpldmresponder/file_io_by_type.cpp
@@ -51,6 +51,25 @@
     return rc < 0 ? PLDM_ERROR : PLDM_SUCCESS;
 }
 
+int FileHandler::transferFileDataToSocket(int32_t fd, uint32_t& length,
+                                          uint64_t address)
+{
+    dma::DMA xdmaInterface;
+    while (length > dma::maxSize)
+    {
+        auto rc =
+            xdmaInterface.transferHostDataToSocket(fd, dma::maxSize, address);
+        if (rc < 0)
+        {
+            return PLDM_ERROR;
+        }
+        length -= dma::maxSize;
+        address += dma::maxSize;
+    }
+    auto rc = xdmaInterface.transferHostDataToSocket(fd, length, address);
+    return rc < 0 ? PLDM_ERROR : PLDM_SUCCESS;
+}
+
 int FileHandler::transferFileData(const fs::path& path, bool upstream,
                                   uint32_t offset, uint32_t& length,
                                   uint64_t address)
diff --git a/oem/ibm/libpldmresponder/file_io_by_type.hpp b/oem/ibm/libpldmresponder/file_io_by_type.hpp
index 73f27d8..6bb87c6 100644
--- a/oem/ibm/libpldmresponder/file_io_by_type.hpp
+++ b/oem/ibm/libpldmresponder/file_io_by_type.hpp
@@ -98,6 +98,9 @@
     virtual int transferFileData(int fd, bool upstream, uint32_t offset,
                                  uint32_t& length, uint64_t address);
 
+    virtual int transferFileDataToSocket(int fd, uint32_t& length,
+                                         uint64_t address);
+
     /** @brief Constructor to create a FileHandler object
      */
     FileHandler(uint32_t fileHandle) : fileHandle(fileHandle)
diff --git a/oem/ibm/libpldmresponder/file_io_type_dump.cpp b/oem/ibm/libpldmresponder/file_io_type_dump.cpp
index 7fe4944..38647ab 100644
--- a/oem/ibm/libpldmresponder/file_io_type_dump.cpp
+++ b/oem/ibm/libpldmresponder/file_io_type_dump.cpp
@@ -4,6 +4,7 @@
 #include "oem/ibm/libpldm/file_io.h"
 
 #include "common/utils.hpp"
+#include "utils.hpp"
 #include "xyz/openbmc_project/Common/error.hpp"
 
 #include <stdint.h>
@@ -17,6 +18,7 @@
 #include <filesystem>
 #include <iostream>
 
+using namespace pldm::responder::utils;
 using namespace pldm::utils;
 
 namespace pldm
@@ -24,10 +26,8 @@
 namespace responder
 {
 
-static constexpr auto nbdInterfaceDefault = "/dev/nbd1";
 static constexpr auto dumpEntry = "xyz.openbmc_project.Dump.Entry";
 static constexpr auto dumpObjPath = "/xyz/openbmc_project/dump/system";
-
 int DumpHandler::fd = -1;
 
 static std::string findDumpObjPath(uint32_t fileHandle)
@@ -106,11 +106,13 @@
         return {};
     }
 
-    std::string nbdInterface{};
+    std::string socketInterface{};
+
     try
     {
-        nbdInterface = pldm::utils::DBusHandler().getDbusProperty<std::string>(
-            path.c_str(), "OffloadUri", dumpEntry);
+        socketInterface =
+            pldm::utils::DBusHandler().getDbusProperty<std::string>(
+                path.c_str(), "OffloadUri", dumpEntry);
     }
     catch (const std::exception& e)
     {
@@ -118,74 +120,45 @@
                   << e.what() << "\n";
     }
 
-    if (nbdInterface == "")
-    {
-        nbdInterface = nbdInterfaceDefault;
-    }
-
-    return nbdInterface;
+    return socketInterface;
 }
 
-int DumpHandler::writeFromMemory(uint32_t offset, uint32_t length,
-                                 uint64_t address)
+int DumpHandler::writeFromMemory(uint32_t, uint32_t length, uint64_t address)
 {
-    auto nbdInterface = getOffloadUri(fileHandle);
-    if (nbdInterface.empty())
-    {
-        return PLDM_ERROR;
-    }
-
-    int flags = O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE;
     if (DumpHandler::fd == -1)
     {
-        DumpHandler::fd = open(nbdInterface.c_str(), flags);
-        if (DumpHandler::fd == -1)
+        auto socketInterface = getOffloadUri(fileHandle);
+        int sock = setupUnixSocket(socketInterface);
+        if (sock < 0)
         {
-            std::cerr << "NBD file does not exist at " << nbdInterface
-                      << " ERROR=" << errno << "\n";
+            sock = -errno;
+            close(DumpHandler::fd);
+            std::cerr
+                << "DumpHandler::writeFromMemory: setupUnixSocket() failed"
+                << std::endl;
+            std::remove(socketInterface.c_str());
             return PLDM_ERROR;
         }
-    }
 
-    return transferFileData(DumpHandler::fd, false, offset, length, address);
+        DumpHandler::fd = sock;
+    }
+    return transferFileDataToSocket(DumpHandler::fd, length, address);
 }
 
-int DumpHandler::write(const char* buffer, uint32_t offset, uint32_t& length)
+int DumpHandler::write(const char* buffer, uint32_t, uint32_t& length)
 {
-    auto nbdInterface = getOffloadUri(fileHandle);
-    if (nbdInterface.empty())
+    int rc = writeToUnixSocket(DumpHandler::fd, buffer, length);
+    if (rc < 0)
     {
+        rc = -errno;
+        close(DumpHandler::fd);
+        auto socketInterface = getOffloadUri(fileHandle);
+        std::remove(socketInterface.c_str());
+        std::cerr << "DumpHandler::write: writeToUnixSocket() failed"
+                  << std::endl;
         return PLDM_ERROR;
     }
 
-    int flags = O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE;
-    if (DumpHandler::fd == -1)
-    {
-        DumpHandler::fd = open(nbdInterface.c_str(), flags);
-        if (DumpHandler::fd == -1)
-        {
-            std::cerr << "NBD file does not exist at " << nbdInterface
-                      << " ERROR=" << errno << "\n";
-            return PLDM_ERROR;
-        }
-    }
-
-    int rc = lseek(DumpHandler::fd, offset, SEEK_SET);
-    if (rc == -1)
-    {
-        std::cerr << "lseek failed, ERROR=" << errno << ", OFFSET=" << offset
-                  << "\n";
-        return PLDM_ERROR;
-    }
-    rc = ::write(DumpHandler::fd, buffer, length);
-    if (rc == -1)
-    {
-        std::cerr << "file write failed, ERROR=" << errno
-                  << ", LENGTH=" << length << ", OFFSET=" << offset << "\n";
-        return PLDM_ERROR;
-    }
-    length = rc;
-
     return PLDM_SUCCESS;
 }
 
@@ -208,7 +181,10 @@
                     << "failed to make a d-bus call to DUMP manager, ERROR="
                     << e.what() << "\n";
             }
+
             close(DumpHandler::fd);
+            auto socketInterface = getOffloadUri(fileHandle);
+            std::remove(socketInterface.c_str());
             DumpHandler::fd = -1;
             return PLDM_SUCCESS;
         }
diff --git a/oem/ibm/libpldmresponder/utils.cpp b/oem/ibm/libpldmresponder/utils.cpp
new file mode 100644
index 0000000..b8bbdf0
--- /dev/null
+++ b/oem/ibm/libpldmresponder/utils.cpp
@@ -0,0 +1,146 @@
+#include "utils.hpp"
+
+#include "libpldm/base.h"
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <iostream>
+
+namespace pldm
+{
+namespace responder
+{
+namespace utils
+{
+
+int setupUnixSocket(const std::string& socketInterface)
+{
+    int sock;
+    struct sockaddr_un addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    if (strnlen(socketInterface.c_str(), sizeof(addr.sun_path)) ==
+        sizeof(addr.sun_path))
+    {
+        std::cerr << "setupUnixSocket: UNIX socket path too long" << std::endl;
+        return -1;
+    }
+
+    strncpy(addr.sun_path, socketInterface.c_str(), sizeof(addr.sun_path) - 1);
+
+    if ((sock = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1)
+    {
+        std::cerr << "setupUnixSocket: socket() call failed" << std::endl;
+        return -1;
+    }
+
+    if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1)
+    {
+        std::cerr << "setupUnixSocket: bind() call failed" << std::endl;
+        close(sock);
+        return -1;
+    }
+
+    if (listen(sock, 1) == -1)
+    {
+        std::cerr << "setupUnixSocket: listen() call failed" << std::endl;
+        close(sock);
+        return -1;
+    }
+
+    fd_set rfd;
+    struct timeval tv;
+    tv.tv_sec = 1;
+    tv.tv_usec = 0;
+
+    FD_ZERO(&rfd);
+    FD_SET(sock, &rfd);
+    int nfd = sock + 1;
+    int fd;
+
+    int retval = select(nfd, &rfd, NULL, NULL, &tv);
+    if (retval < 0)
+    {
+        std::cerr << "setupUnixSocket: select call failed " << errno
+                  << std::endl;
+        close(sock);
+        return -1;
+    }
+
+    if ((retval > 0) && (FD_ISSET(sock, &rfd)))
+    {
+        fd = accept(sock, NULL, NULL);
+        if (fd < 0)
+        {
+            std::cerr << "setupUnixSocket: accept() call failed " << errno
+                      << std::endl;
+            close(sock);
+            return -1;
+        }
+        close(sock);
+    }
+    return fd;
+}
+
+int writeToUnixSocket(const int sock, const char* buf, const uint64_t blockSize)
+{
+    uint64_t i;
+    int nwrite = 0;
+
+    for (i = 0; i < blockSize; i = i + nwrite)
+    {
+
+        fd_set wfd;
+        struct timeval tv;
+        tv.tv_sec = 1;
+        tv.tv_usec = 0;
+
+        FD_ZERO(&wfd);
+        FD_SET(sock, &wfd);
+        int nfd = sock + 1;
+
+        int retval = select(nfd, NULL, &wfd, NULL, &tv);
+        if (retval < 0)
+        {
+            std::cerr << "writeToUnixSocket: select call failed " << errno
+                      << std::endl;
+            close(sock);
+            return -1;
+        }
+        if (retval == 0)
+        {
+            nwrite = 0;
+            continue;
+        }
+        if ((retval > 0) && (FD_ISSET(sock, &wfd)))
+        {
+            nwrite = write(sock, buf + i, blockSize - i);
+            if (nwrite < 0)
+            {
+                if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+                {
+                    std::cerr << "writeToUnixSocket: Write call failed with "
+                                 "EAGAIN or EWOULDBLOCK or EINTR"
+                              << std::endl;
+                    nwrite = 0;
+                    continue;
+                }
+                std::cerr << "writeToUnixSocket: Failed to write" << std::endl;
+                close(sock);
+                return -1;
+            }
+        }
+        else
+        {
+            nwrite = 0;
+        }
+    }
+    return 0;
+}
+
+} // namespace utils
+} // namespace responder
+} // namespace pldm
diff --git a/oem/ibm/libpldmresponder/utils.hpp b/oem/ibm/libpldmresponder/utils.hpp
new file mode 100644
index 0000000..8f9ac47
--- /dev/null
+++ b/oem/ibm/libpldmresponder/utils.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <string>
+
+namespace pldm
+{
+namespace responder
+{
+namespace utils
+{
+
+/** @brief Setup UNIX socket
+ *  This function creates listening socket in non-blocking mode and allows only
+ *  one socket connection. returns accepted socket after accepting connection
+ *  from peer.
+ *
+ *  @param[in] socketInterface - unix socket path
+ *  @return   on success returns accepted socket fd
+ *            on failure returns -1
+ */
+int setupUnixSocket(const std::string& socketInterface);
+
+/** @brief Write data on UNIX socket
+ *  This function writes given data to a non-blocking socket.
+ *  Irrespective of block size, this function make sure of writing given data
+ *  on unix socket.
+ *
+ *  @param[in] sock - unix socket
+ *  @param[in] buf -  data buffer
+ *  @param[in] blockSize - size of data to write
+ *  @return   on success retruns  0
+ *            on failure returns -1
+
+ */
+int writeToUnixSocket(const int sock, const char* buf,
+                      const uint64_t blockSize);
+} // namespace utils
+} // namespace responder
+} // namespace pldm