gator: Version 5.21.1
[firefly-linux-kernel-4.4.55.git] / tools / gator / daemon / ExternalSource.cpp
1 /**
2  * Copyright (C) ARM Limited 2010-2015. All rights reserved.
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
9 #include "ExternalSource.h"
10
11 #include <fcntl.h>
12 #include <sys/prctl.h>
13 #include <unistd.h>
14
15 #include "Child.h"
16 #include "Logging.h"
17 #include "OlySocket.h"
18 #include "SessionData.h"
19
20 extern Child *child;
21
22 static const char STREAMLINE_ANNOTATE[] = "\0streamline-annotate";
23 static const char MALI_VIDEO[] = "\0mali-video";
24 static const char MALI_VIDEO_STARTUP[] = "\0mali-video-startup";
25 static const char MALI_VIDEO_V1[] = "MALI_VIDEO 1\n";
26 static const char MALI_GRAPHICS[] = "\0mali_thirdparty_server";
27 static const char MALI_GRAPHICS_STARTUP[] = "\0mali_thirdparty_client";
28 static const char MALI_GRAPHICS_V1[] = "MALI_GRAPHICS 1\n";
29
30 static bool setNonblock(const int fd) {
31         int flags;
32
33         flags = fcntl(fd, F_GETFL);
34         if (flags < 0) {
35                 logg->logMessage("fcntl getfl failed");
36                 return false;
37         }
38
39         if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
40                 logg->logMessage("fcntl setfl failed");
41                 return false;
42         }
43
44         return true;
45 }
46
47 ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 128*1024, senderSem), mMonitor(), mMveStartupUds(MALI_VIDEO_STARTUP, sizeof(MALI_VIDEO_STARTUP)), mMaliStartupUds(MALI_GRAPHICS_STARTUP, sizeof(MALI_GRAPHICS_STARTUP)), mAnnotate(8083), mAnnotateUds(STREAMLINE_ANNOTATE, sizeof(STREAMLINE_ANNOTATE), true), mInterruptFd(-1), mMaliUds(-1), mMveUds(-1) {
48         sem_init(&mBufferSem, 0, 0);
49 }
50
51 ExternalSource::~ExternalSource() {
52 }
53
54 void ExternalSource::waitFor(const int bytes) {
55         while (mBuffer.bytesAvailable() <= bytes) {
56                 if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
57                         logg->logMessage("One shot (external)");
58                         child->endSession();
59                 }
60                 sem_wait(&mBufferSem);
61         }
62 }
63
64 void ExternalSource::configureConnection(const int fd, const char *const handshake, size_t size) {
65         if (!setNonblock(fd)) {
66                 logg->logError("Unable to set nonblock on fh");
67                 handleException();
68         }
69
70         if (!mMonitor.add(fd)) {
71                 logg->logError("Unable to add fh to monitor");
72                 handleException();
73         }
74
75         // Write the handshake to the circular buffer
76         waitFor(Buffer::MAXSIZE_PACK32 + size - 1);
77         mBuffer.packInt(fd);
78         mBuffer.writeBytes(handshake, size - 1);
79         mBuffer.commit(1, true);
80 }
81
82 bool ExternalSource::connectMali() {
83         mMaliUds = OlySocket::connect(MALI_GRAPHICS, sizeof(MALI_GRAPHICS));
84         if (mMaliUds < 0) {
85                 return false;
86         }
87
88         configureConnection(mMaliUds, MALI_GRAPHICS_V1, sizeof(MALI_GRAPHICS_V1));
89
90         return true;
91 }
92
93 bool ExternalSource::connectMve() {
94         if (!gSessionData->maliVideo.countersEnabled()) {
95                 return true;
96         }
97
98         mMveUds = OlySocket::connect(MALI_VIDEO, sizeof(MALI_VIDEO));
99         if (mMveUds < 0) {
100                 return false;
101         }
102
103         if (!gSessionData->maliVideo.start(mMveUds)) {
104                 return false;
105         }
106
107         configureConnection(mMveUds, MALI_VIDEO_V1, sizeof(MALI_VIDEO_V1));
108
109         return true;
110 }
111
112 bool ExternalSource::prepare() {
113         if (!mMonitor.init() ||
114                         !setNonblock(mMveStartupUds.getFd()) || !mMonitor.add(mMveStartupUds.getFd()) ||
115                         !setNonblock(mMaliStartupUds.getFd()) || !mMonitor.add(mMaliStartupUds.getFd()) ||
116                         !setNonblock(mAnnotate.getFd()) || !mMonitor.add(mAnnotate.getFd()) ||
117                         !setNonblock(mAnnotateUds.getFd()) || !mMonitor.add(mAnnotateUds.getFd()) ||
118                         false) {
119                 return false;
120         }
121
122         connectMali();
123         connectMve();
124
125         return true;
126 }
127
128 void ExternalSource::run() {
129         int pipefd[2];
130
131         prctl(PR_SET_NAME, (unsigned long)&"gatord-external", 0, 0, 0);
132
133         if (pipe_cloexec(pipefd) != 0) {
134                 logg->logError("pipe failed");
135                 handleException();
136         }
137         mInterruptFd = pipefd[1];
138
139         if (!mMonitor.add(pipefd[0])) {
140                 logg->logError("Monitor::add failed");
141                 handleException();
142         }
143
144         // Notify annotate clients to retry connecting to gatord
145         uint64_t val = 1;
146         if (::write(gSessionData->mAnnotateStart, &val, sizeof(val)) != sizeof(val)) {
147                 logg->logMessage("Writing to annotate pipe failed");
148         }
149
150         while (gSessionData->mSessionIsActive) {
151                 struct epoll_event events[16];
152                 // Clear any pending sem posts
153                 while (sem_trywait(&mBufferSem) == 0);
154                 int ready = mMonitor.wait(events, ARRAY_LENGTH(events), -1);
155                 if (ready < 0) {
156                         logg->logError("Monitor::wait failed");
157                         handleException();
158                 }
159
160                 const uint64_t currTime = getTime() - gSessionData->mMonotonicStarted;
161
162                 for (int i = 0; i < ready; ++i) {
163                         const int fd = events[i].data.fd;
164                         if (fd == mMveStartupUds.getFd()) {
165                                 // Mali Video Engine says it's alive
166                                 int client = mMveStartupUds.acceptConnection();
167                                 // Don't read from this connection, establish a new connection to Mali-V500
168                                 close(client);
169                                 if (!connectMve()) {
170                                         logg->logError("Unable to configure incoming Mali video connection");
171                                         handleException();
172                                 }
173                         } else if (fd == mMaliStartupUds.getFd()) {
174                                 // Mali Graphics says it's alive
175                                 int client = mMaliStartupUds.acceptConnection();
176                                 // Don't read from this connection, establish a new connection to Mali Graphics
177                                 close(client);
178                                 if (!connectMali()) {
179                                         logg->logError("Unable to configure incoming Mali graphics connection");
180                                         handleException();
181                                 }
182                         } else if (fd == mAnnotate.getFd()) {
183                                 int client = mAnnotate.acceptConnection();
184                                 if (!setNonblock(client) || !mMonitor.add(client)) {
185                                         logg->logError("Unable to set socket options on incoming annotation connection");
186                                         handleException();
187                                 }
188                         } else if (fd == mAnnotateUds.getFd()) {
189                                 int client = mAnnotateUds.acceptConnection();
190                                 if (!setNonblock(client) || !mMonitor.add(client)) {
191                                         logg->logError("Unable to set socket options on incoming annotation connection");
192                                         handleException();
193                                 }
194                         } else if (fd == pipefd[0]) {
195                                 // Means interrupt has been called and mSessionIsActive should be reread
196                         } else {
197                                 /* This can result in some starvation if there are multiple
198                                  * threads which are annotating heavily, but it is not
199                                  * recommended that threads annotate that much as it can also
200                                  * starve out the gator data.
201                                  */
202                                 while (gSessionData->mSessionIsActive) {
203                                         // Wait until there is enough room for the fd, two headers and two ints
204                                         waitFor(7*Buffer::MAXSIZE_PACK32 + 2*sizeof(uint32_t));
205                                         mBuffer.packInt(fd);
206                                         const int contiguous = mBuffer.contiguousSpaceAvailable();
207                                         const int bytes = read(fd, mBuffer.getWritePos(), contiguous);
208                                         if (bytes < 0) {
209                                                 if (errno == EAGAIN) {
210                                                         // Nothing left to read
211                                                         mBuffer.commit(currTime, true);
212                                                         break;
213                                                 }
214                                                 // Something else failed, close the socket
215                                                 mBuffer.commit(currTime, true);
216                                                 mBuffer.packInt(-1);
217                                                 mBuffer.packInt(fd);
218                                                 // Here and other commits, always force-flush the buffer as this frame don't work like others
219                                                 mBuffer.commit(currTime, true);
220                                                 close(fd);
221                                                 break;
222                                         } else if (bytes == 0) {
223                                                 // The other side is closed
224                                                 mBuffer.commit(currTime, true);
225                                                 mBuffer.packInt(-1);
226                                                 mBuffer.packInt(fd);
227                                                 mBuffer.commit(currTime, true);
228                                                 close(fd);
229                                                 break;
230                                         }
231
232                                         mBuffer.advanceWrite(bytes);
233                                         mBuffer.commit(currTime, true);
234
235                                         // Short reads also mean nothing is left to read
236                                         if (bytes < contiguous) {
237                                                 break;
238                                         }
239                                 }
240                         }
241                 }
242         }
243
244         mBuffer.setDone();
245
246         if (mMveUds >= 0) {
247                 gSessionData->maliVideo.stop(mMveUds);
248         }
249
250         mInterruptFd = -1;
251         close(pipefd[0]);
252         close(pipefd[1]);
253 }
254
255 void ExternalSource::interrupt() {
256         if (mInterruptFd >= 0) {
257                 int8_t c = 0;
258                 // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread
259                 if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) {
260                         logg->logError("write failed");
261                         handleException();
262                 }
263         }
264 }
265
266 bool ExternalSource::isDone() {
267         return mBuffer.isDone();
268 }
269
270 void ExternalSource::write(Sender *sender) {
271         // Don't send external data until the summary packet is sent so that monotonic delta is available
272         if (!gSessionData->mSentSummary) {
273                 return;
274         }
275         if (!mBuffer.isDone()) {
276                 mBuffer.write(sender);
277                 sem_post(&mBufferSem);
278         }
279 }