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