EPICS Multi-Core Utilities  1.2.3-SNAPSHOT
Real-Time Utilities for EPICS IOCs on Multi-Core Linux
threadRules.c
Go to the documentation of this file.
1 /********************************************/
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <pthread.h>
22 #include <sys/types.h>
23 #include <regex.h>
24 #include <string.h>
25 
26 #include <ellLib.h>
27 #include <envDefs.h>
28 #include <errlog.h>
29 #include <epicsStdio.h>
30 #include <epicsMath.h>
31 #include <epicsThread.h>
32 #include <epicsMutex.h>
33 #include <shareLib.h>
34 
35 #include "utils.h"
36 
38 #define epicsExportSharedSymbols
40 #include "mcoreutils.h"
41 
43 extern int epicsThreadGetPosixPriority(epicsThreadOSD *pthreadInfo);
45 
52 typedef struct threadRule {
53  ELLNODE node;
54  char *name;
55  char *pattern;
56  char *cpus;
57  regex_t reg;
58  char ch_policy;
59  char ch_priority;
60  char ch_affinity;
61  char rel_priority;
62  int policy;
63  int priority;
64  cpu_set_t cpuset;
66 
67 static ELLLIST threadRules = ELLLIST_INIT;
68 static epicsMutexId listLock;
69 static unsigned int cpuspecLen;
70 static char *sysConfigFile = "/etc/rtrules";
71 static ENV_PARAM userHome = {"HOME","/"};
72 static ENV_PARAM userConfigFile = {"EPICS_MCORE_USERCONFIG",".rtrules"};
73 
82 static void parseModifiers(threadRule *prule, const char *policy, const char *priority, const char *cpus)
83 {
84  if (policy && '*' != policy[0] && '\0' != policy[0]) {
85  prule->ch_policy = 1;
86  prule->policy = strToPolicy(policy);
87  if (-1 == prule->policy) {
88  prule->ch_policy = prule->policy = 0;
89  }
90  }
91  if (priority && '*' != priority[0] && '\0' != priority[0]) {
92  prule->ch_priority = 1;
93  if ('+' == priority[0] || '-' == priority[0]) {
94  prule->rel_priority = 1;
95  }
96  prule->priority = atoi(priority);
97  if (prule->priority > epicsThreadPriorityMax) prule->priority = epicsThreadPriorityMax;
98  if (prule->priority < epicsThreadPriorityMin) prule->priority = epicsThreadPriorityMin;
99  }
100  if (cpus && '*' != cpus[0] && '\0' != cpus[0]) {
101  prule->ch_affinity = 1;
102  strToCpuset(&prule->cpuset, cpus);
103  }
104 }
105 
109 long mcoreThreadRuleAdd(const char *name, const char *policy, const char *priority, const char *cpus, const char *pattern)
110 {
111  threadRule *prule;
112 
113  prule = calloc(1,sizeof(threadRule));
114  if (!prule) {
115  errlogPrintf("Memory allocation error\n");
116  return -1;
117  }
118 
119  prule->name = strdup(name);
120  prule->pattern = strdup(pattern);
121  prule->cpus = strdup(cpus);
122  if (!prule->name || !prule->pattern || !prule->cpus) {
123  errlogPrintf("Memory allocation error\n");
124  free(prule->name); free(prule->pattern); free(prule->cpus);
125  free(prule);
126  return -1;
127  }
128 
129  parseModifiers(prule, policy, priority, cpus);
130  regcomp(&prule->reg, prule->pattern, (REG_EXTENDED || REG_NOSUB));
131 
132  epicsMutexLock(listLock);
133  mcoreThreadRuleDelete(name);
134  ellAdd(&threadRules, &prule->node);
135  epicsMutexUnlock(listLock);
136  return 0;
137 }
138 
142 void mcoreThreadRuleDelete(const char *name)
143 {
144  threadRule *prule;
145 
146  epicsMutexLock(listLock);
147  prule = (threadRule *) ellFirst(&threadRules);
148  while (prule) {
149  if (0 == strcmp(name, prule->name)) {
150  ellDelete(&threadRules, &prule->node);
151  epicsMutexUnlock(listLock);
152  free(prule->name);
153  free(prule->pattern);
154  free(prule->cpus);
155  regfree(&prule->reg);
156  free(prule);
157  return;
158  }
159  prule = (threadRule *) ellNext(&prule->node);
160  }
161  epicsMutexUnlock(listLock);
162 }
163 
168 {
169  threadRule *prule;
170  const int buflen = 128; //FIXME should be ~ NO_OF_CPUS
171  char buf[buflen];
172 
173  epicsMutexLock(listLock);
174  prule = (threadRule *) ellFirst(&threadRules);
175  if (!prule) {
176  fprintf(epicsGetStdout(), "No rules defined.\n");
177  epicsMutexUnlock(listLock);
178  return;
179  }
180  fprintf(epicsGetStdout(), " NAME PRIO POLICY %-*s PATTERN\n", cpuspecLen, "AFFINITY");
181  while (prule) {
182  cpusetToStr(buf, buflen, &prule->cpuset);
183  fprintf(epicsGetStdout(), "%16s ",
184  prule->name);
185  if (prule->ch_priority) {
186  if (prule->rel_priority) {
187  fprintf(epicsGetStdout(), "%+4d ", prule->priority);
188  } else {
189  fprintf(epicsGetStdout(), "%4d ", prule->priority);
190  }
191  } else {
192  fprintf(epicsGetStdout(), " * ");
193  }
194  fprintf(epicsGetStdout(),"%6s %-*s %s\n",
195  prule->ch_policy?policyToStr(prule->policy):"*",
196  cpuspecLen, prule->ch_affinity?buf:"*",
197  prule->pattern
198  );
199  prule = (threadRule *) ellNext(&prule->node);
200  }
201  epicsMutexUnlock(listLock);
202 }
203 
210 static void modifyRTProperties(epicsThreadId id, threadRule *prule)
211 {
212  int status;
213 
214  if (prule->ch_policy || prule->ch_priority) {
215  status = pthread_attr_getschedparam(&id->attr, &id->schedParam);
216  if (errVerbose)
217  checkStatus(status,"pthread_attr_getschedparam");
218  status = pthread_attr_getschedpolicy(&id->attr, &id->schedPolicy);
219  if (errVerbose)
220  checkStatus(status,"pthread_attr_getschedpolicy");
221 
222  if (prule->ch_policy) {
223  id->schedPolicy = prule->policy;
224  status = pthread_attr_setschedpolicy(&id->attr, id->schedPolicy);
225  if (errVerbose)
226  checkStatus(status,"pthread_attr_setschedpolicy");
227  if (SCHED_FIFO == prule->policy || SCHED_RR == prule->policy) {
228  id->isRealTimeScheduled = 1;
229  } else {
230  id->isRealTimeScheduled = 0;
231  }
232  }
233 
234  if (prule->ch_priority) {
235  unsigned int priority;
236  if (prule->rel_priority) {
237  priority = id->osiPriority + prule->priority;
238  if (priority > epicsThreadPriorityMax) priority = epicsThreadPriorityMax;
239  if (priority < epicsThreadPriorityMin) priority = epicsThreadPriorityMin;
240  } else {
241  priority = prule->priority;
242  }
243  id->osiPriority = priority;
244  id->schedParam.sched_priority = epicsThreadGetPosixPriority(id);
245  status = pthread_attr_setschedparam(&id->attr, &id->schedParam);
246  if (errVerbose)
247  checkStatus(status,"pthread_attr_setschedparam");
248  }
249 
250  status = pthread_setschedparam(id->tid, id->schedPolicy, &id->schedParam);
251  if (errVerbose)
252  checkStatus(status,"pthread_setschedparam");
253  }
254 
255  if (prule->ch_affinity) {
256  status = pthread_attr_setaffinity_np(&id->attr,
257  sizeof(cpu_set_t),
258  &prule->cpuset);
259  if (errVerbose)
260  checkStatus(status,"pthread_attr_setaffinity_np");
261  status = pthread_setaffinity_np(id->tid,
262  sizeof(cpu_set_t),
263  &prule->cpuset);
264  if (errVerbose)
265  checkStatus(status,"pthread_setaffinity_np");
266  }
267 }
268 
269 static void threadStartHook (epicsThreadId id)
270 {
271  threadRule *prule;
272 
273  epicsMutexLock(listLock);
274  prule = (threadRule *) ellFirst(&threadRules);
275  if (!prule) {
276  epicsMutexUnlock(listLock);
277  return;
278  }
279  while (prule) {
280  if (0 == regexec(&prule->reg, id->name, 0, NULL, 0)) {
281  modifyRTProperties(id, prule);
282  }
283  prule = (threadRule *) ellNext(&prule->node);
284  }
285  epicsMutexUnlock(listLock);
286 }
287 
291 void mcoreThreadModify(epicsThreadId id, const char *policy, const char *priority, const char *cpus)
292 {
293  threadRule rule;
294 
295  assert(id);
296  parseModifiers(&rule, policy, priority, cpus);
297  modifyRTProperties(id, &rule);
298 }
299 
306 static int readRulesFromFile(const char *file)
307 {
308  const int linelen = 256;
309  const char sep = ':';
310  char line[linelen];
311  int count = 0;
312 
313  FILE *fp = fopen(file, "r");
314  if (NULL == fp) {
315  if (errVerbose)
316  errlogPrintf("mcoreThreadRules: can't open rules file %s\n", file);
317  } else {
318  unsigned int lineno = 0;
319  char *args[5]; // rtgroups format -- name:policy:priority:affinity:pattern
320  while (fgets(line, linelen, fp)) {
321  int i;
322  char *sp;
323  char *cp;
324  lineno++;
325  args[0] = cp = line;
326  cp += strspn(cp, " \t\r\n"); // trim leading whitespace and empty lines
327  if (*cp == '#' || *cp == '\0')
328  continue;
329  for (i = 1; i < 5; i++) {
330  sp = strchr(cp, sep);
331  if (!sp) {
332  errlogPrintf("mcoreThreadRules: error parsing line %d of file %s\n", lineno, file);
333  return count;
334  }
335  *sp++ = '\0';
336  args[i] = cp = sp;
337  }
338  if ((sp = strpbrk(cp, "\n\r"))) {
339  *sp = '\0';
340  }
341  mcoreThreadRuleAdd(args[0], args[1], args[2], args[3], args[4]);
342  count++;
343  }
344  fclose (fp);
345  }
346  return count;
347 }
348 
349 static void once(void *arg)
350 {
351  const int len = 256;
352  char userFile[len];
353  char userRel[len];
354  int count;
355 
356  cpuspecLen = (int) (log10(NO_OF_CPUS-1) + 2) * NO_OF_CPUS / 2;
357  if (cpuspecLen < 10)
358  cpuspecLen = 10;
359  listLock = epicsMutexMustCreate();
360 
361  envGetConfigParam(&userHome, sizeof(userFile), userFile);
362  envGetConfigParam(&userConfigFile, sizeof(userRel), userRel);
363  if (userFile[strlen(userFile)-1] != '/')
364  strcat(userFile, "/");
365  strncat(userFile, userRel, len-strlen(userFile)-1);
366 
367  count = readRulesFromFile(sysConfigFile);
368  printf("MCoreUtils: Read %d thread rule(s) from %s\n", count, sysConfigFile);
369 
370  count = readRulesFromFile(userFile);
371  printf("MCoreUtils: Read %d thread rule(s) from %s\n", count, userFile);
372 
373  epicsThreadHookAdd(threadStartHook);
374 }
375 
380 {
381  static epicsThreadOnceId onceFlag = EPICS_THREAD_ONCE_INIT;
382  epicsThreadOnce(&onceFlag, once, NULL);
383 }
384 
long mcoreThreadRuleAdd(const char *name, const char *policy, const char *priority, const char *cpus, const char *pattern)
Add or replace a thread rule.
Definition: threadRules.c:109
void mcoreThreadModify(epicsThreadId id, const char *policy, const char *priority, const char *cpus)
Modify a thread's real-time properties.
Definition: threadRules.c:291
void mcoreThreadRuleDelete(const char *name)
Delete a thread rule.
Definition: threadRules.c:142
void mcoreThreadRulesShow(void)
Print a comprehensive list of the thread rules.
Definition: threadRules.c:167
void mcoreThreadRulesInit(void)
Initialization routine.
Definition: threadRules.c:379
struct threadRule threadRule
A thread rule.
const char * policyToStr(const int policy)
Convert scheduling policy to string.
Definition: utils.c:101
void cpusetToStr(char *set, size_t len, const cpu_set_t *cpuset)
Convert a cpuset into its string specification (e.g. "0,2-3").
Definition: utils.c:63
void strToCpuset(cpu_set_t *cpuset, const char *spec)
Convert a cpuset string specification (e.g. "0,2-3") to a cpuset.
Definition: utils.c:33
int strToPolicy(const char *string)
Convert string policy specification to policy.
Definition: utils.c:129
Header file for utils.c.
#define checkStatus(status, message)
Definition: utils.h:24
#define NO_OF_CPUS
Definition: utils.h:22