A Discrete-Event Network Simulator
API
netmap-net-device.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 Universita' degli Studi di Napoli Federico II
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * Author: Pasquale Imputato <p.imputato@gmail.com>
18  */
19 
20 #include "netmap-net-device.h"
21 
22 #include "ns3/uinteger.h"
23 
24 #include <sys/ioctl.h>
25 #include <thread>
26 #include <unistd.h>
27 
28 namespace ns3
29 {
30 
31 NS_LOG_COMPONENT_DEFINE("NetmapNetDevice");
32 
33 TypeId
35 {
36  static TypeId tid = TypeId("ns3::NetDeviceQueueLock")
38  .SetGroupName("Network")
39  .AddConstructor<NetDeviceQueueLock>();
40  return tid;
41 }
42 
44 {
45 }
46 
48 {
49 }
50 
51 bool
53 {
54  m_mutex.lock();
55  bool stopped = NetDeviceQueue::IsStopped();
56  m_mutex.unlock();
57  return stopped;
58 }
59 
60 void
62 {
63  m_mutex.lock();
65  m_mutex.unlock();
66 }
67 
68 void
70 {
71  m_mutex.lock();
73  m_mutex.unlock();
74 }
75 
76 void
78 {
79  m_mutex.lock();
81  m_mutex.unlock();
82 }
83 
84 void
86 {
87  m_mutex.lock();
89  m_mutex.unlock();
90 }
91 
92 void
94 {
95  m_mutex.lock();
97  m_mutex.unlock();
98 }
99 
101  : m_bufferSize(65536),
102  // Defaults to maximum TCP window size
103  m_nifp(nullptr)
104 {
105 }
106 
107 void
109 {
110  NS_LOG_FUNCTION(this << bufferSize);
111  m_bufferSize = bufferSize;
112 }
113 
114 void
116 {
117  NS_LOG_FUNCTION(this << nifp);
118  m_nifp = nifp;
119 }
120 
123 {
124  NS_LOG_FUNCTION(this);
125 
126  uint8_t* buf = (uint8_t*)malloc(m_bufferSize);
127  NS_ABORT_MSG_IF(buf == 0, "malloc() failed");
128 
129  NS_LOG_LOGIC("Calling read on fd " << m_fd);
130 
131  struct netmap_ring* rxring;
132  uint16_t len = 0;
133  uint32_t rxRingIndex = 0;
134 
135  // we have a packet in one of the receiver rings
136  // we check for the first non empty receiver ring
137  while (rxRingIndex < m_nifp->ni_rx_rings)
138  {
139  rxring = NETMAP_RXRING(m_nifp, rxRingIndex);
140 
141  if (!nm_ring_empty(rxring))
142  {
143  uint32_t i = rxring->cur;
144  uint8_t* buffer = (uint8_t*)NETMAP_BUF(rxring, rxring->slot[i].buf_idx);
145  len = rxring->slot[i].len;
146  NS_LOG_DEBUG("Received a packet of " << len << " bytes");
147 
148  // copy buffer in the destination memory area
149  memcpy(buf, buffer, len);
150 
151  // advance the netmap pointers and sync the fd
152  rxring->head = rxring->cur = nm_ring_next(rxring, i);
153 
154  ioctl(m_fd, NIOCRXSYNC, nullptr);
155 
156  break;
157  }
158 
159  rxRingIndex++;
160  }
161 
162  if (len <= 0)
163  {
164  free(buf);
165  buf = 0;
166  len = 0;
167  }
168  NS_LOG_LOGIC("Read " << len << " bytes on fd " << m_fd);
169  return FdReader::Data(buf, len);
170 }
171 
173 
174 TypeId
176 {
177  static TypeId tid =
178  TypeId("ns3::NetmapNetDevice")
180  .SetGroupName("FdNetDevice")
181  .AddConstructor<NetmapNetDevice>()
182  .AddAttribute("SyncAndNotifyQueuePeriod",
183  "The period of time (in number of us) after which the device syncs the "
184  "netmap ring and notifies queue status.",
185  UintegerValue(50),
187  MakeUintegerChecker<uint8_t>());
188  return tid;
189 }
190 
192 {
193  NS_LOG_FUNCTION(this);
194  m_nifp = nullptr;
195  m_nTxRings = 0;
196  m_nTxRingsSlots = 0;
197  m_nRxRings = 0;
198  m_nRxRingsSlots = 0;
199  m_queue = nullptr;
200  m_totalQueuedBytes = 0;
202 }
203 
205 {
206  NS_LOG_FUNCTION(this);
207  m_nifp = nullptr;
208  m_queue = nullptr;
209 }
210 
213 {
214  NS_LOG_FUNCTION(this);
215 
216  Ptr<NetmapNetDeviceFdReader> fdReader = Create<NetmapNetDeviceFdReader>();
217  // 22 bytes covers 14 bytes Ethernet header with possible 8 bytes LLC/SNAP
218  fdReader->SetBufferSize(GetMtu() + 22);
219  fdReader->SetNetmapIfp(m_nifp);
220  return fdReader;
221 }
222 
223 void
225 {
226  NS_LOG_FUNCTION(this);
227 
230 }
231 
232 void
234 {
235  NS_LOG_FUNCTION(this);
236 
237  m_queue->Stop();
238 
240 
241  if (m_syncAndNotifyQueueThread.joinable())
242  {
244  }
245 }
246 
247 uint32_t
249 {
250  NS_LOG_FUNCTION(this);
251 
252  struct netmap_ring* txring;
253  txring = NETMAP_TXRING(m_nifp, 0);
254 
255  int tail = txring->tail;
256 
257  // the netmap ring has one slot reserved
258  int inQueue = (m_nTxRingsSlots - 1) - nm_ring_space(txring);
259 
260  uint32_t bytesInQueue = 0;
261 
262  for (int i = 1; i < inQueue; i++)
263  {
264  bytesInQueue += txring->slot[tail].len;
265  tail++;
266  tail = tail % m_nTxRingsSlots;
267  }
268 
269  return bytesInQueue;
270 }
271 
272 void
274 {
275  NS_LOG_FUNCTION(this);
276 
277  m_queue = queue;
278 }
279 
280 void
282 {
283  NS_LOG_FUNCTION(this << nifp);
284 
285  m_nifp = nifp;
286 }
287 
288 void
289 NetmapNetDevice::SetTxRingsInfo(uint32_t nTxRings, uint32_t nTxRingsSlots)
290 {
291  NS_LOG_FUNCTION(this << nTxRings << nTxRingsSlots);
292 
293  m_nTxRings = nTxRings;
294  m_nTxRingsSlots = nTxRingsSlots;
295 }
296 
297 void
298 NetmapNetDevice::SetRxRingsInfo(uint32_t nRxRings, uint32_t nRxRingsSlots)
299 {
300  NS_LOG_FUNCTION(this << nRxRings << nRxRingsSlots);
301 
302  m_nRxRings = nRxRings;
303  m_nRxRingsSlots = nRxRingsSlots;
304 }
305 
306 int
308 {
309  NS_LOG_FUNCTION(this);
310 
311  struct netmap_ring* txring;
312  txring = NETMAP_TXRING(m_nifp, 0);
313 
314  return nm_ring_space(txring);
315 }
316 
317 // This function runs in a separate thread.
318 void
320 {
321  NS_LOG_FUNCTION(this);
322 
323  struct netmap_ring* txring = NETMAP_TXRING(m_nifp, 0);
324 
325  uint32_t prevTotalTransmittedBytes = 0;
326 
327  while (m_syncAndNotifyQueueThreadRun == true)
328  {
329  // we sync the netmap ring periodically.
330  // the traffic control layer can write packets during the period between two syncs.
331  ioctl(GetFileDescriptor(), NIOCTXSYNC, nullptr);
332 
333  // we need of a nearly periodic notification to queue limits of the transmitted bytes.
334  uint32_t totalTransmittedBytes = m_totalQueuedBytes - GetBytesInNetmapTxRing();
335  uint32_t deltaBytes = totalTransmittedBytes - prevTotalTransmittedBytes;
336  NS_LOG_DEBUG(deltaBytes << " delta transmitted bytes");
337  prevTotalTransmittedBytes = totalTransmittedBytes;
338  if (m_queue)
339  {
340  m_queue->NotifyTransmittedBytes(deltaBytes);
341 
342  if (GetSpaceInNetmapTxRing() >= 32) // WAKE_THRESHOLD
343  {
344  if (m_queue->IsStopped())
345  {
346  m_queue->Wake();
347  }
348  }
349  }
350 
352 
353  NS_LOG_DEBUG("Space in the netmap ring of " << nm_ring_space(txring) << " packets");
354  }
355 
356  ioctl(GetFileDescriptor(), NIOCTXSYNC, nullptr);
357 }
358 
359 ssize_t
360 NetmapNetDevice::Write(uint8_t* buffer, size_t length)
361 {
362  NS_LOG_FUNCTION(this << buffer << length);
363 
364  struct netmap_ring* txring;
365 
366  // we use one ring also in case of multiqueue device to perform an accurate flow control on that
367  // ring
368  txring = NETMAP_TXRING(m_nifp, 0);
369 
370  uint16_t ret = -1;
371 
372  if (m_queue->IsStopped())
373  {
374  // the device queue is stopped and we cannot write other packets
375  return ret;
376  }
377 
378  if (!nm_ring_empty(txring))
379  {
380  uint32_t i = txring->cur;
381  uint8_t* buf = (uint8_t*)NETMAP_BUF(txring, txring->slot[i].buf_idx);
382 
383  memcpy(buf, buffer, length);
384  txring->slot[i].len = length;
385 
386  txring->head = txring->cur = nm_ring_next(txring, i);
387 
388  ret = length;
389 
390  // we update the total transmitted bytes counter and notify queue limits of the queued bytes
391  m_totalQueuedBytes += length;
392  m_queue->NotifyQueuedBytes(length);
393 
394  // if there is no room for other packets then stop the queue.
395  if (nm_ring_space(txring) == 0)
396  {
397  m_queue->Stop();
398  }
399  }
400 
401  return ret;
402 }
403 
404 } // namespace ns3
a NetDevice to read/write network traffic from/into a file descriptor.
Definition: fd-net-device.h:84
int GetFileDescriptor() const
Get the associated file descriptor.
uint16_t GetMtu() const override
int m_fd
The file descriptor to read from.
Definition: fd-reader.h:135
Network device transmission queue.
virtual void Stop()
Called by the device to stop this device transmission queue.
virtual void Wake()
Called by the device to wake the queue disc associated with this device transmission queue.
virtual bool IsStopped() const
Get the status of the device transmission queue.
virtual void Start()
Called by the device to start this device transmission queue.
virtual void NotifyTransmittedBytes(uint32_t bytes)
Called by the netdevice to report the number of bytes it is going to transmit.
virtual void NotifyQueuedBytes(uint32_t bytes)
Called by the netdevice to report the number of bytes queued to the device queue.
Network device transmission queue with lock.
virtual void NotifyTransmittedBytes(uint32_t bytes)
Called by the netdevice to report the number of bytes it is going to transmit.
virtual void NotifyQueuedBytes(uint32_t bytes)
Called by the netdevice to report the number of bytes queued to the device queue.
virtual bool IsStopped() const
Get the status of the device transmission queue.
virtual void Wake()
Called by the device to wake the queue disc associated with this device transmission queue.
std::mutex m_mutex
Mutex to serialize the operations performed on the queue.
static TypeId GetTypeId()
Get the type ID.
virtual void Stop()
Called by the device to stop this device transmission queue.
virtual void Start()
Called by the device to start this device transmission queue.
void SetBufferSize(uint32_t bufferSize)
Set size of the read buffer.
FdReader::Data DoRead()
The read implementation.
void SetNetmapIfp(struct netmap_if *nifp)
Set netmap interface representation.
struct netmap_if * m_nifp
Netmap interface representation.
uint32_t m_bufferSize
size of the read buffer
a NetDevice to read/write network traffic from/into a netmap file descriptor.
int GetSpaceInNetmapTxRing() const
Get the number of slots currently available in the netmap transmission ring.
uint32_t m_nRxRings
Number of receiver rings.
void SetRxRingsInfo(uint32_t nRxRings, uint32_t nRxRingsSlots)
Set the netmap receiver rings info.
void SetNetmapInterfaceRepresentation(struct netmap_if *nifp)
Set the netmap interface representation.
void DoFinishStoppingDevice()
Complete additional actions, if any, to tear down the device.
Ptr< NetDeviceQueue > m_queue
NetDevice queue.
uint8_t m_syncAndNotifyQueuePeriod
The period of time in us after which the device syncs the netmap ring and notifies queue status.
void DoFinishStartingDevice()
Complete additional actions, if any, to spin up down the device.
virtual ssize_t Write(uint8_t *buffer, size_t length)
The function Writes a packet into the netmap transmission ring.
uint32_t m_nTxRingsSlots
Number of slots in the transmission rings.
uint32_t GetBytesInNetmapTxRing()
Get the number of bytes currently in the netmap transmission ring.
std::thread m_syncAndNotifyQueueThread
Thread used to perform the flow control.
uint32_t m_nRxRingsSlots
Number of slots in the receiver rings.
struct netmap_if * m_nifp
Netmap interface representation.
uint32_t m_nTxRings
Number of transmission rings.
static TypeId GetTypeId()
Get the type ID.
void SetNetDeviceQueue(Ptr< NetDeviceQueue > queue)
Set the NetDeviceQueue.
uint32_t m_totalQueuedBytes
Total queued bytes.
std::atomic< bool > m_syncAndNotifyQueueThreadRun
Running flag of the flow control thread.
Ptr< FdReader > DoCreateFdReader()
Create the FdReader object.
virtual void SyncAndNotifyQueue()
This function syncs netmap ring and notifies netdevice queue.
void SetTxRingsInfo(uint32_t nTxRings, uint32_t nTxRingsSlots)
Set the netmap transmission rings info.
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:931
Hold an unsigned integer type.
Definition: uinteger.h:45
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:282
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Definition: uinteger.h:46
A structure representing data read.
Definition: fd-reader.h:91