Giacomo Travaglini has uploaded this change for review. ( https://gem5-review.googlesource.com/c/public/gem5/+/43164 )

Change subject: dev: Factor functionalities out of the Terminal device
......................................................................

dev: Factor functionalities out of the Terminal device

The Terminal device is implementing some useful functionalities
we could use for other purposes.
It is mainly a serial device with circular buffers and a listening
socket. With this patch I am moving the circular buffers and IPC logic
(socket listener) out of the class into a new CircularPipe object;
decoupling the simulated object (a serial device) to its communication
mechanism.

We could reuse this newly created class to establish buffered
communications from everywhere in gem5

Change-Id: I1ede897568a8292f485404fc014defe28ce12deb
Signed-off-by: Giacomo Travaglini <[email protected]>
---
M src/base/SConscript
A src/base/circular_pipe.cc
A src/base/circular_pipe.hh
M src/dev/serial/terminal.cc
M src/dev/serial/terminal.hh
5 files changed, 387 insertions(+), 248 deletions(-)



diff --git a/src/base/SConscript b/src/base/SConscript
index 1190b93..e2a5c3e 100644
--- a/src/base/SConscript
+++ b/src/base/SConscript
@@ -37,6 +37,7 @@
 Source('imgwriter.cc')
 Source('bmpwriter.cc')
 Source('channel_addr.cc')
+Source('circular_pipe.cc')
 Source('cprintf.cc', add_tags='gtest lib')
 GTest('cprintf.test', 'cprintf.test.cc')
 Executable('cprintftime', 'cprintftime.cc', 'cprintf.cc')
diff --git a/src/base/circular_pipe.cc b/src/base/circular_pipe.cc
new file mode 100644
index 0000000..ddceb12
--- /dev/null
+++ b/src/base/circular_pipe.cc
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2019, 2021 Arm Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2001-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "base/circular_pipe.hh"
+
+#include "base/atomicio.hh"
+#include "base/trace.hh"
+#include "sim/eventq.hh"
+
+/*
+ * Poll event for the listen socket
+ */
+CircularPipe::ListenEvent::ListenEvent(CircularPipe *l, int fd, int e)
+    : PollEvent(fd, e), pipe(l)
+{
+}
+
+void
+CircularPipe::ListenEvent::process(int revent)
+{
+    pipe->accept();
+}
+
+/*
+ * Poll event for the data socket
+ */
+CircularPipe::DataEvent::DataEvent(CircularPipe *l, int fd, int e)
+    : PollEvent(fd, e), pipe(l)
+{
+}
+
+void
+CircularPipe::DataEvent::process(int revent)
+{
+    // As a consequence of being called from the PollQueue, we might
+    // have been called from a different thread. Migrate to "our"
+    // thread.
+    EventQueue::ScopedMigration migrate(pipe->evQueue);
+
+    if (revent & POLLIN)
+        pipe->data();
+    else if (revent & POLLNVAL)
+        pipe->detach();
+}
+
+/*
+ * Terminal code
+ */
+CircularPipe::CircularPipe(int port, EventQueue *eq, const std::string &_name,
+                           ssize_t tx_size, ssize_t rx_size)
+    : listenEvent(NULL), dataEvent(NULL), evQueue(eq),
+      number(port), data_fd(-1), pipeName(_name),
+      txbuf(tx_size), rxbuf(rx_size)
+#if TRACING_ON == 1
+      , linebuf(16384)
+#endif
+{
+    if (port)
+        listen(port);
+}
+
+CircularPipe::~CircularPipe()
+{
+    if (data_fd != -1)
+        ::close(data_fd);
+
+    if (listenEvent)
+        delete listenEvent;
+
+    if (dataEvent)
+        delete dataEvent;
+}
+
+void
+CircularPipe::listen(int port)
+{
+    if (ListenSocket::allDisabled()) {
+ warn_once("Sockets disabled, not accepting %s connections", pipeName);
+        return;
+    }
+
+    while (!listener.listen(port, true)) {
+        port++;
+    }
+
+    ccprintf(std::cerr, "%s: Listening for connections on port %d\n",
+             pipeName, port);
+
+    listenEvent = new ListenEvent(this, listener.getfd(), POLLIN);
+    pollQueue.schedule(listenEvent);
+}
+
+void
+CircularPipe::accept()
+{
+    if (!listener.islistening())
+ panic("%s: cannot accept a connection if not listening!", pipeName);
+
+    int fd = listener.accept(true);
+    if (data_fd != -1) {
+        char message[] = "terminal already attached!\n";
+        atomic_write(fd, message, sizeof(message));
+        ::close(fd);
+        return;
+    }
+
+    data_fd = fd;
+    dataEvent = new DataEvent(this, data_fd, POLLIN);
+    pollQueue.schedule(dataEvent);
+
+    std::stringstream stream;
+    ccprintf(stream, "==== m5 terminal: %s %d ====", pipeName, number);
+
+    // we need an actual carriage return followed by a newline for the
+    // terminal
+    stream << "\r\n";
+
+    write((const uint8_t *)stream.str().c_str(), stream.str().size());
+
+    DPRINTFN("attach %s %d\n", pipeName, number);
+    char buf[1024];
+    for (size_t i = 0; i < txbuf.size(); i += sizeof(buf)) {
+        const size_t chunk_len(std::min(txbuf.size() - i, sizeof(buf)));
+        txbuf.peek(buf, i, chunk_len);
+        write((const uint8_t *)buf, chunk_len);
+    }
+}
+
+void
+CircularPipe::detach()
+{
+    if (data_fd != -1) {
+        ::close(data_fd);
+        data_fd = -1;
+    }
+
+    pollQueue.remove(dataEvent);
+    delete dataEvent;
+    dataEvent = NULL;
+
+    DPRINTFN("detach %s %d\n", pipeName, number);
+}
+
+void
+CircularPipe::data()
+{
+    uint8_t buf[1024];
+    int len;
+
+    len = read(buf, sizeof(buf));
+    if (len) {
+        rxbuf.write((char *)buf, len);
+        notifyRx();
+    }
+}
+
+size_t
+CircularPipe::read(uint8_t *buf, size_t len)
+{
+    if (data_fd < 0)
+        panic("%s not properly attached.\n", pipeName);
+
+    ssize_t ret;
+    do {
+      ret = ::read(data_fd, buf, len);
+    } while (ret == -1 && errno == EINTR);
+
+
+    if (ret < 0)
+        DPRINTFN("Read failed.\n");
+
+    if (ret <= 0) {
+        detach();
+        return 0;
+    }
+
+    return ret;
+}
+
+// Terminal output.
+size_t
+CircularPipe::write(const uint8_t *buf, size_t len)
+{
+    if (data_fd < 0)
+        panic("%s not properly attached.\n", pipeName);
+
+    ssize_t ret = atomic_write(data_fd, buf, len);
+    if (ret < len)
+        detach();
+
+    return ret;
+}
+
+void
+CircularPipe::sendChar(uint8_t c)
+{
+    txbuf.write(&c, 1);
+
+    if (data_fd >= 0)
+        write(c);
+}
+
+std::string
+CircularPipe::txDump()
+{
+    const ssize_t size = txbuf.size();
+    std::unique_ptr<char[]> buf(new char[size]);
+
+    txbuf.peek(buf.get(), 0, size);
+
+    return std::string(buf.get(), size);
+}
diff --git a/src/base/circular_pipe.hh b/src/base/circular_pipe.hh
new file mode 100644
index 0000000..f02f261
--- /dev/null
+++ b/src/base/circular_pipe.hh
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2019, 2021 Arm Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2001-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEV_CIRCULAR_PIPE_HH__
+#define __DEV_CIRCULAR_PIPE_HH__
+
+#include <iostream>
+
+#include "base/callback.hh"
+#include "base/circlebuf.hh"
+#include "base/pollevent.hh"
+#include "base/socket.hh"
+
+class EventQueue;
+
+class CircularPipe
+{
+  protected:
+    class ListenEvent : public PollEvent
+    {
+      protected:
+        CircularPipe *pipe;
+
+      public:
+        ListenEvent(CircularPipe *t, int fd, int e);
+        void process(int revent);
+    };
+
+    friend class ListenEvent;
+    ListenEvent *listenEvent;
+
+    class DataEvent : public PollEvent
+    {
+      protected:
+        CircularPipe *pipe;
+
+      public:
+        DataEvent(CircularPipe *t, int fd, int e);
+        void process(int revent);
+    };
+
+    friend class DataEvent;
+    DataEvent *dataEvent;
+    EventQueue *evQueue;
+
+  protected:
+    int number;
+    int data_fd;
+    const std::string pipeName;
+
+  public:
+    CircularPipe(int port, EventQueue *eq, const std::string &name,
+                 ssize_t tx_size=16384, ssize_t rx_size=16384);
+    ~CircularPipe();
+
+  protected:
+    ListenSocket listener;
+
+    void listen(int port);
+    void accept();
+
+  protected:
+    virtual void notifyRx() {}
+
+    CircleBuf<char> txbuf;
+    CircleBuf<char> rxbuf;
+#if TRACING_ON == 1
+    CircleBuf<char> linebuf;
+#endif
+
+  public:
+    void data();
+    void read(uint8_t &c) { read(&c, 1); }
+    size_t read(uint8_t *buf, size_t len);
+    void write(uint8_t c) { write(&c, 1); }
+    size_t write(const uint8_t *buf, size_t len);
+    void detach();
+
+    void sendChar(uint8_t c);
+    std::string txDump();
+};
+
+#endif // __DEV_CIRCULAR_PIPE_HH__
diff --git a/src/dev/serial/terminal.cc b/src/dev/serial/terminal.cc
index d85cf1f..570a173 100644
--- a/src/dev/serial/terminal.cc
+++ b/src/dev/serial/terminal.cc
@@ -63,7 +63,6 @@
 #include <sstream>
 #include <string>

-#include "base/atomicio.hh"
 #include "base/logging.hh"
 #include "base/output.hh"
 #include "base/socket.hh"
@@ -74,69 +73,14 @@
 #include "dev/serial/uart.hh"

 /*
- * Poll event for the listen socket
- */
-Terminal::ListenEvent::ListenEvent(Terminal *t, int fd, int e)
-    : PollEvent(fd, e), term(t)
-{
-}
-
-void
-Terminal::ListenEvent::process(int revent)
-{
-    term->accept();
-}
-
-/*
- * Poll event for the data socket
- */
-Terminal::DataEvent::DataEvent(Terminal *t, int fd, int e)
-    : PollEvent(fd, e), term(t)
-{
-}
-
-void
-Terminal::DataEvent::process(int revent)
-{
-    // As a consequence of being called from the PollQueue, we might
-    // have been called from a different thread. Migrate to "our"
-    // thread.
-    EventQueue::ScopedMigration migrate(term->eventQueue());
-
-    if (revent & POLLIN)
-        term->data();
-    else if (revent & POLLNVAL)
-        term->detach();
-}
-
-/*
  * Terminal code
  */
 Terminal::Terminal(const Params &p)
-    : SerialDevice(p), listenEvent(NULL), dataEvent(NULL),
-      number(p.number), data_fd(-1), txbuf(16384), rxbuf(16384),
+    : SerialDevice(p), CircularPipe(p.port, eventQueue(), name()),
       outfile(terminalDump(p))
-#if TRACING_ON == 1
-      , linebuf(16384)
-#endif
 {
     if (outfile)
         outfile->stream()->setf(std::ios::unitbuf);
-
-    if (p.port)
-        listen(p.port);
-}
-
-Terminal::~Terminal()
-{
-    if (data_fd != -1)
-        ::close(data_fd);
-
-    if (listenEvent)
-        delete listenEvent;
-
-    if (dataEvent)
-        delete dataEvent;
 }

 OutputStream *
@@ -156,133 +100,6 @@
     }
 }

