Skip to content

Commit

Permalink
[Link Event Damping] Port state change handler class.
Browse files Browse the repository at this point in the history
- Class to handle the port state change event callback from SAI and
  sending the events to syncd main thread for processing by link event
  damper.

Depends on: sonic-net#1297

HLD: sonic-net/SONiC#1071
  • Loading branch information
Ashish Singh committed Oct 23, 2023
1 parent 5a9fe8c commit 26ffcca
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 0 deletions.
1 change: 1 addition & 0 deletions syncd/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ libSyncd_a_SOURCES = \
NotificationQueue.cpp \
PortMap.cpp \
PortMapParser.cpp \
PortStateChangeHandler.cpp \
RedisClient.cpp \
RedisNotificationProducer.cpp \
RequestShutdownCommandLineOptions.cpp \
Expand Down
60 changes: 60 additions & 0 deletions syncd/PortStateChangeHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "PortStateChangeHandler.h"

#include <string>

using namespace syncd;

PortStateChangeHandler::PortStateChangeHandler(
_In_ std::shared_ptr<swss::SelectableEvent> portStateChangeEvent)
: m_portStateChangeEvent(portStateChangeEvent)
{
SWSS_LOG_ENTER();

m_portStateChangeQueue = std::make_shared<portOperStatusNotificationQueue>(
m_portStateChangeQueueSize);
}

PortStateChangeHandler::~PortStateChangeHandler()
{
SWSS_LOG_ENTER();
}

void PortStateChangeHandler::handlePortStateChangeNotification(
_In_ uint32_t count,
_In_ const sai_port_oper_status_notification_t *data)
{
SWSS_LOG_ENTER();

if (m_portStateChangeEvent == nullptr)
{
SWSS_LOG_THROW("Unexpected error: port state change event is null.");
}

for (uint32_t idx = 0; idx < count; ++idx)
{
if (m_portStateChangeQueue->enqueue(data[idx]) == false)
{
SWSS_LOG_ERROR(
"Unexpected error: failed to enqueue the port state change "
"notification.");

return;
}
}

m_portStateChangeEvent->notify();
}
73 changes: 73 additions & 0 deletions syncd/PortStateChangeHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

extern "C" {
#include "saimetadata.h"
}

#include <memory>

#include "ConcurrentQueue.h"
#include "swss/logger.h"
#include "swss/selectableevent.h"

namespace syncd
{

// Class to handle the port state change callback from SAI. This consists a
// selectable event that will be used to send notification from producer thread
// to consumer thread, and a mutex protected concurrent queue to share the port
// state change notification data between producer and consumer threads.
class PortStateChangeHandler
{
public:

using portOperStatusNotificationQueue =
ConcurrentQueue<sai_port_oper_status_notification_t>;

PortStateChangeHandler(
_In_ std::shared_ptr<swss::SelectableEvent> portStateChangeEvent);

virtual ~PortStateChangeHandler();

// Adds the port operational status notification data to a queue and generates a
// notification event.
void handlePortStateChangeNotification(
_In_ uint32_t count,
_In_ const sai_port_oper_status_notification_t *data);

// Returns the shared pointer of the queue.
std::shared_ptr<portOperStatusNotificationQueue> getQueue() const
{
SWSS_LOG_ENTER();

return m_portStateChangeQueue;
}

private:

// Choosing 4k max event queue size based on if we had 256 ports, it can
// accommodate on average 16 port events per ports in worst case.
static constexpr size_t m_portStateChangeQueueSize = 4096;

// SelectableEvent for producer to generate the event and for consumer to
// listen on.
std::shared_ptr<swss::SelectableEvent> m_portStateChangeEvent;

// Mutex protected queue to share the data between producer and consumer.
std::shared_ptr<portOperStatusNotificationQueue> m_portStateChangeQueue;
};
} // namespace syncd
1 change: 1 addition & 0 deletions unittest/syncd/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ tests_SOURCES = main.cpp \
TestNotificationProcessor.cpp \
TestNotificationHandler.cpp \
TestMdioIpcServer.cpp \
TestPortStateChangeHandler.cpp \
TestVendorSai.cpp

tests_CXXFLAGS = $(DBGFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS_COMMON)
Expand Down
74 changes: 74 additions & 0 deletions unittest/syncd/TestPortStateChangeHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "PortStateChangeHandler.h"

#include <gtest/gtest.h>

using namespace syncd;

constexpr size_t portStateChangeQueueSize = 4096;

class PortStateChangeHandlerTest : public ::testing::Test
{
protected:
PortStateChangeHandlerTest()
: m_portStateChangeHandler(std::make_shared<swss::SelectableEvent>())
{
SWSS_LOG_ENTER();
}

~PortStateChangeHandlerTest() override
{
SWSS_LOG_ENTER();
}

PortStateChangeHandler m_portStateChangeHandler;
};

TEST_F(PortStateChangeHandlerTest, VerifyGetQueue)
{
auto queue = m_portStateChangeHandler.getQueue();
EXPECT_EQ(queue->size(), 0);
}

TEST_F(PortStateChangeHandlerTest,
HandlePortStateChangeNotificationFailsOnEnqueuingData)
{
auto queue = m_portStateChangeHandler.getQueue();
EXPECT_EQ(queue->size(), 0);

// Insert enough data in the queue so it reaches its capacity.
sai_port_oper_status_notification_t operStatus[portStateChangeQueueSize];
m_portStateChangeHandler.handlePortStateChangeNotification(
portStateChangeQueueSize, &operStatus[0]);
EXPECT_EQ(queue->size(), portStateChangeQueueSize);

// Since queue is at its maximum capacity, adding a new element should cause
// insert failure and new element should not get added.
m_portStateChangeHandler.handlePortStateChangeNotification(/*count=*/1,
&operStatus[0]);
EXPECT_EQ(queue->size(), portStateChangeQueueSize);
}

TEST_F(PortStateChangeHandlerTest, HandlePortStateChangeNotificationSucceeds)
{
auto queue = m_portStateChangeHandler.getQueue();
EXPECT_EQ(queue->size(), 0);

sai_port_oper_status_notification_t operStatus;
m_portStateChangeHandler.handlePortStateChangeNotification(/*count=*/1,
&operStatus);
EXPECT_EQ(queue->size(), 1);
}

0 comments on commit 26ffcca

Please sign in to comment.