1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #ifndef BLOCKAWARE_H
16 #define BLOCKAWARE_H
17 
18 #include <securec.h>
19 #include <cstdio>
20 #include <cstring>
21 #include <sys/prctl.h>
22 #include <cerrno>
23 
24 #ifdef __cplusplus
25 extern "C" {
26 #endif
27 
28 #define BLOCKAWARE_DOMAIN_ID_MAX    15
29 #define HM_PR_SILK_BLOCKAWARE_OPS   0x534b4241
30 #define BLOCKAWARE_SUBOPS_INIT      0x1
31 #define BLOCKAWARE_SUBOPS_REG       0x2
32 #define BLOCKAWARE_SUBOPS_UNREG     0x3
33 #define BLOCKAWARE_SUBOPS_WAIT      0x4
34 #define BLOCKAWARE_SUBOPS_WAKE      0x5
35 #define BLOCKAWARE_SUBOPS_MONITORFD 0x6
36 
37 struct BlockawareDomainInfo {
38     unsigned int nrRunning;
39     unsigned int nrSleeping;
40     unsigned int nrBlocked;
41 };
42 struct BlockawareDomainInfoArea {
43     struct BlockawareDomainInfo localinfo[BLOCKAWARE_DOMAIN_ID_MAX + 1];
44     struct BlockawareDomainInfo globalinfo;
45 };
46 struct BlockawareWatermark {
47     unsigned int low;
48     unsigned int high;
49 };
50 struct BlockawareWakeupCond {
51     struct BlockawareWatermark local[BLOCKAWARE_DOMAIN_ID_MAX + 1];
52     struct BlockawareWatermark global;
53     bool check_ahead;
54 };
55 struct BlockawareKinfoPageS {
56     uint32_t seq;
57     struct BlockawareDomainInfoArea infoArea;
58 };
59 
60 static inline int BlockawareInit(unsigned long *keyPtr);
61 static inline int BlockawareRegister(unsigned int domain);
62 static inline int BlockawareUnregister(void);
63 static inline int BlockawareLoadSnapshot(unsigned long key, struct BlockawareDomainInfoArea *infoArea);
64 static inline int BlockawareEnterSleeping(void);
65 static inline int BlockawareLeaveSleeping(void);
66 static inline int BlockawareWaitCond(struct BlockawareWakeupCond *cond);
67 static inline int BlockawareWake(void);
68 static inline int BlockawareMonitorfd(int fd, struct BlockawareWakeupCond *cond);
69 
70 #ifdef __aarch64__
CpuRelax(void)71 static inline void CpuRelax(void)
72 {
73     asm volatile("yield" ::: "memory");
74 }
75 
SmpRmb(void)76 static inline void SmpRmb(void)
77 {
78     asm volatile("dmb ishld" ::: "memory");
79 }
80 
GetTlsPtr(void)81 static inline unsigned long GetTlsPtr(void)
82 {
83     unsigned long tls = 0;
84     asm volatile ("mrs %0, tpidr_el0\n" : "=r" (tls));
85     return tls;
86 }
87 
curr_thread_tls_blockaware_slot_of(void)88 static inline unsigned long *curr_thread_tls_blockaware_slot_of(void)
89 {
90     unsigned long tls = GetTlsPtr();
91     unsigned long slot_addr = tls - sizeof (unsigned long) * (2UL + 5UL);
92     return reinterpret_cast<unsigned long *>(slot_addr);
93 }
94 
BlockawareEnterSleeping(void)95 static inline int BlockawareEnterSleeping(void)
96 {
97     unsigned long *slot_ptr = curr_thread_tls_blockaware_slot_of();
98     *slot_ptr += 1;
99     return 0;
100 }
101 
BlockawareLeaveSleeping(void)102 static inline int BlockawareLeaveSleeping(void)
103 {
104     unsigned long *slot_ptr = curr_thread_tls_blockaware_slot_of();
105     int err = 0;
106 
107     if (*slot_ptr == 0) {
108         err = -EINVAL;
109     } else {
110         *slot_ptr -= 1;
111     }
112 
113     return err;
114 }
115 #elif defined(__arm__)
116 
CpuRelax(void)117 static inline void CpuRelax(void)
118 {
119     asm volatile("yield" ::: "memory");
120 }
121 
SmpRmb(void)122 static inline void SmpRmb(void)
123 {
124     asm volatile("dmb ish" ::: "memory");
125 }
126 
GetTlsPtr(void)127 static inline unsigned long GetTlsPtr(void)
128 {
129     unsigned long tpid = 0;
130     asm volatile("mrc p15, 0, %0, c13, c0, 3" : "=r"(tpid));
131     return tpid;
132 }
133 
curr_thread_tls_blockaware_slot_of(void)134 static inline unsigned long *curr_thread_tls_blockaware_slot_of(void)
135 {
136     unsigned long tls = GetTlsPtr();
137     unsigned long slot_addr = tls - sizeof (unsigned long) * (2UL + 5UL);
138     return (unsigned long *)slot_addr;
139 }
140 
BlockawareEnterSleeping(void)141 static inline int BlockawareEnterSleeping(void)
142 {
143     unsigned long *slot_ptr = curr_thread_tls_blockaware_slot_of();
144     *slot_ptr += 1;
145     return 0;
146 }
147 
BlockawareLeaveSleeping(void)148 static inline int BlockawareLeaveSleeping(void)
149 {
150     unsigned long *slot_ptr = curr_thread_tls_blockaware_slot_of();
151     int err = 0;
152 
153     if (*slot_ptr == 0) {
154         err = -EINVAL;
155     } else {
156         *slot_ptr -= 1;
157     }
158 
159     return err;
160 }
161 #else
CpuRelax(void)162 static inline void CpuRelax(void)
163 {
164 }
165 
SmpRmb(void)166 static inline void SmpRmb(void)
167 {
168 }
169 
GetTlsPtr(void)170 static inline unsigned long GetTlsPtr(void)
171 {
172     return 0;
173 }
174 
BlockawareEnterSleeping(void)175 static inline int BlockawareEnterSleeping(void)
176 {
177     return 0;
178 }
179 
BlockawareLeaveSleeping(void)180 static inline int BlockawareLeaveSleeping(void)
181 {
182     return 0;
183 }
184 #endif
185 
BlockawareInit(unsigned long * keyPtr)186 static inline int BlockawareInit(unsigned long *keyPtr)
187 {
188     int rc = prctl(HM_PR_SILK_BLOCKAWARE_OPS, BLOCKAWARE_SUBOPS_INIT, reinterpret_cast<unsigned long>(keyPtr));
189     return (rc == 0) ? 0 : errno;
190 }
191 
BlockawareRegister(unsigned int domain)192 static inline int BlockawareRegister(unsigned int domain)
193 {
194     /* Mention that it is kernel's responsibility to init tls slot to 0 */
195     int rc = prctl(HM_PR_SILK_BLOCKAWARE_OPS, BLOCKAWARE_SUBOPS_REG, static_cast<unsigned long>(domain));
196     return (rc == 0) ? 0 : errno;
197 }
198 
BlockawareUnregister(void)199 static inline int BlockawareUnregister(void)
200 {
201     /* Mention that it is kernel's responsibility to reset tls slot to 0 */
202     int rc = prctl(HM_PR_SILK_BLOCKAWARE_OPS, BLOCKAWARE_SUBOPS_UNREG);
203     return (rc == 0) ? 0 : errno;
204 }
205 
seqlock_start_read(const uint32_t * seq_ptr)206 static inline uint32_t seqlock_start_read(const uint32_t *seq_ptr)
207 {
208     uint32_t seq;
209     do {
210         seq = *reinterpret_cast<const volatile uint32_t *>(seq_ptr);
211         if ((seq & 1U) == 0U) {
212             break;
213         }
214         CpuRelax();
215     } while (true);
216     SmpRmb();
217     return seq;
218 }
219 
seqlock_check(const uint32_t * seq_ptr,uint32_t seq_prev)220 static inline bool seqlock_check(const uint32_t *seq_ptr, uint32_t seq_prev)
221 {
222     SmpRmb();
223     return (*seq_ptr == seq_prev);
224 }
225 
BlockawareLoadSnapshot(unsigned long key,struct BlockawareDomainInfoArea * infoArea)226 static inline int BlockawareLoadSnapshot(unsigned long key, struct BlockawareDomainInfoArea *infoArea)
227 {
228     struct BlockawareKinfoPageS *kinfoPage = reinterpret_cast<struct BlockawareKinfoPageS *>(key);
229     uint32_t seq;
230     do {
231         seq = seqlock_start_read(&kinfoPage->seq);
232         memcpy_s(infoArea, sizeof(BlockawareDomainInfoArea), &kinfoPage->infoArea, sizeof(BlockawareDomainInfoArea));
233     } while (!seqlock_check(&kinfoPage->seq, seq));
234     return 0;
235 }
236 
BlockawareWaitCond(struct BlockawareWakeupCond * cond)237 static inline int BlockawareWaitCond(struct BlockawareWakeupCond *cond)
238 {
239     int rc = prctl(HM_PR_SILK_BLOCKAWARE_OPS, BLOCKAWARE_SUBOPS_WAIT, reinterpret_cast<unsigned long>(cond));
240     return (rc == 0) ? 0 : errno;
241 }
242 
BlockawareWake(void)243 static inline int BlockawareWake(void)
244 {
245     int rc = prctl(HM_PR_SILK_BLOCKAWARE_OPS, BLOCKAWARE_SUBOPS_WAKE);
246     return (rc == 0) ? 0 : errno;
247 }
248 
BlockawareMonitorfd(int fd,struct BlockawareWakeupCond * cond)249 static inline int BlockawareMonitorfd(int fd, struct BlockawareWakeupCond *cond)
250 {
251     int rc = prctl(HM_PR_SILK_BLOCKAWARE_OPS, BLOCKAWARE_SUBOPS_MONITORFD,
252         static_cast<unsigned long>(fd), reinterpret_cast<unsigned long>(cond));
253     return (rc == 0) ? rc : -errno;
254 }
255 
256 
257 #ifdef __cplusplus
258 }
259 #endif
260 #endif /* BLOCKAWARE_H */