/ src / rtapi / uspace_common.h
uspace_common.h
  1  //    Copyright 2006-2014, various authors
  2  //
  3  //    This program is free software; you can redistribute it and/or modify
  4  //    it under the terms of the GNU General Public License as published by
  5  //    the Free Software Foundation; either version 2 of the License, or
  6  //    (at your option) any later version.
  7  //
  8  //    This program is distributed in the hope that it will be useful,
  9  //    but WITHOUT ANY WARRANTY; without even the implied warranty of
 10  //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 11  //    GNU General Public License for more details.
 12  //
 13  //    You should have received a copy of the GNU General Public License
 14  //    along with this program; if not, write to the Free Software
 15  //    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 16  #include <sys/time.h>
 17  #include <time.h>
 18  #include <stdio.h>
 19  #include <sys/utsname.h>
 20  #include <string.h>
 21  #include <unistd.h>
 22  #include <sys/types.h>
 23  #include <sys/stat.h>
 24  
 25  #include <rtapi_errno.h>
 26  #include <rtapi_mutex.h>
 27  static int msg_level = RTAPI_MSG_ERR;	/* message printing level */
 28  
 29  #include <sys/ipc.h>		/* IPC_* */
 30  #include <sys/shm.h>		/* shmget() */
 31  /* These structs hold data associated with objects like tasks, etc. */
 32  /* Task handles are pointers to these structs.                      */
 33  
 34  #include "config.h"
 35  
 36  #ifdef RTAPI
 37  #include "rtapi_uspace.hh"
 38  #endif
 39  
 40  typedef struct {
 41    int magic;			/* to check for valid handle */
 42    int key;			/* key to shared memory area */
 43    int id;			/* OS identifier for shmem */
 44    int count;                    /* count of maps in this process */
 45    unsigned long int size;	/* size of shared memory area */
 46    void *mem;			/* pointer to the memory */
 47  } rtapi_shmem_handle;
 48  
 49  #define MAX_SHM 64
 50  
 51  #define SHMEM_MAGIC   25453	/* random numbers used as signatures */
 52  
 53  static rtapi_shmem_handle shmem_array[MAX_SHM] = {{0},};
 54  
 55  int rtapi_shmem_new(int key, int module_id, unsigned long int size)
 56  {
 57  #ifdef RTAPI
 58    WITH_ROOT;
 59  #endif
 60    rtapi_shmem_handle *shmem;
 61    int i;
 62  
 63    for(i=0 ; i < MAX_SHM; i++) {
 64      if(shmem_array[i].magic == SHMEM_MAGIC && shmem_array[i].key == key) {
 65        shmem_array[i].count ++;
 66        return i;
 67      }
 68      if(shmem_array[i].magic != SHMEM_MAGIC) break;
 69    }
 70    if(i == MAX_SHM)
 71    {
 72      rtapi_print_msg(RTAPI_MSG_ERR, "rtapi_shmem_new failed due to MAX_SHM\n");
 73      return -ENOMEM;
 74    }
 75    shmem = &shmem_array[i];
 76  
 77    /* now get shared memory block from OS */
 78    shmem->id = shmget((key_t) key, (int) size, IPC_CREAT | 0600);
 79    if (shmem->id == -1) {
 80      rtapi_print_msg(RTAPI_MSG_ERR, "rtapi_shmem_new failed due to shmget(key=0x%08x): %s\n", key, strerror(errno));
 81      return -errno;
 82    }
 83  
 84    struct shmid_ds stat;
 85    int res = shmctl(shmem->id, IPC_STAT, &stat);
 86    if(res < 0) perror("shmctl IPC_STAT");
 87  
 88  #ifdef RTAPI
 89    /* ensure the segment is owned by user, not root */
 90    if(geteuid() == 0) {
 91      stat.shm_perm.uid = ruid;
 92      res = shmctl(shmem->id, IPC_SET, &stat);
 93      if(res < 0) perror("shmctl IPC_SET");
 94    }
 95  
 96  #ifndef __FreeBSD__ // FreeBSD doesn't implement SHM_LOCK
 97    if(rtapi_is_realtime())
 98    {
 99      /* ensure the segment is locked */
100      res = shmctl(shmem->id, SHM_LOCK, NULL);
101      if(res < 0) perror("shmctl IPC_LOCK");
102  
103      res = shmctl(shmem->id, IPC_STAT, &stat);
104      if(res < 0) perror("shmctl IPC_STAT");
105      if((stat.shm_perm.mode & SHM_LOCKED) != SHM_LOCKED)
106        rtapi_print_msg(RTAPI_MSG_ERR,
107            "shared memory segment not locked as requested\n");
108    }
109  #endif
110  #endif
111  
112    /* and map it into process space */
113    shmem->mem = shmat(shmem->id, 0, 0);
114    if ((ssize_t) (shmem->mem) == -1) {
115      rtapi_print_msg(RTAPI_MSG_ERR, "rtapi_shmem_new failed due to shmat()\n");
116      return -errno;
117    }
118  
119    long pagesize = sysconf(_SC_PAGESIZE);
120    /* touch every page */
121    for(size_t off = 0; off < size; off += pagesize)
122    {
123        volatile char i = ((char*)shmem->mem)[off];
124        (void)i;
125    }
126  
127    /* label as a valid shmem structure */
128    shmem->magic = SHMEM_MAGIC;
129    /* fill in the other fields */
130    shmem->size = size;
131    shmem->key = key;
132    shmem->count = 1;
133  
134    /* return handle to the caller */
135    return i;
136  }
137  
138  
139  int rtapi_shmem_getptr(int handle, void **ptr)
140  {
141    rtapi_shmem_handle *shmem;
142    if(handle < 0 || handle >= MAX_SHM)
143      return -EINVAL;
144  
145    shmem = &shmem_array[handle];
146  
147    /* validate shmem handle */
148    if (shmem->magic != SHMEM_MAGIC)
149      return -EINVAL;
150  
151    /* pass memory address back to caller */
152    *ptr = shmem->mem;
153    return 0;
154  }
155  
156  
157  int rtapi_shmem_delete(int handle, int module_id)
158  {
159    struct shmid_ds d;
160    int r1, r2;
161    rtapi_shmem_handle *shmem;
162  
163    if(handle < 0 || handle >= MAX_SHM)
164      return -EINVAL;
165  
166    shmem = &shmem_array[handle];
167  
168    /* validate shmem handle */
169    if (shmem->magic != SHMEM_MAGIC)
170      return -EINVAL;
171  
172    shmem->count --;
173    if(shmem->count) return 0;
174  
175    /* unmap the shared memory */
176    r1 = shmdt(shmem->mem);
177  
178    /* destroy the shared memory */
179    r2 = shmctl(shmem->id, IPC_STAT, &d);
180    if (r2 != 0)
181        rtapi_print_msg(RTAPI_MSG_ERR, "shmctl(%d, IPC_STAT, ...): %s\n", shmem->id, strerror(errno));
182  
183    if(r2 == 0 && d.shm_nattch == 0) {
184        r2 = shmctl(shmem->id, IPC_RMID, &d);
185        if (r2 != 0)
186  	      rtapi_print_msg(RTAPI_MSG_ERR, "shmctl(%d, IPC_RMID, ...): %s\n", shmem->id, strerror(errno));
187    }
188  
189    /* free the shmem structure */
190    shmem->magic = 0;
191  
192    if ((r1 != 0) || (r2 != 0))
193      return -EINVAL;
194    return 0;
195  }
196  
197  
198  
199  
200  void default_rtapi_msg_handler(msg_level_t level, const char *fmt, va_list ap);
201  
202  static rtapi_msg_handler_t rtapi_msg_handler = default_rtapi_msg_handler;
203  
204  rtapi_msg_handler_t rtapi_get_msg_handler(void) {
205      return rtapi_msg_handler;
206  }
207  
208  void rtapi_set_msg_handler(rtapi_msg_handler_t handler) {
209      if(handler == NULL) rtapi_msg_handler = default_rtapi_msg_handler;
210      else rtapi_msg_handler = handler;
211  }
212  
213  
214  void rtapi_print(const char *fmt, ...)
215  {
216      va_list args;
217  
218      va_start(args, fmt);
219      rtapi_msg_handler(RTAPI_MSG_ALL, fmt, args);
220      va_end(args);
221  }
222  
223  
224  void rtapi_print_msg(msg_level_t level, const char *fmt, ...)
225  {
226      va_list args;
227  
228      if ((level <= msg_level) && (msg_level != RTAPI_MSG_NONE)) {
229  	va_start(args, fmt);
230  	rtapi_msg_handler(level, fmt, args);
231  	va_end(args);
232      }
233  }
234  
235  int rtapi_snprintf(char *buffer, unsigned long int size, const char *msg, ...) {
236      va_list args;
237      int result;
238  
239      va_start(args, msg);
240      /* call the normal library vnsprintf() */
241      result = vsnprintf(buffer, size, msg, args);
242      va_end(args);
243      return result;
244  }
245  
246  int rtapi_vsnprintf(char *buffer, unsigned long int size, const char *fmt,
247  	va_list args) {
248      return vsnprintf(buffer, size, fmt, args);
249  }
250  
251  int rtapi_set_msg_level(int level) {
252      msg_level = level;
253      return 0;
254  }
255  
256  int rtapi_get_msg_level() {
257      return msg_level;
258  }
259  
260  #if defined(__i386) || defined(__amd64)
261  #define rdtscll(val) ((val) = __builtin_ia32_rdtsc())
262  #else
263  #define rdtscll(val) ((val) = rtapi_get_time())
264  #endif
265  
266  long long rtapi_get_clocks(void)
267  {
268      long long int retval;
269  
270      rdtscll(retval);
271      return retval;
272  }
273  
274  typedef struct {
275      rtapi_mutex_t mutex;
276      int           uuid;
277  } uuid_data_t;
278  
279  #define UUID_KEY  0x48484c34 /* key for UUID for simulator */
280  
281  static         int  uuid_mem_id = 0;
282  int rtapi_init(const char *modname)
283  {
284      static uuid_data_t* uuid_data   = 0;
285      const static   int  uuid_id     = 0;
286  
287      static char* uuid_shmem_base = 0;
288      int retval,id;
289      void *uuid_mem;
290  
291      uuid_mem_id = rtapi_shmem_new(UUID_KEY,uuid_id,sizeof(uuid_data_t));
292      if (uuid_mem_id < 0) {
293          rtapi_print_msg(RTAPI_MSG_ERR,
294          "rtapi_init: could not open shared memory for uuid\n");
295          rtapi_exit(uuid_id);
296          return -EINVAL;
297      }
298      retval = rtapi_shmem_getptr(uuid_mem_id,&uuid_mem);
299      if (retval < 0) {
300          rtapi_print_msg(RTAPI_MSG_ERR,
301          "rtapi_init: could not access shared memory for uuid\n");
302          rtapi_exit(uuid_id);
303          return -EINVAL;
304      }
305      if (uuid_shmem_base == 0) {
306          uuid_shmem_base =        (char *) uuid_mem;
307          uuid_data       = (uuid_data_t *) uuid_mem;
308      }
309      rtapi_mutex_get(&uuid_data->mutex);
310          uuid_data->uuid++;
311          id = uuid_data->uuid;
312      rtapi_mutex_give(&uuid_data->mutex);
313  
314      return id;
315  }
316  
317  int rtapi_exit(int module_id)
318  {
319    rtapi_shmem_delete(uuid_mem_id, module_id);
320    return 0;
321  }
322  
323  int rtapi_is_kernelspace() { return 0; }
324  static int _rtapi_is_realtime = -1;
325  #ifdef __linux__
326  static int detect_preempt_rt() {
327      struct utsname u;
328      int crit1, crit2 = 0;
329      FILE *fd;
330  
331      uname(&u);
332      crit1 = strcasestr (u.version, "PREEMPT RT") != 0;
333  
334      if ((fd = fopen("/sys/kernel/realtime","r")) != NULL) {
335          int flag;
336          crit2 = ((fscanf(fd, "%d", &flag) == 1) && (flag == 1));
337          fclose(fd);
338      }
339  
340      return crit1 && crit2;
341  }
342  #else
343  static int detect_preempt_rt() {
344      return 0;
345  }
346  #endif
347  #ifdef USPACE_RTAI
348  static int detect_rtai() {
349      struct utsname u;
350      uname(&u);
351      return strcasestr (u.release, "-rtai") != 0;
352  }
353  #else
354  static int detect_rtai() {
355      return 0;
356  }
357  #endif
358  #ifdef USPACE_XENOMAI
359  static int detect_xenomai() {
360      struct utsname u;
361      uname(&u);
362      return strcasestr (u.release, "-xenomai") != 0;
363  }
364  #else
365  static int detect_xenomai() {
366      return 0;
367  }
368  #endif
369  static int detect_env_override() {
370      char *p = getenv("LINUXCNC_FORCE_REALTIME");
371      return p != NULL && atoi(p) != 0;
372  }
373  
374  static int detect_realtime() {
375      struct stat st;
376      if ((stat(EMC2_BIN_DIR "/rtapi_app", &st) < 0)
377              || st.st_uid != 0 || !(st.st_mode & S_ISUID))
378          return 0;
379      return detect_env_override() || detect_preempt_rt() || detect_rtai() || detect_xenomai();
380  }
381  
382  int rtapi_is_realtime() {
383      if(_rtapi_is_realtime == -1) _rtapi_is_realtime = detect_realtime();
384      return _rtapi_is_realtime;
385  }
386  
387  /* Like clock_nanosleep, except that an optional 'estimate of now' parameter may
388   * optionally be passed in.  This is a very slight optimization for platforms
389   * where rtapi_clock_nanosleep is implemented in terms of nanosleep, because it
390   * can avoid an additional clock_gettime syscall.
391   */
392  static int rtapi_clock_nanosleep(clockid_t clock_id, int flags,
393          const struct timespec *prequest, struct timespec *remain,
394          const struct timespec *pnow)
395  {
396  #if defined(HAVE_CLOCK_NANOSLEEP)
397      return clock_nanosleep(clock_id, flags, prequest, remain);
398  #else
399      if(flags == 0)
400          return nanosleep(prequest, remain);
401      if(flags != TIMER_ABSTIME)
402      {
403          errno = EINVAL;
404          return -1;
405      }
406      struct timespec now;
407      if(!pnow)
408      {
409          int res = clock_gettime(clock_id, &now);
410          if(res < 0) return res;
411          pnow = &now;
412      }
413  #undef timespecsub
414  #define	timespecsub(tvp, uvp, vvp)					\
415  	do {								\
416  		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
417  		(vvp)->tv_nsec = (tvp)->tv_nsec - (uvp)->tv_nsec;	\
418  		if ((vvp)->tv_nsec < 0) {				\
419  			(vvp)->tv_sec--;				\
420  			(vvp)->tv_nsec += 1000000000;			\
421  		}							\
422  	} while (0)
423      struct timespec request;
424      timespecsub(prequest, pnow, &request);
425      return nanosleep(&request, remain);
426  #endif
427  }