-///////////////////////////////////////////////////////////////////////
-// socket creation and terminal attach
-//
-
-void
-Terminal::listen(int port)
-{
-    if (ListenSocket::allDisabled()) {
-        warn_once("Sockets disabled, not accepting terminal connections");
-        return;
-    }
-
-    while (!listener.listen(port, true)) {
-        DPRINTF(Terminal,
-                ": can't bind address terminal port %d inuse PID %d\n",
-                port, getpid());
-        port++;
-    }
-
-    ccprintf(std::cerr, "%s: Listening for connections on port %d\n",
-             name(), port);
-
-    listenEvent = new ListenEvent(this, listener.getfd(), POLLIN);
-    pollQueue.schedule(listenEvent);
-}
-
-void
-Terminal::accept()
-{
-    if (!listener.islistening())
-        panic("%s: cannot accept a connection if not listening!", name());
-
-    int fd = listener.accept(true);
-    if (data_fd != -1) {
-        char message[] = "terminal already attached!\n";
-        atomic_write(fd, message, sizeof(message));
-        ::close(fd);
-        return;
-    }
-
-    data_fd = fd;
-    dataEvent = new DataEvent(this, data_fd, POLLIN);
-    pollQueue.schedule(dataEvent);
-
-    std::stringstream stream;
-    ccprintf(stream, "==== m5 terminal: Terminal %d ====", number);
-
-    // we need an actual carriage return followed by a newline for the
-    // terminal
-    stream << "\r\n";
-
-    write((const uint8_t *)stream.str().c_str(), stream.str().size());
-
-    DPRINTFN("attach terminal %d\n", number);
-    char buf[1024];
-    for (size_t i = 0; i < txbuf.size(); i += sizeof(buf)) {
-        const size_t chunk_len(std::min(txbuf.size() - i, sizeof(buf)));
-        txbuf.peek(buf, i, chunk_len);
-        write((const uint8_t *)buf, chunk_len);
-    }
-}
-
-void
-Terminal::detach()
-{
-    if (data_fd != -1) {
-        ::close(data_fd);
-        data_fd = -1;
-    }
-
-    pollQueue.remove(dataEvent);
-    delete dataEvent;
-    dataEvent = NULL;
-
-    DPRINTFN("detach terminal %d\n", number);
-}
-
-void
-Terminal::data()
-{
-    uint8_t buf[1024];
-    int len;
-
-    len = read(buf, sizeof(buf));
-    if (len) {
-        rxbuf.write((char *)buf, len);
-        notifyInterface();
-    }
-}
-
-size_t
-Terminal::read(uint8_t *buf, size_t len)
-{
-    if (data_fd < 0)
-        panic("Terminal not properly attached.\n");
-
-    ssize_t ret;
-    do {
-      ret = ::read(data_fd, buf, len);
-    } while (ret == -1 && errno == EINTR);
-
-
-    if (ret < 0)
-        DPRINTFN("Read failed.\n");
-
-    if (ret <= 0) {
-        detach();
-        return 0;
-    }
-
-    return ret;
-}
-
-// Terminal output.
-size_t
-Terminal::write(const uint8_t *buf, size_t len)
-{
-    if (data_fd < 0)
-        panic("Terminal not properly attached.\n");
-
-    ssize_t ret = atomic_write(data_fd, buf, len);
-    if (ret < len)
-        detach();
-
-    return ret;
-}
-
 uint8_t
 Terminal::readData()
 {
@@ -321,10 +138,7 @@
     }
 #endif

