From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 27 Dec 2021 16:06:10 +0000 (+0000) Subject: Adding more multi-client functionality (still incomplete) but keeping the original... X-Git-Tag: 1.2~55 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=96dd6c2bb093033f8e4253da5d93e018cb51c6a2;p=thirdparty%2Fnqptp.git Adding more multi-client functionality (still incomplete) but keeping the original single client functional for the present. --- diff --git a/nqptp-clock-sources.c b/nqptp-clock-sources.c index 0f0ae08..ede9f3d 100644 --- a/nqptp-clock-sources.c +++ b/nqptp-clock-sources.c @@ -27,7 +27,13 @@ #include #include #include -#include +#include // for ftruncate and others +#include // for ftruncate and others + +#include /* For O_* constants */ +#include // for shared memory stuff +#include // for fd_set +#include // umask #ifdef CONFIG_FOR_FREEBSD #include @@ -38,6 +44,142 @@ #endif clock_source_private_data clocks_private[MAX_CLOCKS]; +client_record clients[MAX_CLIENTS]; + +int find_client_id(char *client_shared_memory_interface_name) { + int response = -1; // signify not found + if (client_shared_memory_interface_name != NULL) { + int i = 0; + // first, see if yu can find it anywhere + while ((response == -1) && (i < MAX_CLIENTS)) { + if (strcmp(clients[i].shm_interface_name, client_shared_memory_interface_name) == 0) + response = i; + else + i++; + } + } + return response; +} + +int get_client_id(char *client_shared_memory_interface_name) { + int response = -1; // signify not found + if (client_shared_memory_interface_name != NULL) { + int i = 0; + // first, see if yu can find it anywhere + while ((response == -1) && (i < MAX_CLIENTS)) { + if (strcmp(clients[i].shm_interface_name, client_shared_memory_interface_name) == 0) + response = i; + else + i++; + } + + if (response == -1) { // no match, so create one + i = 0; + while ((response == -1) && (i < MAX_CLIENTS)) { + if (clients[i].shm_interface_name[0] == '\0') + response = i; + else + i++; + } + if (response != -1) { + pthread_mutexattr_t shared; + int err; + strncpy(clients[i].shm_interface_name, client_shared_memory_interface_name, + sizeof(clients[i].shm_interface_name)); + // creat the named smi interface + + // open a shared memory interface. + clients[i].shm_fd = -1; + + mode_t oldumask = umask(0); + clients[i].shm_fd = shm_open(client_shared_memory_interface_name, O_RDWR | O_CREAT, 0666); + if (clients[i].shm_fd == -1) { + die("cannot open shared memory \"%s\".", client_shared_memory_interface_name); + } + (void)umask(oldumask); + + if (ftruncate(clients[i].shm_fd, sizeof(struct shm_structure)) == -1) { + die("failed to set size of shared memory \"%s\".", client_shared_memory_interface_name); + } + +#ifdef CONFIG_FOR_FREEBSD + clients[i].shared_memory = + (struct shm_structure *)mmap(NULL, sizeof(struct shm_structure), PROT_READ | PROT_WRITE, + MAP_SHARED, clients[i].shm_fd, 0); +#endif + +#ifdef CONFIG_FOR_LINUX + clients[i].shared_memory = + (struct shm_structure *)mmap(NULL, sizeof(struct shm_structure), PROT_READ | PROT_WRITE, + MAP_LOCKED | MAP_SHARED, clients[i].shm_fd, 0); +#endif + + if (clients[i].shared_memory == (struct shm_structure *)-1) { + die("failed to mmap shared memory \"%s\".", client_shared_memory_interface_name); + } + + if ((close(clients[i].shm_fd) == -1)) { + warn("error closing \"%s\" after mapping.", client_shared_memory_interface_name); + } + + // zero it + memset(clients[i].shared_memory, 0, sizeof(struct shm_structure)); + clients[i].shared_memory->version = NQPTP_SHM_STRUCTURES_VERSION; + + /*create mutex attr */ + err = pthread_mutexattr_init(&shared); + if (err != 0) { + die("mutex attribute initialization failed - %s.", strerror(errno)); + } + pthread_mutexattr_setpshared(&shared, 1); + /*create a mutex */ + err = pthread_mutex_init((pthread_mutex_t *)&clients[i].shared_memory->shm_mutex, &shared); + if (err != 0) { + die("mutex initialization failed - %s.", strerror(errno)); + } + + err = pthread_mutexattr_destroy(&shared); + if (err != 0) { + die("mutex attribute destruction failed - %s.", strerror(errno)); + } + + for (i = 0; i < MAX_CLOCKS; i++) { + clocks_private[i].client_flags[response] = + 0; // turn off all client flags in every clock for this client + } + } else { + debug(1, "could not create a client record for client \"%s\".", + client_shared_memory_interface_name); + } + } + } else { + debug(1, "no client_shared_memory_interface_name"); + } + return response; +} + +int delete_clients() { + int response = 0; // okay unless something happens + int i; + for (i = 0; i < MAX_CLIENTS; i++) { + if (clients[i].shm_interface_name[0] != '\0') { + if (clients[i].shared_memory != NULL) { + // mmap cleanup + if (munmap(clients[i].shared_memory, sizeof(struct shm_structure)) != 0) { + debug(1, "error unmapping shared memory"); + response = -1; + } + // shm_open cleanup + if (shm_unlink(clients[i].shm_interface_name) == -1) { + debug(1, "error unlinking shared memory \"%s\"", clients[i].shm_interface_name); + response = -1; + } + } + clients[i].shm_interface_name[0] = '\0'; // remove the name, just in case + } + } + return response; +} int find_clock_source_record(char *sender_string, clock_source_private_data *clocks_private_info) { // return the index of the clock in the clock information arrays or -1 @@ -45,7 +187,7 @@ int find_clock_source_record(char *sender_string, clock_source_private_data *clo int i = 0; int found = 0; while ((found == 0) && (i < MAX_CLOCKS)) { - if ((clocks_private_info[i].in_use != 0) && + if (((clocks_private_info[i].flags & (1 << clock_is_in_use)) != 0) && (strcasecmp(sender_string, (const char *)&clocks_private_info[i].ip) == 0)) found = 1; else @@ -58,13 +200,13 @@ int find_clock_source_record(char *sender_string, clock_source_private_data *clo int create_clock_source_record(char *sender_string, clock_source_private_data *clocks_private_info) { - // return the index of a clock entry in the clock information arrays or -1 if full + // return the index of a clock entry in the clock information arrays or -1 if full // initialise the entries in the shared and private arrays int response = -1; int i = 0; int found = 0; // trying to find an unused entry while ((found == 0) && (i < MAX_CLOCKS)) { - if (clocks_private_info[i].in_use == 0) + if ((clocks_private_info[i].flags & (1 << clock_is_in_use)) == 0) found = 1; else i++; @@ -89,7 +231,7 @@ int create_clock_source_record(char *sender_string, #ifdef MAX_TIMING_SAMPLES clocks_private_info[i].vacant_samples = MAX_TIMING_SAMPLES; #endif - clocks_private_info[i].in_use = 1; + clocks_private_info[i].flags |= (1 << clock_is_in_use); debug(2, "create record for ip: %s, family: %s.", &clocks_private_info[i].ip, clocks_private_info[i].family == AF_INET6 ? "IPv6" : "IPv4"); } else { @@ -108,7 +250,7 @@ void manage_clock_sources(uint64_t reception_time, clock_source_private_data *cl // do a garbage collect for clock records no longer in use for (i = 0; i < MAX_CLOCKS; i++) { // only if its in use and not a timing peer... don't need a mutex to check - if ((clocks_private_info[i].in_use != 0) && + if (((clocks_private_info[i].flags & (1 << clock_is_in_use)) != 0) && ((clocks_private_info[i].flags & (1 << clock_is_a_timing_peer)) == 0)) { int64_t time_since_last_use = reception_time - clocks_private_info[i].time_of_last_use; // using a sync timeout to determine when to drop the record... @@ -124,7 +266,7 @@ void manage_clock_sources(uint64_t reception_time, clock_source_private_data *cl debug(2, "delete record for: %s.", &clocks_private_info[i].ip); memset(&clocks_private_info[i], 0, sizeof(clock_source_private_data)); if (old_flags != 0) - update_master(); + update_master(0); // TODO else debug_log_nqptp_status(2); } @@ -139,7 +281,7 @@ void update_clock_self_identifications(clock_source_private_data *clocks_private // first, turn off all the self-id flags int i; for (i = 0; i < MAX_CLOCKS; i++) { - clocks_private_info[i].is_one_of_ours = 0; + clocks_private_info[i].flags &= ~(1 << clock_is_one_of_ours); } struct ifaddrs *ifap, *ifa; @@ -166,11 +308,11 @@ void update_clock_self_identifications(clock_source_private_data *clocks_private if (addr != NULL) inet_ntop(family, addr, ip_string, sizeof(ip_string)); if (strlen(ip_string) != 0) { - // now set the is_one_of_ours flag of any clock with this ip + // now set the clock_is_one_of_ours flag of any clock with this ip for (i = 0; i < MAX_CLOCKS; i++) { if (strcasecmp(ip_string, clocks_private_info[i].ip) == 0) { debug(2, "found an entry for one of our clocks"); - clocks_private_info[i].is_one_of_ours = 1; + clocks_private_info[i].flags |= (1 << clock_is_one_of_ours); } } } @@ -188,7 +330,7 @@ void debug_log_nqptp_status(int level) { int records_in_use = 0; int i; for (i = 0; i < MAX_CLOCKS; i++) - if (clocks_private[i].in_use != 0) + if ((clocks_private[i].flags & (1 << clock_is_in_use)) != 0) records_in_use++; debug(level, ""); if (records_in_use > 0) { @@ -200,7 +342,7 @@ void debug_log_nqptp_status(int level) { uint32_t non_peer_clock_mask = (1 << clock_is_valid); uint32_t non_peer_master_mask = non_peer_clock_mask | (1 << clock_is_master); for (i = 0; i < MAX_CLOCKS; i++) { - if (clocks_private[i].in_use != 0) { + if ((clocks_private[i].flags & (1 << clock_is_in_use)) != 0) { if ((clocks_private[i].flags & peer_master_mask) == peer_master_mask) { debug(level, " Peer Master: %" PRIx64 " %s.", clocks_private[i].clock_id, clocks_private[i].ip); @@ -256,7 +398,7 @@ int uint64_cmp(uint64_t a, uint64_t b, const char *cause) { } } -void update_master() { +void update_master(int client_id) { // This implements the IEEE 1588-2008 best master clock algorithm. @@ -353,10 +495,10 @@ void update_master() { } if (best_so_far == -1) { // no master clock - //if (old_master != -1) { - // but there was a master clock, so remove it - debug(1, "Remove master clock."); - update_master_clock_info(0, NULL, 0, 0, 0); + // if (old_master != -1) { + // but there was a master clock, so remove it + debug(1, "Remove master clock."); + update_master_clock_info(0, NULL, 0, 0, 0); //} if (timing_peer_count == 0) debug(2, "no valid qualified clocks "); diff --git a/nqptp-clock-sources.h b/nqptp-clock-sources.h index 85720e7..7b666aa 100644 --- a/nqptp-clock-sources.h +++ b/nqptp-clock-sources.h @@ -22,10 +22,9 @@ #include "nqptp.h" -// transaction tracking -enum stage { waiting_for_sync, sync_seen, follow_up_seen }; - typedef enum { + clock_is_in_use, + clock_is_one_of_ours, clock_is_valid, clock_is_a_timing_peer, clock_is_qualified, @@ -33,15 +32,13 @@ typedef enum { clock_is_master } clock_flags; -// 8 samples per seconds -// #define MAX_TIMING_SAMPLES 47 -// #define MAX_TIMING_SAMPLES 1 - -#ifdef MAX_TIMING_SAMPLES -typedef struct { - uint64_t local_time, clock_time; -} timing_samples; -#endif +/* +typedef enum { + clock_is_a_timing_peer, + clock_is_becoming_master, + clock_is_master +} client_flags; +*/ // information about each clock source typedef struct { @@ -53,8 +50,6 @@ typedef struct { uint64_t local_time; // the local time when the offset was calculated uint64_t source_time; uint64_t local_to_source_time_offset; // add this to the local time to get source time - uint32_t flags; - uint16_t in_use; uint64_t previous_offset, previous_offset_time, last_sync_time; uint64_t mastership_start_time; // set to the time of the first sample used as master @@ -63,15 +58,9 @@ typedef struct { // timing peer group // (A member of the timing peer group could appear and disappear so will not be gc'ed.) // for Announce Qualification - uint64_t announce_times[4]; // we'll check qualification and currency using these - int is_one_of_ours; // true if it is one of our own clocks - -#ifdef MAX_TIMING_SAMPLES - timing_samples samples[MAX_TIMING_SAMPLES]; - int vacant_samples; // the number of elements in the timing_samples array that are not yet used - int next_sample_goes_here; // point to where in the timing samples array the next entries should - // go -#endif + uint64_t announce_times[4]; // we'll check qualification and currency using these + uint8_t flags; // stuff related specifically to the clock itself + uint8_t client_flags[MAX_CLIENTS]; // stuff related to membership of the clients' timing lists // these are for finding the best clock to use // See Figure 27 and 27 pp 89 -- 90 for the Data set comparison algorithm @@ -87,6 +76,15 @@ typedef struct { } clock_source_private_data; +// information on each client +typedef struct { + int shm_fd; + struct shm_structure *shared_memory; // the client's individual smi interface + char shm_interface_name[64]; // it's name + int client_id; // the 1-based index number of clocks' client_flags field associated with this + // interface +} client_record; + int find_clock_source_record(char *sender_string, clock_source_private_data *clocks_private_info); int create_clock_source_record(char *sender_string, clock_source_private_data *clocks_private_info); @@ -95,9 +93,15 @@ void update_clock_self_identifications(clock_source_private_data *clocks_private void manage_clock_sources(uint64_t reception_time, clock_source_private_data *clocks_private_info); +int find_client_id(char *client_shared_memory_interface_name); + +int get_client_id(char *client_shared_memory_interface_name); + +int delete_clients(); + extern clock_source_private_data clocks_private[MAX_CLOCKS]; -void update_master(); +void update_master(int client_id); void debug_log_nqptp_status(int level); diff --git a/nqptp-message-handlers.c b/nqptp-message-handlers.c index c458959..a1be6ed 100644 --- a/nqptp-message-handlers.c +++ b/nqptp-message-handlers.c @@ -29,50 +29,96 @@ void handle_control_port_messages(char *buf, ssize_t recv_len, clock_source_private_data *clock_private_info) { if (recv_len != -1) { buf[recv_len - 1] = 0; // make sure there's a null in it! - debug(2, "New timing peer list: \"%s\".", buf); - if (buf[0] == 'T') { - - char *ip_list = buf + 1; - if (*ip_list == ' ') - ip_list++; - - // turn off all is_timing_peer flags - int i; - for (i = 0; i < MAX_CLOCKS; i++) { - clock_private_info[i].flags &= - ~(1 << clock_is_a_timing_peer); // turn off peer flag (but not the master flag!) - clock_private_info[i].announcements_without_followups = 0; // to allow a possibly silent clocks to be revisited when added to a timing peer list - if (strlen(buf) == 1) { // if it's giving an empty timing peer list, that means drop mastership from the past - clock_private_info[i].flags &= ~(1 << clock_is_master); - clock_private_info[i].mastership_start_time = 0; - clock_private_info[i].previous_offset_time = 0; - } - } - - while (ip_list != NULL) { - char *new_ip = strsep(&ip_list, " "); - // look for the IP in the list of clocks, and create an inert entry if not there - if ((new_ip != NULL) && (new_ip[0] != 0)) { - int t = find_clock_source_record(new_ip, clock_private_info); - if (t == -1) - t = create_clock_source_record(new_ip, clock_private_info); - if (t != -1) // if the clock table is not full, show it's a timing peer - clock_private_info[t].flags |= (1 << clock_is_a_timing_peer); - // otherwise, drop it + debug(1, "New control port message: \"%s\".", buf); + // we need to get the client shared memory interface name from the front + char *ip_list = buf; + char *smi_name = strsep(&ip_list, " "); + char *command = NULL; + if (smi_name != NULL) { + debug(1, "SMI Name: \"%s\"", smi_name); + int client_id = 0; + if (ip_list != NULL) + command = strsep(&ip_list, " "); + if ((command == NULL) || ((strcmp(command, "T") == 0) && (ip_list == NULL))) { + // clear all the flags, but only if the client exists + client_id = find_client_id(smi_name); // don't create a record + if (client_id != -1) { + // turn off all is_timing_peer flags + int i; + for (i = 0; i < MAX_CLOCKS; i++) { + // e.g. (obsolete) + clock_private_info[i].flags &= ~(1 << clock_is_master); + clock_private_info[i].mastership_start_time = 0; + clock_private_info[i].previous_offset_time = 0; + + // if a clock would now stop being a master everywhere + // it should drop mastership history and do a sync when it becomes master again + if ((clock_private_info[i].client_flags[client_id] & (1 << clock_is_master)) != + 0) { // if clock[i] is master for this client's timing group + int c; + int this_clock_is_master_elsewhere = 0; + for (c = 0; c < MAX_CLIENTS; c++) { + if ((c != client_id) && + ((clock_private_info[i].client_flags[c] & (1 << clock_is_master)) != 0)) + this_clock_is_master_elsewhere = 1; + } + if (this_clock_is_master_elsewhere == 0) { + clock_private_info[i].mastership_start_time = 0; + clock_private_info[i].previous_offset_time = 0; + } + } + clock_private_info[i].client_flags[client_id] = 0; + } } - } + } else { + client_id = get_client_id(smi_name); // create the record if it doesn't exist + if (client_id != -1) { + if (strcmp(command, "T") == 0) { + // turn off all is_timing_peer flags + int i; + for (i = 0; i < MAX_CLOCKS; i++) { + clock_private_info[i].flags &= + ~(1 << clock_is_a_timing_peer); // turn off peer flag (but not the master flag!) + clock_private_info[i].client_flags[client_id] &= + ~(1 << clock_is_a_timing_peer); // turn off peer flag (but not the master flag!) + clock_private_info[i].announcements_without_followups = + 0; // to allow a possibly silent clock to be revisited when added to a timing + // peer list + } + while (ip_list != NULL) { + char *new_ip = strsep(&ip_list, " "); + // look for the IP in the list of clocks, and create an inert entry if not there + if ((new_ip != NULL) && (new_ip[0] != 0)) { + int t = find_clock_source_record(new_ip, clock_private_info); + if (t == -1) + t = create_clock_source_record(new_ip, clock_private_info); + if (t != -1) { // if the clock table is not full, show it's a timing peer + clock_private_info[t].flags |= (1 << clock_is_a_timing_peer); + clock_private_info[t].client_flags[client_id] |= (1 << clock_is_a_timing_peer); + } + // otherwise, drop it + } + } - // now find and mark the best clock in the timing peer list as the master - update_master(); + // now find and mark the best clock in the timing peer list as the master + update_master(client_id); - debug(2, "Timing group start"); - for (i = 0; i < MAX_CLOCKS; i++) { - if ((clock_private_info[i].flags & (1 << clock_is_a_timing_peer)) != 0) - debug(2, "%s.", &clock_private_info[i].ip); + debug(2, "Timing group start"); + for (i = 0; i < MAX_CLOCKS; i++) { + if ((clock_private_info[i].client_flags[client_id] & (1 << clock_is_a_timing_peer)) != + 0) + debug(2, "%s.", &clock_private_info[i].ip); + } + debug(2, "Timing group end"); + } else { + warn("Unrecognised string on the control port."); + } + } else { + warn("Could not find or create a record for SMI Interface \"%s\".", smi_name); + } } - debug(2, "Timing group end"); } else { - warn("Unrecognised string on the control port."); + warn("SMI Interface Name not found on the control port."); } } else { warn("Bad packet on the control port."); @@ -81,8 +127,8 @@ void handle_control_port_messages(char *buf, ssize_t recv_len, void handle_announce(char *buf, ssize_t recv_len, clock_source_private_data *clock_private_info, uint64_t reception_time) { - // reject Announce messages from self - if (clock_private_info->is_one_of_ours == 0) { + // only process Announce messages that do not come from self + if ((clock_private_info->flags & (1 << clock_is_one_of_ours)) == 0) { // debug_print_buffer(1, buf, (size_t) recv_len); // make way for the new time if ((size_t)recv_len >= sizeof(struct ptp_announce_message)) { @@ -220,7 +266,7 @@ void handle_announce(char *buf, ssize_t recv_len, clock_source_private_data *clo clock_private_info->flags &= ~(1 << clock_is_qualified); else clock_private_info->flags |= (1 << clock_is_qualified); - update_master(); + update_master(0); // TODO -- use client_id here } } else { if ((clock_private_info->flags & (1 << clock_is_qualified)) != @@ -285,7 +331,6 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len, time_since_previous_offset = reception_time - clock_private_info->previous_offset_time; } - if ((clock_private_info->flags & (1 << clock_is_becoming_master)) != 0) { // we definitely have at least one sample since the request was made to // designate it a master, so we assume it is legitimate. That is, we assume @@ -377,7 +422,8 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len, clock_private_info->flags |= 1 << clock_is_master; clock_private_info->previous_offset_time = 0; debug_log_nqptp_status(2); - } else if ((clock_private_info->previous_offset_time != 0) && (time_since_previous_offset < 300000000000)) { + } else if ((clock_private_info->previous_offset_time != 0) && + (time_since_previous_offset < 300000000000)) { // i.e. if it's not becoming a master and there has been a previous follow_up int64_t time_since_last_sync = reception_time - clock_private_info->last_sync_time; int64_t sync_timeout = 300000000000; // nanoseconds @@ -407,12 +453,12 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len, // we take any positive or a limited negative jitter as a sync event if (jitter < 0) { if (clock_private_info->follow_up_number < - (5 * 8)) // at the beginning (8 samples per second) + (5 * 8)) // at the beginning (8 samples per second) offset = clock_private_info->previous_offset + jitter / 16; else offset = clock_private_info->previous_offset + jitter / 64; } else if (clock_private_info->follow_up_number < - (5 * 8)) // at the beginning (8 samples per second) + (5 * 8)) // at the beginning (8 samples per second) offset = clock_private_info->previous_offset + jitter / 1; // accept positive changes quickly else @@ -427,14 +473,17 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len, clock_private_info->ip); // leave the offset as it was coming in and take it as a sync time clock_private_info->last_sync_time = reception_time; - clock_private_info->mastership_start_time = reception_time; // mastership is reset to this time... + clock_private_info->mastership_start_time = + reception_time; // mastership is reset to this time... clock_private_info->previous_offset_time = 0; } } else { clock_private_info->last_sync_time = reception_time; if (time_since_previous_offset >= 300000000000) { - debug(1,"Long interval: %f seconds since previous follow_up", time_since_previous_offset * 1E-9); - clock_private_info->mastership_start_time = reception_time; // mastership is reset to this time... + debug(1, "Long interval: %f seconds since previous follow_up", + time_since_previous_offset * 1E-9); + clock_private_info->mastership_start_time = + reception_time; // mastership is reset to this time... clock_private_info->previous_offset_time = 0; } } @@ -454,6 +503,6 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len, clock_private_info->ip); clock_private_info->flags |= (1 << clock_is_valid); // valid because it has at least one follow_up - update_master(); + update_master(0); // TODO } } diff --git a/nqptp-message-handlers.h b/nqptp-message-handlers.h index fee9403..c89b359 100644 --- a/nqptp-message-handlers.h +++ b/nqptp-message-handlers.h @@ -20,9 +20,9 @@ #ifndef NQPTP_MESSAGE_HANDLERS_H #define NQPTP_MESSAGE_HANDLERS_H -#include #include "general-utilities.h" #include "nqptp-clock-sources.h" +#include void handle_announce(char *buf, ssize_t recv_len, clock_source_private_data *clock_private_info, uint64_t reception_time); diff --git a/nqptp-shm-structures.h b/nqptp-shm-structures.h index f5edbd4..7f2b2b8 100644 --- a/nqptp-shm-structures.h +++ b/nqptp-shm-structures.h @@ -20,16 +20,18 @@ #ifndef NQPTP_SHM_STRUCTURES_H #define NQPTP_SHM_STRUCTURES_H -#define STORAGE_ID "/nqptp" -#define MAX_CLOCKS 64 -#define NQPTP_SHM_STRUCTURES_VERSION 6 +#define NQPTP_SHM_STRUCTURES_VERSION 7 #define NQPTP_CONTROL_PORT 9000 -// the control port will accept a UDP packet with the first letter being: -// "T", followed by a space and then a space-delimited -// list of ip numbers, either IPv4 or IPv6 +// The control port will accept a UDP packet with the first letter being +// "T", followed by the name of the shared memory interface, which should be of +// the form nqptp-. This can be followed by nothing or by +// a space and then a space-delimited list of ip numbers, either IPv4 or IPv6 // the whole not to exceed 4096 characters in total -// The IPs will become the new list of timing peers, replacing any previous +// The IPs, if provided, will become the new list of timing peers, clearing or replacing any +// previous list. If the master clock of the new list is the same as that of the old list, it is +// retained without having to resynchronise. This means that non-master devices can be added and +// removed without disturbing the existing-and-continuing master clock. #include #include @@ -37,7 +39,6 @@ struct shm_structure { pthread_mutex_t shm_mutex; // for safely accessing the structure uint16_t version; // check this is equal to NQPTP_SHM_STRUCTURES_VERSION - uint32_t flags; // unused uint64_t master_clock_id; // the current master clock char master_clock_ip[64]; // where it's coming from uint64_t local_time; // the time when the offset was calculated diff --git a/nqptp.c b/nqptp.c index 062520b..e58280c 100644 --- a/nqptp.c +++ b/nqptp.c @@ -63,8 +63,6 @@ sockets_open_bundle sockets_open_stuff; -int master_clock_index = -1; - typedef struct { uint64_t trigger_time; uint64_t (*task)(uint64_t nominal_call_time, void *private_data); @@ -90,7 +88,7 @@ int epoll_fd; void update_master_clock_info(uint64_t master_clock_id, const char *ip, uint64_t local_time, uint64_t local_to_master_offset, uint64_t mastership_start_time) { - //debug(1,"update_master_clock_info start"); + // debug(1,"update_master_clock_info start"); if (shared_memory->master_clock_id != master_clock_id) debug_log_nqptp_status(1); int rc = pthread_mutex_lock(&shared_memory->shm_mutex); @@ -112,7 +110,7 @@ void update_master_clock_info(uint64_t master_clock_id, const char *ip, uint64_t rc = pthread_mutex_unlock(&shared_memory->shm_mutex); if (rc != 0) warn("Can't release mutex after updating master clock!"); - //debug(1,"update_master_clock_info done"); + // debug(1,"update_master_clock_info done"); } void goodbye(void) { @@ -120,6 +118,8 @@ void goodbye(void) { unsigned int i; for (i = 0; i < sockets_open_stuff.sockets_open; i++) close(sockets_open_stuff.sockets[i].number); + // close off shared memory interfaces + if (shared_memory != NULL) { // mmap cleanup if (munmap(shared_memory, sizeof(struct shm_structure)) != 0) @@ -128,6 +128,9 @@ void goodbye(void) { if (shm_unlink(STORAGE_ID) == -1) debug(1, "error unlinking shared memory \"%s\"", STORAGE_ID); } + + delete_clients(); + if (epoll_fd != -1) close(epoll_fd); @@ -431,7 +434,7 @@ uint64_t broadcasting_task(uint64_t call_time, __attribute__((unused)) void *pri int i; for (i = 0; i < MAX_CLOCKS; i++) { if ((clocks_private[i].announcements_without_followups == 3) && - (clocks_private[i].is_one_of_ours == 0)) { + ((clocks_private[i].flags & (1 << clock_is_one_of_ours)) == 0)) { debug(1, "Found a silent clock %" PRIx64 " at %s.", clocks_private[i].clock_id, clocks_private[i].ip); // send an Announce message to attempt to waken this silent PTP clock by @@ -532,12 +535,12 @@ uint64_t broadcasting_task(uint64_t call_time, __attribute__((unused)) void *pri } } -/* - uint64_t announce_interval = 1; - announce_interval = announce_interval << (8 + aPTPinitialLogAnnounceInterval); - announce_interval = announce_interval * 1000000000; - announce_interval = announce_interval >> 8; // nanoseconds - return call_time + announce_interval; -*/ + /* + uint64_t announce_interval = 1; + announce_interval = announce_interval << (8 + aPTPinitialLogAnnounceInterval); + announce_interval = announce_interval * 1000000000; + announce_interval = announce_interval >> 8; // nanoseconds + return call_time + announce_interval; + */ return call_time + 50000000; } diff --git a/nqptp.h b/nqptp.h index 327aba9..f86c968 100644 --- a/nqptp.h +++ b/nqptp.h @@ -25,14 +25,20 @@ #include "nqptp-shm-structures.h" +#define STORAGE_ID "/nqptp" +#define MAX_CLOCKS 64 +#define MAX_CLIENTS 16 #define MAX_OPEN_SOCKETS 16 // When a new timing peer group is created, one of the clocks in the -// group becomes the master and its native time becomes the "master time". +// group may become the master and its native time becomes the "master time". // This is what is provided to the client. -extern int master_clock_index; -extern struct shm_structure *shared_memory; +// An NQPTP client interface communicates through a shared memory interface named by the +// shm_interface_name It provides the shm_interface_name at the start of every control message it +// sends through port 9000. Following the name, the client can specify the members -- the "PTP +// Instances" -- of a "PTP Network" it wishes to monitor. This is a "timing group" in AirPlay 2 +// parlance, it seems. void update_master_clock_info(uint64_t master_clock_id, const char *ip, uint64_t local_time, uint64_t local_to_master_offset, uint64_t mastership_start_time);