Commit dcddecbc authored by Pietro Fezzardi's avatar Pietro Fezzardi Committed by Alessandro Rubini

arch-sim: network operations and main loop

New struct sim_pending_pkt is introduced to hold information on pakets still in
fly. An array of such structures is stored in ppg->arch_data. This array is
written by every send. The insertion is designed to sort automatically the
packets so that the first element of the array after an insertion is always
the first packet that will be received. Function sim_fast_forward_ns is changed
to update also the reception times into this array, so that when you fast
forward, also the pending packets are fast_forwarded.
The main loop works in this way:
	- if there are packets to be received check when
	- if the first packet to be received is coming before the state machine
	  timestamps expire, then fast forward till it's time to receive it.
	  Then receive it and call the state machine of the receiver
	- if there are no packet pending or the first pending packet is coming
	  after a timeout, then fast forward till the timeout expires and
	  run all the state machines
parent d53137e5
...@@ -10,8 +10,8 @@ OBJ-y += $A/sim-startup.o \ ...@@ -10,8 +10,8 @@ OBJ-y += $A/sim-startup.o \
$A/sim-conf.o \ $A/sim-conf.o \
lib/cmdline.o \ lib/cmdline.o \
lib/conf.o \ lib/conf.o \
lib/dump-funcs.o \
lib/div64.o lib/div64.o
# lib/dump-funcs.o \
# lib/libc-functions.o \ # lib/libc-functions.o \
# Support only "sim" time operations # Support only "sim" time operations
......
/* /*
* Copyright (C) 2013 CERN (ww.cern.ch) * Copyright (C) 2013 CERN (www.cern.ch)
* Author: Pietro Fezzardi (pietrofezzardi@gmail.com) * Author: Pietro Fezzardi (pietrofezzardi@gmail.com)
* *
* Released to the public domain * Released to the public domain
*/ */
/*
* This is the main loop for the simulator.
*/
#include <stdlib.h>
#include <errno.h>
#include <sys/select.h>
#include <linux/if_ether.h>
#include <ppsi/ppsi.h> #include <ppsi/ppsi.h>
#include "ppsi-sim.h" #include "ppsi-sim.h"
/* Call pp_state_machine for each instance. To be called periodically,
* when no packets are incoming */
static int run_all_state_machines(struct pp_globals *ppg)
{
int j;
int delay_ms = 0, delay_ms_j;
for (j = 0; j < ppg->nlinks; j++) {
struct pp_instance *ppi = &ppg->pp_instances[j];
sim_set_global_DS(ppi);
delay_ms_j = pp_state_machine(ppi, NULL, 0);
/* delay_ms is the least delay_ms among all instances */
if (j == 0)
delay_ms = delay_ms_j;
if (delay_ms_j < delay_ms)
delay_ms = delay_ms_j;
}
return delay_ms;
}
void sim_main_loop(struct pp_globals *ppg) void sim_main_loop(struct pp_globals *ppg)
{ {
return; struct pp_instance *ppi;
struct sim_ppg_arch_data *data = SIM_PPG_ARCH(ppg);
int64_t delay_ns, tmp_ns;
int j, i;
/* Initialize each link's state machine */
for (j = 0; j < ppg->nlinks; j++) {
ppi = &ppg->pp_instances[j];
ppi->is_new_state = 1;
}
delay_ns = run_all_state_machines(ppg) * 1000LL * 1000LL;
while (1) {
/*
* If Ebest was changed in previous loop, run best
* master clock before checking for new packets, which
* would affect port state again
*/
if (ppg->ebest_updated) {
for (j = 0; j < ppg->nlinks; j++) {
int new_state;
struct pp_instance *ppi = &ppg->pp_instances[j];
new_state = bmc(ppi);
if (new_state != ppi->state) {
ppi->state = new_state;
ppi->is_new_state = 1;
}
}
ppg->ebest_updated = 0;
}
while (data->n_pending && data->pending->delay_ns <= delay_ns) {
ppi = &ppg->pp_instances[data->pending->which_ppi];
sim_fast_forward_ns(ppg, data->pending->delay_ns);
delay_ns -= data->pending->delay_ns;
i = ppi->n_ops->recv(ppi, ppi->rx_frame,
PP_MAX_FRAME_LENGTH - 4,
&ppi->last_rcv_time);
if (i < PP_MINIMUM_LENGTH) {
pp_diag(ppi, frames, 1, "Error or short frame: "
"%d < %d\n", i, PP_MINIMUM_LENGTH);
continue;
}
sim_set_global_DS(ppi);
tmp_ns = 1000LL * 1000LL * pp_state_machine(ppi,
ppi->rx_ptp, i - NP(ppi)->ptp_offset);
if (tmp_ns < delay_ns)
delay_ns = tmp_ns;
}
/* here we have no pending packets or the timeout for a state
* machine is expired (so delay_ns == 0). If the timeout is not
* expired we just fast forward till it's not expired, since we
* know that there are no packets pending. */
sim_fast_forward_ns(ppg, delay_ns);
delay_ns = run_all_state_machines(ppg) * 1000LL * 1000LL;
}
} }
...@@ -31,13 +31,32 @@ struct pp_sim_net_delay { ...@@ -31,13 +31,32 @@ struct pp_sim_net_delay {
}; };
/* /*
* This structure holds the lowest timeout of all the state machines in the * This structure represets a pending packet. which_ppi is the destination ppi,
* ppg, namely the master and slave state machines in the simulator. All the * chtype is the channel and delay_ns is the time that should pass from when the
* future configuration parameters needed from both master and slave ppi * packet is sent until it will be received from the destination.
* can be added here. * Every time a packet is sent a structure like this is filled and stored in the
* ppg->arch_data so that we always know which is the first packet that has to
* be received and when.
*/
struct sim_pending_pkt {
int64_t delay_ns;
int which_ppi;
int chtype;
};
/*
* Structure holding an array of pending packets. 64 does not have a special
* meaning. They could be less if you look inside the ptp specification, but
* I put 64 just to be sure.
* The aim of this structure is to store information on flying packets and when
* the'll be received, because the standard state machine timeouts are not
* enough for this. Infact the main loop need to know if there are some packets
* arriving and when, otherwise it will not know how much fast forwarding is
* needed. If you fast forward based on timeouts they will expire before any
* packet has arrived and the state machine will do nothing.
*/ */
struct sim_ppg_arch_data { struct sim_ppg_arch_data {
struct timeval tv; int n_pending;
struct sim_pending_pkt pending[64];
}; };
static inline struct sim_ppg_arch_data *SIM_PPG_ARCH(struct pp_globals *ppg) static inline struct sim_ppg_arch_data *SIM_PPG_ARCH(struct pp_globals *ppg)
...@@ -105,3 +124,6 @@ static inline int pp_sim_is_slave(struct pp_instance *ppi) ...@@ -105,3 +124,6 @@ static inline int pp_sim_is_slave(struct pp_instance *ppi)
extern int sim_fast_forward_ns(struct pp_globals *ppg, int64_t ff_ns); extern int sim_fast_forward_ns(struct pp_globals *ppg, int64_t ff_ns);
extern int sim_set_global_DS(struct pp_instance *ppi); extern int sim_set_global_DS(struct pp_instance *ppi);
extern void sim_main_loop(struct pp_globals *ppg); extern void sim_main_loop(struct pp_globals *ppg);
extern struct pp_network_operations sim_master_net_ops;
extern struct pp_network_operations sim_slave_net_ops;
...@@ -122,7 +122,7 @@ int main(int argc, char **argv) ...@@ -122,7 +122,7 @@ int main(int argc, char **argv)
* to set the initial offset for the slave * to set the initial offset for the slave
*/ */
sim_set_global_DS(ppi_master); sim_set_global_DS(ppi_master);
pp_config_string(ppg, strdup("port SIM_MASTER; iface MASTER;" pp_config_string(ppg, strdup("port SIM_MASTER; iface lo;"
"proto udp; role master;" "proto udp; role master;"
"sim_init_master_time 10.0;")); "sim_init_master_time 10.0;"));
...@@ -144,6 +144,8 @@ int main(int argc, char **argv) ...@@ -144,6 +144,8 @@ int main(int argc, char **argv)
pp_printf("Warning: simulator doesn't support raw " pp_printf("Warning: simulator doesn't support raw "
"ethernet. Using UDP\n"); "ethernet. Using UDP\n");
ppi->ethernet_mode = 0; ppi->ethernet_mode = 0;
NP(ppi)->ch[PP_NP_GEN].fd = -1;
NP(ppi)->ch[PP_NP_EVT].fd = -1;
if (ppi->cfg.role == PPSI_ROLE_MASTER) { if (ppi->cfg.role == PPSI_ROLE_MASTER) {
ppi->master_only = 1; ppi->master_only = 1;
ppi->slave_only = 0; ppi->slave_only = 0;
...@@ -152,12 +154,13 @@ int main(int argc, char **argv) ...@@ -152,12 +154,13 @@ int main(int argc, char **argv)
ppi->slave_only = 1; ppi->slave_only = 1;
} }
ppi->t_ops = &DEFAULT_TIME_OPS; ppi->t_ops = &DEFAULT_TIME_OPS;
//ppi->n_ops = &DEFAULT_NET_OPS;
} }
sim_set_global_DS(ppi_master); sim_set_global_DS(ppi_master);
pp_init_globals(ppg, &sim_master_rt_opts); pp_init_globals(ppg, &sim_master_rt_opts);
ppi_master->n_ops = &sim_master_net_ops;
sim_set_global_DS(ppi_slave); sim_set_global_DS(ppi_slave);
pp_init_globals(ppg, &__pp_default_rt_opts); pp_init_globals(ppg, &__pp_default_rt_opts);
ppi_slave->n_ops = &sim_slave_net_ops;
sim_main_loop(ppg); sim_main_loop(ppg);
return 0; return 0;
......
/*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Pietro Fezzardi (pietrofezzardi@gmail.com)
*
* Released according to the GNU LGPL, version 2.1 or any later version.
*/
/* Socket interface for GNU/Linux (and most likely other posix systems) */
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <ppsi/ppsi.h>
#include "ptpdump.h"
#include "../arch-sim/ppsi-sim.h"
#define PP_SIM_GEN_PORT (10000 + PP_GEN_PORT)
#define PP_SIM_EVT_PORT (10000 + PP_EVT_PORT)
/* Returns 1 if p1 has higher priority than p2 */
static int compare_pending(struct sim_pending_pkt *p1,
struct sim_pending_pkt *p2)
{
/* expires earlier ---> higher priority */
if (p1->delay_ns < p2->delay_ns)
return 1;
/* same expire time ---> higher priority to the slave beacuse it has to
* handle Sync and Follow_Up in a row. If both are for the slave handle
* PP_NP_EVT first */
if (p1->delay_ns == p2->delay_ns) {
if (p1->which_ppi > p2->which_ppi)
return 1;
if (p1->which_ppi == p2->which_ppi)
return (p1->chtype > p2->chtype);
}
return 0;
}
static int insert_pending(struct sim_ppg_arch_data *data,
struct sim_pending_pkt *new)
{
struct sim_pending_pkt *pkt, tmp;
int i = data->n_pending;
pkt = &data->pending[i];
*pkt = *new;
pkt--;
while (compare_pending(new, pkt) && (i > 0)) {
tmp = *pkt;
*pkt = *new;
*new = tmp;
pkt--;
new--;
}
data->n_pending++;
return 0;
}
static int pending_received(struct sim_ppg_arch_data *data)
{
int i;
if (data->n_pending == 0)
return 0;
for (i = 0; i < data->n_pending; i++)
data->pending[i] = data->pending[i+1];
data->n_pending--;
return 0;
}
static int sim_recv_msg(struct pp_instance *ppi, int fd, void *pkt, int len,
TimeInternal *t)
{
ssize_t ret;
struct msghdr msg;
struct iovec vec[1];
union {
struct cmsghdr cm;
char control[512];
} cmsg_un;
vec[0].iov_base = pkt;
vec[0].iov_len = PP_MAX_FRAME_LENGTH;
memset(&msg, 0, sizeof(msg));
memset(&cmsg_un, 0, sizeof(cmsg_un));
/* msg_name, msg_namelen == 0: not used */
msg.msg_iov = vec;
msg.msg_iovlen = 1;
msg.msg_control = cmsg_un.control;
msg.msg_controllen = sizeof(cmsg_un.control);
ret = recvmsg(fd, &msg, 0);
if (ret <= 0) {
if (errno == EAGAIN || errno == EINTR)
return 0;
return ret;
}
if (msg.msg_flags & MSG_TRUNC) {
pp_error("%s: truncated message\n", __func__);
return 0;
}
/* get time stamp of packet */
if (msg.msg_flags & MSG_CTRUNC) {
pp_error("%s: truncated ancillary data\n", __func__);
return 0;
}
ppi->t_ops->get(ppi, t);
/* This is not really hw... */
pp_diag(ppi, time, 2, "recv stamp: %i.%09i (%s)\n",
(int)t->seconds, (int)t->nanoseconds, "user");
return ret;
}
static int sim_net_recv(struct pp_instance *ppi, void *pkt, int len,
TimeInternal *t)
{
struct sim_ppg_arch_data *data = SIM_PPG_ARCH(ppi->glbs);
struct pp_channel *ch;
int ret;
/*
* only UDP
* We can return one frame only. Look in the global structure to know if
* the pending packet is on PP_NP_GEN or PP_NP_EVT
*/
if (data->n_pending <= 0)
return 0;
ch = &(NP(ppi)->ch[data->pending->chtype]);
ret = -1;
if (ch->pkt_present > 0) {
ret = sim_recv_msg(ppi, ch->fd, pkt, len, t);
if (ret > 0)
ch->pkt_present--;
}
if (ret > 0 && pp_diag_allow(ppi, frames, 2))
dump_payloadpkt("recv: ", pkt, ret, t);
/* remove received packet from pending */
pending_received(SIM_PPG_ARCH(ppi->glbs));
return ret;
}
static int sim_net_send(struct pp_instance *ppi, void *pkt, int len,
TimeInternal *t, int chtype, int use_pdelay_addr)
{
struct sockaddr_in addr;
struct sim_pending_pkt pending;
int ret;
/* only UDP */
addr.sin_family = AF_INET;
if (ppi - ppi->glbs->pp_instances == SIM_SLAVE)
addr.sin_port = htons(chtype == PP_NP_GEN ?
PP_SIM_GEN_PORT :
PP_SIM_EVT_PORT);
else
addr.sin_port = htons(chtype == PP_NP_GEN ?
PP_GEN_PORT :
PP_EVT_PORT);
addr.sin_addr.s_addr = NP(ppi)->mcast_addr;
if (t)
ppi->t_ops->get(ppi, t);
ret = sendto(NP(ppi)->ch[chtype].fd, pkt, len, 0,
(struct sockaddr *)&addr, sizeof(struct sockaddr_in));
if (pp_diag_allow(ppi, frames, 2))
dump_payloadpkt("send: ", pkt, len, t);
/* store pending packets in global structure */
pending.chtype = chtype;
if (ppi - ppi->glbs->pp_instances == SIM_MASTER)
pending.which_ppi = SIM_SLAVE;
else
pending.which_ppi = SIM_MASTER;
pending.delay_ns = SIM_PPI_ARCH(ppi)->n_delay.t_prop_ns +
SIM_PPI_ARCH(ppi)->n_delay.jit_ns;
insert_pending(SIM_PPG_ARCH(ppi->glbs), &pending);
NP(SIM_PPI_ARCH(ppi)->other_ppi)->ch[chtype].pkt_present++;
return ret;
}
static int sim_net_exit(struct pp_instance *ppi)
{
int fd;
int i;
/* only UDP */
for (i = PP_NP_GEN; i <= PP_NP_EVT; i++) {
fd = NP(ppi)->ch[i].fd;
if (fd < 0)
continue;
close(fd);
NP(ppi)->ch[i].fd = -1;
}
return 0;
}
static int sim_open_ch(struct pp_instance *ppi, char *ifname, int chtype)
{
int sock = -1;
int temp;
struct in_addr iface_addr;
struct ifreq ifr;
struct sockaddr_in addr;
char *context;
/* only UDP */
context = "socket()";
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0)
goto err_out;
NP(ppi)->ch[chtype].fd = sock;
/* hw interface information */
memset(&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, ifname);
context = "ioctl(SIOCGIFINDEX)";
if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0)
goto err_out;
context = "ioctl(SIOCGIFHWADDR)";
if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0)
goto err_out;
memcpy(NP(ppi)->ch[chtype].addr, ifr.ifr_hwaddr.sa_data, 6);
context = "ioctl(SIOCGIFADDR)";
if (ioctl(sock, SIOCGIFADDR, &ifr) < 0)
goto err_out;
iface_addr.s_addr =
((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
pp_diag(ppi, frames, 2, "Local IP address used : %s\n",
inet_ntoa(iface_addr));
temp = 1; /* allow address reuse */
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
&temp, sizeof(int)) < 0)
pp_printf("%s: ioctl(SO_REUSEADDR): %s\n", __func__,
strerror(errno));
/* bind sockets */
/* need INADDR_ANY to allow receipt of multi-cast and uni-cast
* messages */
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if ((ppi - ppi->glbs->pp_instances) == SIM_MASTER)
addr.sin_port = htons(chtype == PP_NP_GEN ?
PP_SIM_GEN_PORT :
PP_SIM_EVT_PORT);
else
addr.sin_port = htons(chtype == PP_NP_GEN ?
PP_GEN_PORT :
PP_EVT_PORT);
context = "bind()";
if (bind(sock, (struct sockaddr *)&addr,
sizeof(struct sockaddr_in)) < 0)
goto err_out;
NP(ppi)->ch[chtype].fd = sock;
/*
* Standard ppsi state machine is designed to drop packets coming from
* itself, based on the clockIdentity. This hack avoids this behaviour,
* changing the clockIdentity of the master.
*/
if (ppi - ppi->glbs->pp_instances == SIM_MASTER)
memset(NP(ppi)->ch[chtype].addr, 111, 1);
return 0;
err_out:
pp_printf("%s: %s: %s\n", __func__, context, strerror(errno));
if (sock >= 0)
close(sock);
NP(ppi)->ch[chtype].fd = -1;
return -1;
}
static int sim_net_init(struct pp_instance *ppi)
{
int i;
if (NP(ppi)->ch[0].fd > 0)
sim_net_exit(ppi);
/* The buffer is inside ppi, but we need to set pointers and align */
pp_prepare_pointers(ppi);
/* only UDP, RAW is not supported */
pp_diag(ppi, frames, 1, "sim_net_init UDP\n");
for (i = PP_NP_GEN; i <= PP_NP_EVT; i++) {
if (sim_open_ch(ppi, ppi->iface_name, i))
return -1;
}
return 0;
}
struct pp_network_operations sim_master_net_ops = {
.init = sim_net_init,
.exit = sim_net_exit,
.recv = sim_net_recv,
.send = sim_net_send,
.check_packet = NULL,
};
struct pp_network_operations sim_slave_net_ops = {
.init = sim_net_init,
.exit = sim_net_exit,
.recv = sim_net_recv,
.send = sim_net_send,
.check_packet = NULL,
};
...@@ -19,6 +19,7 @@ int sim_fast_forward_ns(struct pp_globals *ppg, int64_t ff_ns) ...@@ -19,6 +19,7 @@ int sim_fast_forward_ns(struct pp_globals *ppg, int64_t ff_ns)
{ {
struct pp_sim_time_instance *t_inst; struct pp_sim_time_instance *t_inst;
int i; int i;
for (i = 0; i < ppg->nlinks; i++) { for (i = 0; i < ppg->nlinks; i++) {
t_inst = &SIM_PPI_ARCH(ppg->pp_instances + i)->time; t_inst = &SIM_PPI_ARCH(ppg->pp_instances + i)->time;
t_inst->current_ns += ff_ns + t_inst->current_ns += ff_ns +
...@@ -26,6 +27,18 @@ int sim_fast_forward_ns(struct pp_globals *ppg, int64_t ff_ns) ...@@ -26,6 +27,18 @@ int sim_fast_forward_ns(struct pp_globals *ppg, int64_t ff_ns)
ff_ns / 1000 / 1000 / 1000; ff_ns / 1000 / 1000 / 1000;
} }
pp_diag(0, time, 1, "%s: %li ns\n", __func__, (long)ff_ns); pp_diag(0, time, 1, "%s: %li ns\n", __func__, (long)ff_ns);
struct sim_pending_pkt *pkt;
struct sim_ppg_arch_data *data = SIM_PPG_ARCH(ppg);
if (data->n_pending) {
for (i = 0; i < data->n_pending; i++) {
pkt = &data->pending[i];
pkt->delay_ns -= ff_ns;
if (pkt->delay_ns < 0)
exit(-1);
}
}
return 0; return 0;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment