Skip to content

Commit 97bb377

Browse files
committed
Realtime detection rework
1 parent f80af10 commit 97bb377

5 files changed

Lines changed: 130 additions & 66 deletions

File tree

src/hal/hal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ extern int hal_unready(int comp_id);
237237
*/
238238
extern char* hal_comp_name(int comp_id);
239239

240-
extern int hal_realtime_status();
240+
extern rtapi_realtime_status_t hal_realtime_status();
241241

242242
/** The HAL maintains lists of variables, functions, and so on in
243243
a central database, located in shared memory so all components

src/hal/hal_lib.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3121,7 +3121,7 @@ int rtapi_app_main(void)
31213121
rtapi_print_msg(RTAPI_MSG_DBG,
31223122
"HAL_LIB: kernel lib installed successfully\n");
31233123

3124-
hal_data->realtime_status = rtapi_is_realtime();
3124+
hal_data->realtime_status = rtapi_realtime_status();
31253125

31263126
return 0;
31273127
}

src/rtapi/rtai_rtapi.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,6 +1689,7 @@ unsigned char rtapi_inb(unsigned int port)
16891689
}
16901690

16911691
int rtapi_is_realtime() { return 1; }
1692+
rtapi_realtime_status_t rtapi_realtime_status(void){ return REALTIME_STATUS_RTAI; }
16921693
int rtapi_is_kernelspace() { return 1; }
16931694

16941695
/* starting with kernel 2.6, symbols that are used by other modules

src/rtapi/rtapi.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,24 @@ int rtapi_spawnp_as_root(pid_t *pid, const char *path,
985985
#endif
986986

987987
extern int rtapi_is_kernelspace(void);
988+
989+
typedef enum{
990+
REALTIME_STATUS_UNINITIALIZED = -1,
991+
REALTIME_STATUS_NONE = 0,
992+
REALTIME_STATUS_RTAI = 1,
993+
REALTIME_STATUS_PREEMT_DYNAMIC = 2,
994+
REALTIME_STATUS_PREEMT_RT = 3,
995+
REALTIME_STATUS_LXRT = 4,
996+
REALTIME_STATUS_XENOMAI = 5,
997+
REALTIME_STATUS_XENOMAI_EVL = 6,
998+
} rtapi_realtime_status_t;
999+
1000+
#ifdef RTAPI
1001+
//Only available in real time context
1002+
//In userspace context, use hal_realtime_status()
9881003
extern int rtapi_is_realtime(void);
1004+
extern rtapi_realtime_status_t rtapi_realtime_status(void);
1005+
#endif
9891006

9901007
int rtapi_open_as_root(const char *filename, int mode);
9911008

src/rtapi/uspace_rtapi_main.cc

Lines changed: 110 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,20 @@ static inline int detect_preempt_rt() {
7979
}
8080
#endif
8181

82+
#ifdef __linux__
83+
// detect_preempt_dynamic() inspects uname for the PREEMPT_DYNAMIC marker.
84+
static inline int detect_preempt_dynamic() {
85+
struct utsname u;
86+
if(uname(&u) < 0) return 0;
87+
return strcasestr(u.version, "PREEMPT_DYNAMIC") != 0
88+
|| strcasestr(u.version, "PREEMPT_DYNAMIC") != 0;
89+
}
90+
#else
91+
static inline int detect_preempt_dynamic() {
92+
return 0;
93+
}
94+
#endif
95+
8296
// FIXME: detect_rtai/detect_xenomai/detect_xenomai_evl currently gate on
8397
// setuid because the RTAI/Xenomai backends still need root for iopl()
8498
// (RTAI) or RTDM device access (Xenomai/EVL). Long-term these should
@@ -127,10 +141,52 @@ static int detect_xenomai_evl() {
127141
}
128142
#endif
129143

130-
// errno from the most recent sched_setscheduler(SCHED_FIFO) probe. Zero
131-
// when the probe succeeded or has not run yet. Read via
132-
// rtapi_sched_fifo_errno() from diagnostic code.
133-
static int rtapi_sched_fifo_last_errno = 0;
144+
static int detect_force(){
145+
const char *force = getenv("LINUXCNC_FORCE_REALTIME");
146+
if(force != NULL && atoi(force) != 0){
147+
return 1;
148+
}else{
149+
return 0;
150+
}
151+
}
152+
153+
#ifdef __linux__
154+
// Diagnostic helper: report cap_effective state for a single capability.
155+
// Returns "yes", "no", or "unknown" if libcap could not introspect.
156+
static const char *cap_effective_str(cap_t caps, cap_value_t cap) {
157+
if (!caps) return "unknown";
158+
cap_flag_value_t v;
159+
if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &v) != 0) return "unknown";
160+
return v == CAP_SET ? "yes" : "no";
161+
}
162+
#else
163+
static const char *cap_effective_str(cap_t caps, cap_value_t cap) {
164+
return "unknown";
165+
}
166+
#endif
167+
168+
static void report_sched_fifo_error(int sched_err){
169+
// Surface the actual reason so the user does not have to guess
170+
// between "no caps", "stock kernel", or "wrong rlimits" (issue
171+
// #3928). errno comes from the SCHED_FIFO probe in
172+
// can_set_sched_fifo(); cap state comes from libcap.
173+
cap_t caps = cap_get_proc();
174+
const char *nice_s = cap_effective_str(caps, CAP_SYS_NICE);
175+
const char *lock_s = cap_effective_str(caps, CAP_IPC_LOCK);
176+
rtapi_print_msg(RTAPI_MSG_ERR,
177+
"Note: realtime scheduling unavailable "
178+
"(sched_setscheduler SCHED_FIFO: %s).\n"
179+
" Process capabilities: cap_sys_nice=%s cap_ipc_lock=%s.\n"
180+
" Falling back to POSIX non-realtime.\n"
181+
" Fix: 'sudo make setcap' (preferred) or 'sudo make setuid' "
182+
"on rtapi_app.\n"
183+
" Override (testing only): set LINUXCNC_FORCE_REALTIME=1.\n",
184+
sched_err ? strerror(sched_err) : "denied",
185+
nice_s, lock_s);
186+
#ifdef __linux__
187+
if (caps) cap_free(caps);
188+
#endif
189+
}
134190

135191
// Success-probe for realtime scheduling: briefly try to set SCHED_FIFO on
136192
// the calling thread and restore the previous policy. Succeeds when the
@@ -142,29 +198,63 @@ static int can_set_sched_fifo(void) {
142198
struct sched_param old_param, probe_param;
143199
int old_policy = sched_getscheduler(0);
144200
if(old_policy < 0) {
145-
rtapi_sched_fifo_last_errno = errno;
201+
report_sched_fifo_error(errno);
146202
return 0;
147203
}
148204
if(sched_getparam(0, &old_param) < 0) {
149-
rtapi_sched_fifo_last_errno = errno;
205+
report_sched_fifo_error(errno);
150206
return 0;
151207
}
152208

153209
memset(&probe_param, 0, sizeof(probe_param));
154210
probe_param.sched_priority = sched_get_priority_min(SCHED_FIFO);
155211
if(sched_setscheduler(0, SCHED_FIFO, &probe_param) < 0) {
156-
rtapi_sched_fifo_last_errno = errno;
212+
report_sched_fifo_error(errno);
157213
return 0;
158214
}
159215

160216
// Best-effort restore; if this fails we are still on SCHED_FIFO at
161217
// minimum priority, which is no worse than where we started.
162218
sched_setscheduler(0, old_policy, &old_param);
163-
rtapi_sched_fifo_last_errno = 0;
164219
return 1;
165220
}
166221

167-
static inline int rtapi_sched_fifo_errno(void) { return rtapi_sched_fifo_last_errno; }
222+
rtapi_realtime_status_t rtapi_realtime_status(void){
223+
static rtapi_realtime_status_t cached = REALTIME_STATUS_UNINITIALIZED;
224+
if(cached != REALTIME_STATUS_UNINITIALIZED){
225+
return cached;
226+
}
227+
228+
if(!detect_force() && !can_set_sched_fifo()){
229+
cached = REALTIME_STATUS_NONE;
230+
return cached;
231+
}
232+
233+
if(detect_rtai()){
234+
cached = REALTIME_STATUS_LXRT;
235+
return cached;
236+
}
237+
if(detect_xenomai()){
238+
cached = REALTIME_STATUS_XENOMAI;
239+
return cached;
240+
}
241+
if(detect_xenomai_evl()){
242+
cached = REALTIME_STATUS_XENOMAI_EVL;
243+
return cached;
244+
}
245+
if(detect_preempt_rt()){
246+
cached = REALTIME_STATUS_PREEMT_RT;
247+
return cached;
248+
}
249+
if(detect_preempt_dynamic()){
250+
cached = REALTIME_STATUS_PREEMT_DYNAMIC;
251+
return cached;
252+
}
253+
254+
rtapi_print_msg(RTAPI_MSG_ERR, "rtapi_realtime_status(): Bug, something unknown is running\n");
255+
cached = REALTIME_STATUS_NONE;
256+
return cached;
257+
}
168258

169259
// rtapi_is_realtime() reports whether this process can actually run
170260
// realtime code. This matches the convention used by JACK, PipeWire,
@@ -174,17 +264,7 @@ static inline int rtapi_sched_fifo_errno(void) { return rtapi_sched_fifo_last_er
174264
// wrapper-based installs like NixOS /run/wrappers) and silently masked
175265
// LINUXCNC_FORCE_REALTIME (see issue #3928).
176266
int rtapi_is_realtime() {
177-
static int cached = -1;
178-
if(cached != -1) return cached;
179-
180-
const char *force = getenv("LINUXCNC_FORCE_REALTIME");
181-
if(force != NULL && atoi(force) != 0)
182-
return (cached = 1);
183-
184-
if(detect_rtai() || detect_xenomai() || detect_xenomai_evl())
185-
return (cached = 1);
186-
187-
return (cached = can_set_sched_fifo());
267+
return rtapi_realtime_status() > 0;
188268
}
189269

190270
struct message_t {
@@ -1149,16 +1229,7 @@ static RtapiApp *makeDllApp(const std::string &dllName, int policy) {
11491229
return result;
11501230
}
11511231

1152-
// Diagnostic helper: report cap_effective state for a single capability.
1153-
// Returns "yes", "no", or "unknown" if libcap could not introspect.
11541232
#ifdef __linux__
1155-
static const char *cap_effective_str(cap_t caps, cap_value_t cap) {
1156-
if (!caps) return "unknown";
1157-
cap_flag_value_t v;
1158-
if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &v) != 0) return "unknown";
1159-
return v == CAP_SET ? "yes" : "no";
1160-
}
1161-
11621233
// Raise CAP_NET_ADMIN into the ambient set so it survives execve() into
11631234
// child processes (iptables, ip6tables) launched by HAL drivers like
11641235
// hm2_eth. Linux file capabilities on rtapi_app give cap_net_admin in
@@ -1190,59 +1261,34 @@ static void raise_net_admin_ambient(void) {
11901261

11911262
static RtapiApp *makeApp() {
11921263
RtapiApp *app;
1193-
bool rt_ok = rtapi_is_realtime();
1194-
if (!rt_ok) {
1195-
// Surface the actual reason so the user does not have to guess
1196-
// between "no caps", "stock kernel", or "wrong rlimits" (issue
1197-
// #3928). errno comes from the SCHED_FIFO probe in
1198-
// can_set_sched_fifo(); cap state comes from libcap.
1199-
int sched_err = rtapi_sched_fifo_errno();
1200-
#ifdef __linux__
1201-
cap_t caps = cap_get_proc();
1202-
const char *nice_s = cap_effective_str(caps, CAP_SYS_NICE);
1203-
const char *lock_s = cap_effective_str(caps, CAP_IPC_LOCK);
1204-
#else
1205-
const char *nice_s = "unknown";
1206-
const char *lock_s = "unknown";
1207-
#endif
1208-
rtapi_print_msg(RTAPI_MSG_ERR,
1209-
"Note: realtime scheduling unavailable "
1210-
"(sched_setscheduler SCHED_FIFO: %s).\n"
1211-
" Process capabilities: cap_sys_nice=%s cap_ipc_lock=%s.\n"
1212-
" Falling back to POSIX non-realtime.\n"
1213-
" Fix: 'sudo make setcap' (preferred) or 'sudo make setuid' "
1214-
"on rtapi_app.\n"
1215-
" Override (testing only): set LINUXCNC_FORCE_REALTIME=1.\n",
1216-
sched_err ? strerror(sched_err) : "denied",
1217-
nice_s, lock_s);
1218-
#ifdef __linux__
1219-
if (caps) cap_free(caps);
1220-
#endif
1221-
}
1222-
if (!rt_ok) {
1264+
rtapi_realtime_status_t rt_status = rtapi_realtime_status();
1265+
if (rt_status == REALTIME_STATUS_NONE) {
12231266
app = makeDllApp("liblinuxcnc-uspace-posix.so.0", SCHED_OTHER);
12241267
} else {
12251268
WithRoot r;
12261269
harden_rt();
1227-
if (detect_xenomai_evl()) {
1270+
if (rt_status == REALTIME_STATUS_XENOMAI_EVL) {
12281271
app = makeDllApp("liblinuxcnc-uspace-xenomai-evl.so.0", SCHED_FIFO);
1229-
} else if (detect_xenomai()) {
1272+
} else if (rt_status == REALTIME_STATUS_XENOMAI) {
12301273
app = makeDllApp("liblinuxcnc-uspace-xenomai.so.0", SCHED_FIFO);
1231-
} else if (detect_rtai()) {
1274+
} else if (rt_status == REALTIME_STATUS_RTAI) {
12321275
app = makeDllApp("liblinuxcnc-uspace-rtai.so.0", SCHED_FIFO);
1233-
} else {
1276+
} else if (rt_status == REALTIME_STATUS_PREEMT_RT || rt_status == REALTIME_STATUS_PREEMT_DYNAMIC) {
12341277
// SCHED_FIFO available but no Xenomai/RTAI backend. Warn if the
12351278
// kernel is not PREEMPT_RT: SCHED_FIFO still beats SCHED_OTHER,
12361279
// but latency on a PREEMPT_DYNAMIC stock kernel can be tens of
12371280
// milliseconds, which will surprise users who expect the same
12381281
// bounds as a PREEMPT_RT or Xenomai setup.
1239-
if (!detect_preempt_rt()) {
1282+
if (rt_status == REALTIME_STATUS_PREEMT_DYNAMIC) {
12401283
rtapi_print_msg(RTAPI_MSG_ERR,
12411284
"Note: SCHED_FIFO available but kernel is not PREEMPT_RT. "
12421285
"Latency may be unbounded; install a PREEMPT_RT kernel "
12431286
"for hard realtime guarantees.\n");
12441287
}
12451288
app = makeDllApp("liblinuxcnc-uspace-posix.so.0", SCHED_FIFO);
1289+
} else {
1290+
app = nullptr;
1291+
rtapi_print_msg(RTAPI_MSG_ERR, "Bug: rt_status = %i in not handled\n", rt_status);
12461292
}
12471293
}
12481294

0 commit comments

Comments
 (0)