Home | History | Annotate | Download | only in common_time
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "common_time"
     18 #include <utils/Log.h>
     19 
     20 #include <fcntl.h>
     21 #include <linux/in.h>
     22 #include <linux/tcp.h>
     23 #include <poll.h>
     24 #include <sys/socket.h>
     25 #include <sys/types.h>
     26 #include <unistd.h>
     27 #include <utils/Errors.h>
     28 #include <utils/misc.h>
     29 
     30 #include <common_time/local_clock.h>
     31 
     32 #include "common_clock.h"
     33 #include "diag_thread.h"
     34 
     35 #define kMaxEvents 16
     36 #define kListenPort 9876
     37 
     38 static bool setNonblocking(int fd) {
     39     int flags = fcntl(fd, F_GETFL);
     40     if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
     41         ALOGE("Failed to set socket (%d) to non-blocking mode (errno %d)",
     42              fd, errno);
     43         return false;
     44     }
     45 
     46     return true;
     47 }
     48 
     49 static bool setNodelay(int fd) {
     50     int tmp = 1;
     51     if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &tmp, sizeof(tmp)) < 0) {
     52         ALOGE("Failed to set socket (%d) to no-delay mode (errno %d)",
     53              fd, errno);
     54         return false;
     55     }
     56 
     57     return true;
     58 }
     59 
     60 namespace android {
     61 
     62 DiagThread::DiagThread(CommonClock* common_clock, LocalClock* local_clock) {
     63     common_clock_ = common_clock;
     64     local_clock_ = local_clock;
     65     listen_fd_ = -1;
     66     data_fd_ = -1;
     67     kernel_logID_basis_known_ = false;
     68     discipline_log_ID_ = 0;
     69 }
     70 
     71 DiagThread::~DiagThread() {
     72 }
     73 
     74 status_t DiagThread::startWorkThread() {
     75     status_t res;
     76     stopWorkThread();
     77     res = run("Diag");
     78 
     79     if (res != OK)
     80         ALOGE("Failed to start work thread (res = %d)", res);
     81 
     82     return res;
     83 }
     84 
     85 void DiagThread::stopWorkThread() {
     86     status_t res;
     87     res = requestExitAndWait(); // block until thread exit.
     88     if (res != OK)
     89         ALOGE("Failed to stop work thread (res = %d)", res);
     90 }
     91 
     92 bool DiagThread::openListenSocket() {
     93     bool ret = false;
     94     int flags;
     95     cleanupListenSocket();
     96 
     97     if ((listen_fd_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
     98         ALOGE("Socket failed.");
     99         goto bailout;
    100     }
    101 
    102     // Set non-blocking operation
    103     if (!setNonblocking(listen_fd_))
    104         goto bailout;
    105 
    106     struct sockaddr_in addr;
    107     memset(&addr, 0, sizeof(addr));
    108     addr.sin_family = AF_INET;
    109     addr.sin_addr.s_addr = INADDR_ANY;
    110     addr.sin_port = htons(kListenPort);
    111 
    112     if (bind(listen_fd_, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
    113         ALOGE("Bind failed.");
    114         goto bailout;
    115     }
    116 
    117     if (listen(listen_fd_, 1) < 0) {
    118         ALOGE("Listen failed.");
    119         goto bailout;
    120     }
    121 
    122     ret = true;
    123 bailout:
    124     if (!ret)
    125         cleanupListenSocket();
    126 
    127     return ret;
    128 }
    129 
    130 void DiagThread::cleanupListenSocket() {
    131     if (listen_fd_ >= 0) {
    132         int res;
    133 
    134         struct linger l;
    135         l.l_onoff  = 1;
    136         l.l_linger = 0;
    137 
    138         setsockopt(listen_fd_, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
    139         shutdown(listen_fd_, SHUT_RDWR);
    140         close(listen_fd_);
    141         listen_fd_ = -1;
    142     }
    143 }
    144 
    145 void DiagThread::cleanupDataSocket() {
    146     if (data_fd_ >= 0) {
    147         int res;
    148 
    149         struct linger l;
    150         l.l_onoff  = 1;
    151         l.l_linger = 0;
    152 
    153         setsockopt(data_fd_, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
    154         shutdown(data_fd_, SHUT_RDWR);
    155         close(data_fd_);
    156         data_fd_ = -1;
    157     }
    158 }
    159 
    160 void DiagThread::resetLogIDs() {
    161     // Drain and discard all of the events from the kernel
    162     struct local_time_debug_event events[kMaxEvents];
    163     while(local_clock_->getDebugLog(events, kMaxEvents) > 0)
    164         ;
    165 
    166     {
    167         Mutex::Autolock lock(&discipline_log_lock_);
    168         discipline_log_.clear();
    169         discipline_log_ID_ = 0;
    170     }
    171 
    172     kernel_logID_basis_known_ = false;
    173 }
    174 
    175 void DiagThread::pushDisciplineEvent(int64_t observed_local_time,
    176                                      int64_t observed_common_time,
    177                                      int64_t nominal_common_time,
    178                                      int32_t total_correction,
    179                                      int32_t rtt) {
    180     Mutex::Autolock lock(&discipline_log_lock_);
    181 
    182     DisciplineEventRecord evt;
    183 
    184     evt.event_id = discipline_log_ID_++;
    185 
    186     evt.action_local_time = local_clock_->getLocalTime();
    187     common_clock_->localToCommon(evt.action_local_time,
    188             &evt.action_common_time);
    189 
    190     evt.observed_local_time  = observed_local_time;
    191     evt.observed_common_time = observed_common_time;
    192     evt.nominal_common_time  = nominal_common_time;
    193     evt.total_correction     = total_correction;
    194     evt.rtt                  = rtt;
    195 
    196     discipline_log_.push_back(evt);
    197     while (discipline_log_.size() > kMaxDisciplineLogSize)
    198         discipline_log_.erase(discipline_log_.begin());
    199 }
    200 
    201 bool DiagThread::threadLoop() {
    202     struct pollfd poll_fds[1];
    203 
    204     if (!openListenSocket()) {
    205         ALOGE("Failed to open listen socket");
    206         goto bailout;
    207     }
    208 
    209     while (!exitPending()) {
    210         memset(&poll_fds, 0, sizeof(poll_fds));
    211 
    212         if (data_fd_ < 0) {
    213             poll_fds[0].fd     = listen_fd_;
    214             poll_fds[0].events = POLLIN;
    215         } else {
    216             poll_fds[0].fd     = data_fd_;
    217             poll_fds[0].events = POLLRDHUP | POLLIN;
    218         }
    219 
    220         int poll_res = poll(poll_fds, NELEM(poll_fds), 50);
    221         if (poll_res < 0) {
    222             ALOGE("Fatal error (%d,%d) while waiting on events",
    223                  poll_res, errno);
    224             goto bailout;
    225         }
    226 
    227         if (exitPending())
    228             break;
    229 
    230         if (poll_fds[0].revents) {
    231             if (poll_fds[0].fd == listen_fd_) {
    232                 data_fd_ = accept(listen_fd_, NULL, NULL);
    233 
    234                 if (data_fd_ < 0) {
    235                     ALOGW("Failed accept on socket %d with err %d",
    236                          listen_fd_, errno);
    237                 } else {
    238                     if (!setNonblocking(data_fd_))
    239                         cleanupDataSocket();
    240                     if (!setNodelay(data_fd_))
    241                         cleanupDataSocket();
    242                 }
    243             } else
    244                 if (poll_fds[0].fd == data_fd_) {
    245                     if (poll_fds[0].revents & POLLRDHUP) {
    246                         // Connection hung up; time to clean up.
    247                         cleanupDataSocket();
    248                     } else
    249                         if (poll_fds[0].revents & POLLIN) {
    250                             uint8_t cmd;
    251                             if (read(data_fd_, &cmd, sizeof(cmd)) > 0) {
    252                                 switch(cmd) {
    253                                     case 'r':
    254                                     case 'R':
    255                                         resetLogIDs();
    256                                         break;
    257                                 }
    258                             }
    259                         }
    260                 }
    261         }
    262 
    263         struct local_time_debug_event events[kMaxEvents];
    264         int amt = local_clock_->getDebugLog(events, kMaxEvents);
    265 
    266         if (amt > 0) {
    267             for (int i = 0; i < amt; i++) {
    268                 struct local_time_debug_event& e = events[i];
    269 
    270                 if (!kernel_logID_basis_known_) {
    271                     kernel_logID_basis_ = e.local_timesync_event_id;
    272                     kernel_logID_basis_known_ = true;
    273                 }
    274 
    275                 char buf[1024];
    276                 int64_t common_time;
    277                 status_t res = common_clock_->localToCommon(e.local_time,
    278                                                             &common_time);
    279                 snprintf(buf, sizeof(buf), "E,%lld,%lld,%lld,%d\n",
    280                          e.local_timesync_event_id - kernel_logID_basis_,
    281                          e.local_time,
    282                          common_time,
    283                          (OK == res) ? 1 : 0);
    284                 buf[sizeof(buf) - 1] = 0;
    285 
    286                 if (data_fd_ >= 0)
    287                     write(data_fd_, buf, strlen(buf));
    288             }
    289         }
    290 
    291         { // scope for autolock pattern
    292             Mutex::Autolock lock(&discipline_log_lock_);
    293 
    294             while (discipline_log_.size() > 0) {
    295                 char buf[1024];
    296                 DisciplineEventRecord& e = *discipline_log_.begin();
    297                 snprintf(buf, sizeof(buf),
    298                          "D,%lld,%lld,%lld,%lld,%lld,%lld,%d,%d\n",
    299                          e.event_id,
    300                          e.action_local_time,
    301                          e.action_common_time,
    302                          e.observed_local_time,
    303                          e.observed_common_time,
    304                          e.nominal_common_time,
    305                          e.total_correction,
    306                          e.rtt);
    307                 buf[sizeof(buf) - 1] = 0;
    308 
    309                 if (data_fd_ >= 0)
    310                     write(data_fd_, buf, strlen(buf));
    311 
    312                 discipline_log_.erase(discipline_log_.begin());
    313             }
    314         }
    315     }
    316 
    317 bailout:
    318     cleanupDataSocket();
    319     cleanupListenSocket();
    320     return false;
    321 }
    322 
    323 }  // namespace android
    324