EPICS Multi-Core Utilities  1.2.2-SNAPSHOT
Real-Time Utilities for EPICS IOCs on Multi-Core Linux
 All Files Functions Variables Typedefs Macros Groups Pages
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
39 
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;
65 } threadRule;
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  return -1;
125  }
126 
127  parseModifiers(prule, policy, priority, cpus);
128  regcomp(&prule->reg, prule->pattern, (REG_EXTENDED || REG_NOSUB));
129 
130  epicsMutexLock(listLock);
131  mcoreThreadRuleDelete(name);
132  ellAdd(&threadRules, &prule->node);
133  epicsMutexUnlock(listLock);
134  return 0;
135 }
136 
140 void mcoreThreadRuleDelete(const char *name)
141 {
142  threadRule *prule;
143 
144  epicsMutexLock(listLock);
145  prule = (threadRule *) ellFirst(&threadRules);
146  while (prule) {
147  if (0 == strcmp(name, prule->name)) {
148  ellDelete(&threadRules, &prule->node);
149  epicsMutexUnlock(listLock);
150  free(prule->name);
151  free(prule->pattern);
152  free(prule->cpus);
153  regfree(&prule->reg);
154  free(prule);
155  return;
156  }
157  prule = (threadRule *) ellNext(&prule->node);
158  }
159  epicsMutexUnlock(listLock);
160 }
161 
166 {
167  threadRule *prule;
168  const int buflen = 128; //FIXME should be ~ NO_OF_CPUS
169  char buf[buflen];
170 
171  epicsMutexLock(listLock);
172  prule = (threadRule *) ellFirst(&threadRules);
173  if (!prule) {
174  fprintf(epicsGetStdout(), "No rules defined.\n");
175  epicsMutexUnlock(listLock);
176  return;
177  }
178  fprintf(epicsGetStdout(), " NAME PRIO POLICY %-*s PATTERN\n", cpuspecLen, "AFFINITY");
179  while (prule) {
180  cpusetToStr(buf, buflen, &prule->cpuset);
181  fprintf(epicsGetStdout(), "%16s ",
182  prule->name);
183  if (prule->ch_priority) {
184  if (prule->rel_priority) {
185  fprintf(epicsGetStdout(), "%+4d ", prule->priority);
186  } else {
187  fprintf(epicsGetStdout(), "%4d ", prule->priority);
188  }
189  } else {
190  fprintf(epicsGetStdout(), " * ");
191  }
192  fprintf(epicsGetStdout(),"%6s %-*s %s\n",
193  prule->ch_policy?policyToStr(prule->policy):"*",
194  cpuspecLen, prule->ch_affinity?buf:"*",
195  prule->pattern
196  );
197  prule = (threadRule *) ellNext(&prule->node);
198  }
199  epicsMutexUnlock(listLock);
200 }
201 
208 static void modifyRTProperties(epicsThreadId id, threadRule *prule)
209 {
210  int status;
211  unsigned int priority;
212 
213  if (prule->ch_policy || prule->ch_priority) {
214  status = pthread_attr_getschedparam(&id->attr, &id->schedParam);
215  if (errVerbose)
216  checkStatus(status,"pthread_attr_getschedparam");
217  status = pthread_attr_getschedpolicy(&id->attr, &id->schedPolicy);
218  if (errVerbose)
219  checkStatus(status,"pthread_attr_getschedpolicy");
220 
221  if (prule->ch_policy) {
222  id->schedPolicy = prule->policy;
223  status = pthread_attr_setschedpolicy(&id->attr, id->schedPolicy);
224  if (errVerbose)
225  checkStatus(status,"pthread_attr_setschedpolicy");
226  if (SCHED_FIFO == prule->policy || SCHED_RR == prule->policy) {
227  id->isRealTimeScheduled = 1;
228  } else {
229  id->isRealTimeScheduled = 0;
230  }
231  }
232 
233  if (prule->ch_priority) {
234  if (prule->rel_priority) {
235  priority = id->osiPriority + prule->priority;
236  if (priority > epicsThreadPriorityMax) priority = epicsThreadPriorityMax;
237  if (priority < epicsThreadPriorityMin) priority = epicsThreadPriorityMin;
238  } else {
239  priority = prule->priority;
240  }
241  id->osiPriority = priority;
242  id->schedParam.sched_priority = epicsThreadGetPosixPriority(id);
243  status = pthread_attr_setschedparam(&id->attr, &id->schedParam);
244  if (errVerbose)
245  checkStatus(status,"pthread_attr_setschedparam");
246  }
247 
248  status = pthread_setschedparam(id->tid, id->schedPolicy, &id->schedParam);
249  if (errVerbose)
250  checkStatus(status,"pthread_setschedparam");
251  }
252 
253  if (prule->ch_affinity) {
254  status = pthread_attr_setaffinity_np(&id->attr,
255  sizeof(cpu_set_t),
256  &prule->cpuset);
257  if (errVerbose)
258  checkStatus(status,"pthread_attr_setaffinity_np");
259  status = pthread_setaffinity_np(id->tid,
260  sizeof(cpu_set_t),
261  &prule->cpuset);
262  if (errVerbose)
263  checkStatus(status,"pthread_setaffinity_np");
264  }
265 }
266 
267 static void threadStartHook (epicsThreadId id)
268 {
269  threadRule *prule;
270 
271  epicsMutexLock(listLock);
272  prule = (threadRule *) ellFirst(&threadRules);
273  if (!prule) {
274  epicsMutexUnlock(listLock);
275  return;
276  }
277  while (prule) {
278  if (0 == regexec(&prule->reg, id->name, 0, NULL, 0)) {
279  modifyRTProperties(id, prule);
280  }
281  prule = (threadRule *) ellNext(&prule->node);
282  }
283  epicsMutexUnlock(listLock);
284 }
285 
289 void mcoreThreadModify(epicsThreadId id, const char *policy, const char *priority, const char *cpus)
290 {
291  threadRule rule;
292 
293  assert(id);
294  parseModifiers(&rule, policy, priority, cpus);
295  modifyRTProperties(id, &rule);
296 }
297 
304 static int readRulesFromFile(const char *file)
305 {
306  const int linelen = 256;
307  const char sep = ':';
308  char line[linelen];
309  unsigned int lineno = 0;
310  char *args[5]; // rtgroups format -- name:policy:priority:affinity:pattern
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  while (fgets(line, linelen, fp)) {
319  int i;
320  char *sp;
321  char *cp;
322  lineno++;
323  args[0] = cp = line;
324  cp += strspn(cp, " \t\r\n"); // trim leading whitespace and empty lines
325  if (*cp == '#' || *cp == '\0')
326  continue;
327  for (i = 1; i < 5; i++) {
328  sp = strchr(cp, sep);
329  if (!sp) {
330  errlogPrintf("mcoreThreadRules: error parsing line %d of file %s\n", lineno, file);
331  return count;
332  }
333  *sp++ = '\0';
334  args[i] = cp = sp;
335  }
336  if ((sp = strpbrk(cp, "\n\r"))) {
337  *sp = '\0';
338  }
339  mcoreThreadRuleAdd(args[0], args[1], args[2], args[3], args[4]);
340  count++;
341  }
342  fclose (fp);
343  }
344  return count;
345 }
346 
347 static void once(void *arg)
348 {
349  const int len = 256;
350  char userFile[len];
351  char userRel[len];
352  int count;
353 
354  cpuspecLen = (int) (log10(NO_OF_CPUS-1) + 2) * NO_OF_CPUS / 2;
355  if (cpuspecLen < 10)
356  cpuspecLen = 10;
357  listLock = epicsMutexMustCreate();
358 
359  envGetConfigParam(&userHome, sizeof(userFile), userFile);
360  envGetConfigParam(&userConfigFile, sizeof(userRel), userRel);
361  if (userFile[strlen(userFile)-1] != '/')
362  strcat(userFile, "/");
363  strncat(userFile, userRel, len-strlen(userFile)-1);
364 
365  count = readRulesFromFile(sysConfigFile);
366  printf("MCoreUtils: Read %d thread rule(s) from %s\n", count, sysConfigFile);
367 
368  count = readRulesFromFile(userFile);
369  printf("MCoreUtils: Read %d thread rule(s) from %s\n", count, userFile);
370 
371  epicsThreadHookAdd(threadStartHook);
372 }
373 
378 {
379  static epicsThreadOnceId onceFlag = EPICS_THREAD_ONCE_INIT;
380  epicsThreadOnce(&onceFlag, once, NULL);
381 }
382