- Adapt MDS with this SNA implementation. --- src/base/Makefile.am | 6 +- src/base/sna.h | 123 +++++++++++++++++++++++++++++++ src/base/tests/sna_test.cc | 117 +++++++++++++++++++++++++++++ src/mds/mds_tipc_fctrl_portid.cc | 39 +++++----- src/mds/mds_tipc_fctrl_portid.h | 76 ++----------------- 5 files changed, 270 insertions(+), 91 deletions(-) create mode 100644 src/base/sna.h create mode 100644 src/base/tests/sna_test.cc
diff --git a/src/base/Makefile.am b/src/base/Makefile.am index 025fb86a2..5082175cf 100644 --- a/src/base/Makefile.am +++ b/src/base/Makefile.am @@ -173,7 +173,8 @@ noinst_HEADERS += \ src/base/unix_client_socket.h \ src/base/unix_server_socket.h \ src/base/unix_socket.h \ - src/base/usrbuf.h + src/base/usrbuf.h \ + src/base/sna.h TESTS += bin/testleap bin/libbase_test bin/core_common_test @@ -237,7 +238,8 @@ bin_libbase_test_SOURCES = \ src/base/tests/time_compare_test.cc \ src/base/tests/time_convert_test.cc \ src/base/tests/time_subtract_test.cc \ - src/base/tests/unix_socket_test.cc + src/base/tests/unix_socket_test.cc \ + src/base/tests/sna_test.cc bin_libbase_test_LDADD = \ $(GTEST_DIR)/lib/libgtest.la \ diff --git a/src/base/sna.h b/src/base/sna.h new file mode 100644 index 000000000..bd138293f --- /dev/null +++ b/src/base/sna.h @@ -0,0 +1,123 @@ +/* -*- OpenSAF -*- + * + * Copyright Ericsson AB 2019 - All Rights Reserved. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Reference: Serial Number Arithmetic from RFC1982 + * + */ + +#ifndef BASE_SNA_H_ +#define BASE_SNA_H_ + +#include <typeinfo> +#include <stdexcept> + +#define MAX_16BITS 65536 // 2^16 +#define MAX_32BITS 4294967296 // 2^32 + +template <class T> +class _sna { + private: + T i; + uint64_t max() { + if (typeid(T) == typeid(uint64_t)) { + return MAX_32BITS; + } else if (typeid(T) == typeid(uint32_t)) { + return MAX_16BITS; + } else { + printf("Type:%s\n", typeid(T).name()); + throw std::out_of_range("Invalid type"); + } + } + + public: + _sna(): i(0) {} + _sna(const _sna &t) { + i = t.i; + } + explicit _sna(const uint64_t &n) { + if ((n < 0) || (n > (max()-1))) + throw std::out_of_range("SNA assign with invalid value"); + i = n; + } + _sna& operator=(const _sna &t) { + // check for self-assignment + if (&t == this) + return *this; + i = t.i; + return *this; + } + T v() const { + return i; + } + _sna& operator+=(const uint64_t& n) { + if ((n < 0) || (n > (max()/2 - 1))) + throw std::out_of_range("SNA received invalid addition value"); + i = (i + n) % max(); + return *this; + } + friend _sna operator+(_sna m, const uint64_t& n) { + m += n; + return m; + } + // prefix ++ + _sna& operator++() { + *this += 1; + return *this; + } + // postfix ++ + _sna operator++(int) { + _sna tmp(*this); + operator++(); + return tmp; + } + bool operator==(const _sna& rhs) { + return i == rhs.i; + } + bool operator!=(const _sna& rhs) { + return i != rhs.i; + } + bool operator<(const _sna& rhs) { + return (i < rhs.i && rhs.i - i < max()/2) || \ + (i > rhs.i && i - rhs.i > max()/2); + } + bool operator>=(const _sna& rhs) { + return !(*this < rhs); + } + bool operator>(const _sna& rhs) { + return (i < rhs.i && rhs.i - i > max()/2) || \ + (i > rhs.i && i - rhs.i < max()/2); + } + bool operator<=(const _sna& rhs) { + return !(*this > rhs); + } + int32_t operator-(const _sna& rhs) { + if (*this >= rhs) { + if (i >= rhs.v()) { + return i - rhs.v(); + } else { + return (i + max()) - rhs.v(); + } + } else { + if (i < rhs.v()) { + return i - rhs.v(); + } else { + return i - (rhs.v() + max()); + } + } + } +}; + +typedef _sna<uint32_t> sna16_t; +typedef _sna<uint64_t> sna32_t; + +#endif // BASE_SNA_H_ diff --git a/src/base/tests/sna_test.cc b/src/base/tests/sna_test.cc new file mode 100644 index 000000000..4b7eb83e3 --- /dev/null +++ b/src/base/tests/sna_test.cc @@ -0,0 +1,117 @@ +/* -*- OpenSAF -*- + * + * Copyright Ericsson AB 2019 - All Rights Reserved. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Reference: Serial Number Arithmetic from RFC1982 + * + */ + +#include "base/sna.h" +#include "gtest/gtest.h" + +template <class T> +int test_sna(T x) { + int rc = 1; + printf("\n============= START with x=%lu =============\n", (uint64_t)x.v()); + T y = x; + printf("x=%lu, y=%lu: check x == y++ is TRUE\n", + (uint64_t)x.v(), (uint64_t)y.v()); + if (x == y++) { + printf("now y=%lu, reset y = x\n", (uint64_t)y.v()); + y = x; + printf("x=%lu, y=%lu: check x != ++y is TRUE\n", + (uint64_t)x.v(), (uint64_t)y.v()); + if (x != ++y) { + printf("now y=%lu, reset y = x\n", (uint64_t)y.v()); + y = x; + printf("x=%lu, y=%lu: check x < ++y is TRUE\n", + (uint64_t)x.v(), (uint64_t)y.v()); + if (x < ++y) { + printf("x=%lu: check x + 1 > x and x + 1 >= x is TRUE\n", + (uint64_t)x.v()); + if ((x + 1 > x) && (x + 1 >= x)) { + printf("x=%lu: check x < x + 1 and x <= x + 1 is TRUE\n", + (uint64_t)x.v()); + y = x + 1; + printf("y = x+1 => y=%lu\n", (uint64_t)y.v()); + y = y + 1; + printf("y = y+1 => y=%lu\n", (uint64_t)y.v()); + if ((x < x + 1) && (x <= x + 1)) { + try { + printf("x=%lu: add invalid (-1)\n", (uint64_t)x.v()); + x = x + (-1); + } catch (const std::out_of_range& oor) { + printf("Expected error: %s\n", oor.what()); + try { + uint64_t max_value = 0; + if (typeid(T) == typeid(sna16_t)) + max_value = MAX_16BITS; + else if (typeid(T) == typeid(sna32_t)) + max_value = MAX_32BITS; + printf("x=%lu: add invalid (%lu)\n", + (uint64_t)x.v(), max_value); + x = x + max_value; + } catch (const std::out_of_range& oor) { + printf("Expected error: %s\n", oor.what()); + rc = 0; + } + } + } + } + } + } + } + printf("================ END with x=%lu ==============\n", (uint64_t)x.v()); + return rc; +} + + +class SnaTest : public ::testing::Test { + protected: + SnaTest() {} + virtual ~SnaTest() { + // Cleanup work that doesn't throw exceptions here. + } + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test) + } + virtual void TearDown() {} +}; + +TEST_F(SnaTest, unit16_sna) { + sna16_t x; + EXPECT_EQ(0, test_sna(x)); + sna16_t x1 = sna16_t(1); + sna16_t x2 = sna16_t(MAX_16BITS - 1); + EXPECT_EQ(2, x1 - x2); + EXPECT_EQ(-2, x2 - x1); + EXPECT_EQ(0, test_sna(x1)); + EXPECT_EQ(0, test_sna(x2)); +} + +TEST_F(SnaTest, unit32_sna) { + sna32_t x; + EXPECT_EQ(0, test_sna(x)); + sna32_t x1 = sna32_t(1); + sna32_t x2 = sna32_t(MAX_32BITS - 1); + EXPECT_EQ(2, x1 - x2); + EXPECT_EQ(-2, x2 - x1); + EXPECT_EQ(0, test_sna(x1)); + EXPECT_EQ(0, test_sna(x2)); +} + + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/mds/mds_tipc_fctrl_portid.cc b/src/mds/mds_tipc_fctrl_portid.cc index 1ce792dd8..f5dbf803e 100644 --- a/src/mds/mds_tipc_fctrl_portid.cc +++ b/src/mds/mds_tipc_fctrl_portid.cc @@ -67,12 +67,12 @@ DataMessage* MessageQueue::Find(uint32_t mseq, uint16_t mfrag) { return nullptr; } -uint64_t MessageQueue::Erase(Seq16 fseq_from, Seq16 fseq_to) { +uint64_t MessageQueue::Erase(uint16_t chksize, sna16_t fseq_to) { uint64_t msg_len = 0; for (auto it = queue_.begin(); it != queue_.end();) { DataMessage *m = *it; - if (fseq_from <= Seq16(m->header_.fseq_) && - Seq16(m->header_.fseq_) <= fseq_to) { + if (sna16_t(m->header_.fseq_) + chksize > fseq_to && + sna16_t(m->header_.fseq_) <= fseq_to) { msg_len += m->header_.msg_len_; it = queue_.erase(it); delete m; @@ -93,10 +93,10 @@ DataMessage* MessageQueue::FirstUnsent() { return nullptr; } -void MessageQueue::MarkUnsentFrom(Seq16 fseq) { +void MessageQueue::MarkUnsentFrom(sna16_t fseq) { for (const auto& it : queue_) { DataMessage *m = it; - if (Seq16(m->header_.fseq_) >= fseq) m->is_sent_ = false; + if (sna16_t(m->header_.fseq_) >= fseq) m->is_sent_ = false; } } @@ -259,7 +259,7 @@ uint32_t TipcPortId::ReceiveData(uint32_t mseq, uint16_t mfrag, txprob_cnt_, (uint8_t)state_); } // update receiver sequence window - if (rcvwnd_.acked_ < Seq16(fseq) && rcvwnd_.rcv_ + Seq16(1) == Seq16(fseq)) { + if (rcvwnd_.acked_ < sna16_t(fseq) && rcvwnd_.rcv_ + 1 == sna16_t(fseq)) { m_MDS_LOG_DBG("FCTRL: [me] <-- [node:%x, ref:%u], " "RcvData[mseq:%u, mfrag:%u, fseq:%u], " "rcvwnd[acked:%u, rcv:%u, nacked:%" PRIu64 "]", @@ -268,12 +268,12 @@ uint32_t TipcPortId::ReceiveData(uint32_t mseq, uint16_t mfrag, rcvwnd_.acked_.v(), rcvwnd_.rcv_.v(), rcvwnd_.nacked_space_); ++rcvwnd_.rcv_; - if (rcvwnd_.rcv_ - rcvwnd_.acked_ >= chunk_size_) { + if (rcvwnd_.rcv_ >= rcvwnd_.acked_ + chunk_size_) { // send ack for @chunk_size_ msgs starting from fseq SendChunkAck(fseq, svc_id, chunk_size_); rcvwnd_.acked_ = rcvwnd_.rcv_; rc = NCSCC_RC_CONTINUE; - } else if (fseq == 1 && rcvwnd_.acked_ == 0) { + } else if (fseq == 1 && rcvwnd_.acked_ == sna16_t(0)) { // send ack right away for the very first data message // to stop txprob timer at sender SendChunkAck(fseq, svc_id, 1); @@ -290,8 +290,8 @@ uint32_t TipcPortId::ReceiveData(uint32_t mseq, uint16_t mfrag, // It is not used for now, so ignore it. // check for transmission error - if (rcvwnd_.rcv_ + Seq16(1) < Seq16(fseq)) { - if (rcvwnd_.rcv_ == 0 && rcvwnd_.acked_ == 0) { + if (rcvwnd_.rcv_ + 1 < sna16_t(fseq)) { + if (rcvwnd_.rcv_ == sna16_t(0) && rcvwnd_.acked_ == sna16_t(0)) { // peer does not realize that this portid reset m_MDS_LOG_ERR("FCTRL: [me] <-- [node:%x, ref:%u], " "RcvData[mseq:%u, mfrag:%u, fseq:%u], " @@ -301,7 +301,7 @@ uint32_t TipcPortId::ReceiveData(uint32_t mseq, uint16_t mfrag, mseq, mfrag, fseq, rcvwnd_.acked_.v(), rcvwnd_.rcv_.v(), rcvwnd_.nacked_space_); - rcvwnd_.rcv_ = fseq; + rcvwnd_.rcv_ = sna16_t(fseq); } else { rc = NCSCC_RC_FAILURE; // msg loss @@ -314,7 +314,7 @@ uint32_t TipcPortId::ReceiveData(uint32_t mseq, uint16_t mfrag, rcvwnd_.acked_.v(), rcvwnd_.rcv_.v(), rcvwnd_.nacked_space_); } } - if (Seq16(fseq) <= rcvwnd_.acked_) { + if (sna16_t(fseq) <= rcvwnd_.acked_) { rc = NCSCC_RC_FAILURE; // unexpected retransmission m_MDS_LOG_ERR("FCTRL: [me] <-- [node:%x, ref:%u], " @@ -349,7 +349,7 @@ void TipcPortId::ReceiveChunkAck(uint16_t fseq, uint16_t chksize) { txprob_cnt_, (uint8_t)state_); } // update sender sequence window - if (sndwnd_.acked_ < Seq16(fseq) && Seq16(fseq) < sndwnd_.send_) { + if (sndwnd_.acked_ < sna16_t(fseq) && sna16_t(fseq) < sndwnd_.send_) { m_MDS_LOG_DBG("FCTRL: [me] <-- [node:%x, ref:%u], " "RcvChkAck[fseq:%u, chunk:%u], " "sndwnd[acked:%u, send:%u, nacked:%" PRIu64 "], " @@ -360,12 +360,11 @@ void TipcPortId::ReceiveChunkAck(uint16_t fseq, uint16_t chksize) { sndqueue_.Size()); // fast forward the sndwnd_.acked_ sequence to fseq - sndwnd_.acked_ = fseq; + sndwnd_.acked_ = sna16_t(fseq); // remove a number @chksize messages out of sndqueue_ and decrease // the nacked_space_ of sender - uint64_t acked_bytes = sndqueue_.Erase(Seq16(fseq) - (chksize-1), - Seq16(fseq)); + uint64_t acked_bytes = sndqueue_.Erase(chksize, sna16_t(fseq)); sndwnd_.nacked_space_ -= acked_bytes; // try to send a few pending msg @@ -429,14 +428,14 @@ void TipcPortId::ReceiveNack(uint32_t mseq, uint16_t mfrag, "RcvNack, ignore[fseq:%u, state:%u]", id_.node, id_.ref, fseq, (uint8_t)state_); - sndqueue_.MarkUnsentFrom(Seq16(fseq)); + sndqueue_.MarkUnsentFrom(sna16_t(fseq)); return; } if (state_ != State::kRcvBuffOverflow) { state_ = State::kRcvBuffOverflow; m_MDS_LOG_NOTIFY("FCTRL: [node:%x, ref:%u] --> Overflow ", id_.node, id_.ref); - sndqueue_.MarkUnsentFrom(Seq16(fseq)); + sndqueue_.MarkUnsentFrom(sna16_t(fseq)); } DataMessage* msg = sndqueue_.Find(mseq, mfrag); if (msg != nullptr) { @@ -462,8 +461,8 @@ void TipcPortId::ReceiveNack(uint32_t mseq, uint16_t mfrag, bool TipcPortId::ReceiveTmrTxProb(uint8_t max_txprob) { bool restart_txprob = false; if (state_ == State::kDisabled || - sndwnd_.acked_ > Seq16(1) || - rcvwnd_.rcv_ > Seq16(1)) return restart_txprob; + sndwnd_.acked_ > sna16_t(1) || + rcvwnd_.rcv_ > sna16_t(1)) return restart_txprob; if (state_ == State::kTxProb || state_ == State::kRcvBuffOverflow) { txprob_cnt_++; if (txprob_cnt_ >= max_txprob) { diff --git a/src/mds/mds_tipc_fctrl_portid.h b/src/mds/mds_tipc_fctrl_portid.h index cf2daaa31..8b22f1dee 100644 --- a/src/mds/mds_tipc_fctrl_portid.h +++ b/src/mds/mds_tipc_fctrl_portid.h @@ -24,82 +24,20 @@ #include <stdio.h> #include <unistd.h> #include <deque> +#include "base/sna.h" #include "mds/mds_tipc_fctrl_msg.h" namespace mds { -class Seq16 { - public: -#define SEQ16_MAX 65536 -#define SEQ16_SPACE 32768 - uint16_t value_; - explicit Seq16(uint16_t v) { - value_ = uint16_t((uint32_t)v % SEQ16_MAX); - } - uint16_t v() { - return value_; - } - Seq16 operator + (const Seq16 add) const { - return Seq16(((uint32_t)value_ + (uint32_t)add.value_) % SEQ16_MAX); - } - - int16_t operator - (const Seq16 sub) const { - if (value_ < sub.value_ && (sub.value_ - value_ < SEQ16_SPACE)) { - return value_ - sub.value_; - } - if (value_ > sub.value_ && (value_ - sub.value_ > SEQ16_SPACE)) { - return (int32_t)value_ + SEQ16_MAX - (int32_t)sub.value_; - } - if (value_ < sub.value_ && (sub.value_ - value_ > SEQ16_SPACE)) { - return (int32_t)value_ + SEQ16_MAX - (int32_t)sub.value_; - } - if (value_ > sub.value_ && (value_ - sub.value_ < SEQ16_SPACE)) { - return value_ - sub.value_; - } - return 0; - } - Seq16 operator - (const uint16_t sub) const { - return Seq16(((uint32_t)value_ + 65536 - sub) % SEQ16_MAX); - } - void operator ++() { - value_ = (value_ + 1) % SEQ16_MAX; - } - void operator = (const uint16_t v) { - value_ = v % SEQ16_MAX; - } - bool operator == (const Seq16& seq) const { - return value_ == seq.value_; - } - bool operator == (uint16_t val) const { - return value_ == val; - } - bool operator <= (const Seq16& seq) { - return *this == seq || *this < seq; - } - bool operator < (const Seq16& seq) { - if (value_ < seq.value_ && (seq.value_ - value_ < SEQ16_SPACE)) return true; - if (value_ > seq.value_ && (value_ - seq.value_ > SEQ16_SPACE)) return true; - return false; - } - bool operator > (const Seq16& seq) { - if (value_ < seq.value_ && (seq.value_ - value_ > SEQ16_SPACE)) return true; - if (value_ > seq.value_ && (value_ - seq.value_ < SEQ16_SPACE)) return true; - return false; - } - bool operator >= (const Seq16& seq) { - return *this == seq || *this > seq; - } -}; - class MessageQueue { public: void Queue(DataMessage* msg); DataMessage* Find(uint32_t mseq, uint16_t mfrag); - uint64_t Erase(Seq16 fseq_from, Seq16 fseq_to); + uint64_t Erase(uint16_t chksize, sna16_t fseq_to); uint64_t Size() const { return queue_.size(); } void Clear(); DataMessage* FirstUnsent(); - void MarkUnsentFrom(Seq16 fseq); + void MarkUnsentFrom(sna16_t fseq); private: std::deque<DataMessage*> queue_; }; @@ -155,16 +93,16 @@ class TipcPortId { struct sndwnd { // sender sequence window - Seq16 acked_{0}; // last sequence has been acked by receiver - Seq16 send_{1}; // next sequence to be sent + sna16_t acked_{0}; // last sequence has been acked by receiver + sna16_t send_{1}; // next sequence to be sent uint64_t nacked_space_{0}; // total bytes are sent but not acked }; struct sndwnd sndwnd_; struct rcvwnd { // receiver sequence window - Seq16 acked_{0}; // last sequence has been acked to sender - Seq16 rcv_{0}; // last sequence has been received + sna16_t acked_{0}; // last sequence has been acked to sender + sna16_t rcv_{0}; // last sequence has been received uint64_t nacked_space_{0}; // total bytes has not been acked }; struct rcvwnd rcvwnd_; -- 2.17.1 _______________________________________________ Opensaf-devel mailing list Opensaf-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/opensaf-devel