-    txbuf.write(&c, 1);
-
-    if (data_fd >= 0)
-        write(c);
+    sendChar(c);

     if (outfile)
         outfile->stream()->put((char)c);
@@ -333,3 +147,9 @@
             isprint(c) ? c : ' ', (int)c);

 }
+
+void
+Terminal::notifyRx()
+{
+    notifyInterface();
+}
diff --git a/src/dev/serial/terminal.hh b/src/dev/serial/terminal.hh
index 31c8ed9..9b9b8d8 100644
--- a/src/dev/serial/terminal.hh
+++ b/src/dev/serial/terminal.hh
@@ -49,6 +49,7 @@

 #include "base/callback.hh"
 #include "base/circlebuf.hh"
+#include "base/circular_pipe.hh"
 #include "base/pollevent.hh"
 #include "base/socket.hh"
 #include "dev/serial/serial.hh"
@@ -58,75 +59,23 @@
 class OutputStream;
 class TerminalListener;

-class Terminal : public SerialDevice
+class Terminal : public SerialDevice, public CircularPipe
 {
-  protected:
-    class ListenEvent : public PollEvent
-    {
-      protected:
-        Terminal *term;
-
-      public:
-        ListenEvent(Terminal *t, int fd, int e);
-        void process(int revent);
-    };
-
-    friend class ListenEvent;
-    ListenEvent *listenEvent;
-
-    class DataEvent : public PollEvent
-    {
-      protected:
-        Terminal *term;
-
-      public:
-        DataEvent(Terminal *t, int fd, int e);
-        void process(int revent);
-    };
-
-    friend class DataEvent;
-    DataEvent *dataEvent;
-
-  protected:
-    int number;
-    int data_fd;
-
   public:
     typedef TerminalParams Params;
     Terminal(const Params &p);
-    ~Terminal();
     OutputStream * terminalDump(const TerminalParams &p);

-  protected:
-    ListenSocket listener;
-
-    void listen(int port);
-    void accept();
-
-  protected:
-    CircleBuf<char> txbuf;
-    CircleBuf<char> rxbuf;
-    OutputStream *outfile;
-#if TRACING_ON == 1
-    CircleBuf<char> linebuf;
-#endif
-
-  public:
-    ///////////////////////
-    // Terminal Interface
-
-    void data();
-
-    void read(uint8_t &c) { read(&c, 1); }
-    size_t read(uint8_t *buf, size_t len);
-    void write(uint8_t c) { write(&c, 1); }
-    size_t write(const uint8_t *buf, size_t len);
-    void detach();
-
-  public: // SerialDevice interface
+    // Serial Device interface
     uint8_t readData() override;
     void writeData(uint8_t c) override;
     bool dataAvailable() const override { return !rxbuf.empty(); }
+
+    // CircularPipe interface
+    void notifyRx() override;
+
+  protected:
+    OutputStream *outfile;
 };

 #endif // __DEV_TERMINAL_HH__

--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/43164
To unsubscribe, or for help writing mail filters, visit https://gem5-review.googlesource.com/settings

Gerrit-Project: public/gem5
Gerrit-Branch: develop
Gerrit-Change-Id: I1ede897568a8292f485404fc014defe28ce12deb
Gerrit-Change-Number: 43164
Gerrit-PatchSet: 1
Gerrit-Owner: Giacomo Travaglini <[email protected]>
Gerrit-MessageType: newchange
_______________________________________________
gem5-dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s

Reply via email to