ipc.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "plugin_proc.h"
  3. #include <sys/sem.h>
  4. #include <sys/msg.h>
  5. #include <sys/shm.h>
  6. #ifndef SEMVMX
  7. #define SEMVMX 32767 /* <= 32767 semaphore maximum value */
  8. #endif
  9. /* Some versions of libc only define IPC_INFO when __USE_GNU is defined. */
  10. #ifndef IPC_INFO
  11. #define IPC_INFO 3
  12. #endif
  13. struct ipc_limits {
  14. uint64_t shmmni; /* max number of segments */
  15. uint64_t shmmax; /* max segment size */
  16. uint64_t shmall; /* max total shared memory */
  17. uint64_t shmmin; /* min segment size */
  18. int semmni; /* max number of arrays */
  19. int semmsl; /* max semaphores per array */
  20. int semmns; /* max semaphores system wide */
  21. int semopm; /* max ops per semop call */
  22. unsigned int semvmx; /* semaphore max value (constant) */
  23. int msgmni; /* max queues system wide */
  24. size_t msgmax; /* max size of message */
  25. int msgmnb; /* default max size of queue */
  26. };
  27. struct ipc_status {
  28. int semusz; /* current number of arrays */
  29. int semaem; /* current semaphores system wide */
  30. };
  31. /*
  32. * The last arg of semctl is a union semun, but where is it defined? X/OPEN
  33. * tells us to define it ourselves, but until recently Linux include files
  34. * would also define it.
  35. */
  36. #ifndef HAVE_UNION_SEMUN
  37. /* according to X/OPEN we have to define it ourselves */
  38. union semun {
  39. int val;
  40. struct semid_ds *buf;
  41. unsigned short int *array;
  42. struct seminfo *__buf;
  43. };
  44. #endif
  45. static inline int ipc_sem_get_limits(struct ipc_limits *lim) {
  46. static procfile *ff = NULL;
  47. static int error_shown = 0;
  48. static char filename[FILENAME_MAX + 1] = "";
  49. if(unlikely(!filename[0]))
  50. snprintfz(filename, FILENAME_MAX, "%s/proc/sys/kernel/sem", netdata_configured_host_prefix);
  51. if(unlikely(!ff)) {
  52. ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
  53. if(unlikely(!ff)) {
  54. if(unlikely(!error_shown)) {
  55. error("IPC: Cannot open file '%s'.", filename);
  56. error_shown = 1;
  57. }
  58. goto ipc;
  59. }
  60. }
  61. ff = procfile_readall(ff);
  62. if(unlikely(!ff)) {
  63. if(unlikely(!error_shown)) {
  64. error("IPC: Cannot read file '%s'.", filename);
  65. error_shown = 1;
  66. }
  67. goto ipc;
  68. }
  69. if(procfile_lines(ff) >= 1 && procfile_linewords(ff, 0) >= 4) {
  70. lim->semvmx = SEMVMX;
  71. lim->semmsl = str2i(procfile_lineword(ff, 0, 0));
  72. lim->semmns = str2i(procfile_lineword(ff, 0, 1));
  73. lim->semopm = str2i(procfile_lineword(ff, 0, 2));
  74. lim->semmni = str2i(procfile_lineword(ff, 0, 3));
  75. return 0;
  76. }
  77. else {
  78. if(unlikely(!error_shown)) {
  79. error("IPC: Invalid content in file '%s'.", filename);
  80. error_shown = 1;
  81. }
  82. goto ipc;
  83. }
  84. ipc:
  85. // cannot do it from the file
  86. // query IPC
  87. {
  88. struct seminfo seminfo = {.semmni = 0};
  89. union semun arg = {.array = (ushort *) &seminfo};
  90. if(unlikely(semctl(0, 0, IPC_INFO, arg) < 0)) {
  91. error("IPC: Failed to read '%s' and request IPC_INFO with semctl().", filename);
  92. goto error;
  93. }
  94. lim->semvmx = SEMVMX;
  95. lim->semmni = seminfo.semmni;
  96. lim->semmsl = seminfo.semmsl;
  97. lim->semmns = seminfo.semmns;
  98. lim->semopm = seminfo.semopm;
  99. return 0;
  100. }
  101. error:
  102. lim->semvmx = 0;
  103. lim->semmni = 0;
  104. lim->semmsl = 0;
  105. lim->semmns = 0;
  106. lim->semopm = 0;
  107. return -1;
  108. }
  109. /*
  110. printf ("------ Semaphore Limits --------\n");
  111. printf ("max number of arrays = %d\n", limits.semmni);
  112. printf ("max semaphores per array = %d\n", limits.semmsl);
  113. printf ("max semaphores system wide = %d\n", limits.semmns);
  114. printf ("max ops per semop call = %d\n", limits.semopm);
  115. printf ("semaphore max value = %u\n", limits.semvmx);
  116. printf ("------ Semaphore Status --------\n");
  117. printf ("used arrays = %d\n", status.semusz);
  118. printf ("allocated semaphores = %d\n", status.semaem);
  119. */
  120. static inline int ipc_sem_get_status(struct ipc_status *st) {
  121. struct seminfo seminfo;
  122. union semun arg;
  123. arg.array = (ushort *) (void *) &seminfo;
  124. if(unlikely(semctl (0, 0, SEM_INFO, arg) < 0)) {
  125. /* kernel not configured for semaphores */
  126. static int error_shown = 0;
  127. if(unlikely(!error_shown)) {
  128. error("IPC: kernel is not configured for semaphores");
  129. error_shown = 1;
  130. }
  131. st->semusz = 0;
  132. st->semaem = 0;
  133. return -1;
  134. }
  135. st->semusz = seminfo.semusz;
  136. st->semaem = seminfo.semaem;
  137. return 0;
  138. }
  139. int do_ipc(int update_every, usec_t dt) {
  140. (void)dt;
  141. static int initialized = 0, read_limits_next = -1;
  142. static struct ipc_limits limits;
  143. static struct ipc_status status;
  144. static RRDVAR *arrays_max = NULL, *semaphores_max = NULL;
  145. static RRDSET *st_semaphores = NULL, *st_arrays = NULL;
  146. static RRDDIM *rd_semaphores = NULL, *rd_arrays = NULL;
  147. if(unlikely(!initialized)) {
  148. initialized = 1;
  149. // make sure it works
  150. if(ipc_sem_get_limits(&limits) == -1) {
  151. error("unable to fetch semaphore limits");
  152. return 1;
  153. }
  154. // make sure it works
  155. if(ipc_sem_get_status(&status) == -1) {
  156. error("unable to fetch semaphore statistics");
  157. return 1;
  158. }
  159. // create the charts
  160. if(unlikely(!st_semaphores)) {
  161. st_semaphores = rrdset_create_localhost(
  162. "system"
  163. , "ipc_semaphores"
  164. , NULL
  165. , "ipc semaphores"
  166. , NULL
  167. , "IPC Semaphores"
  168. , "semaphores"
  169. , PLUGIN_PROC_NAME
  170. , "ipc"
  171. , NETDATA_CHART_PRIO_SYSTEM_IPC_SEMAPHORES
  172. , localhost->rrd_update_every
  173. , RRDSET_TYPE_AREA
  174. );
  175. rd_semaphores = rrddim_add(st_semaphores, "semaphores", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  176. }
  177. if(unlikely(!st_arrays)) {
  178. st_arrays = rrdset_create_localhost(
  179. "system"
  180. , "ipc_semaphore_arrays"
  181. , NULL
  182. , "ipc semaphores"
  183. , NULL
  184. , "IPC Semaphore Arrays"
  185. , "arrays"
  186. , PLUGIN_PROC_NAME
  187. , "ipc"
  188. , NETDATA_CHART_PRIO_SYSTEM_IPC_SEM_ARRAYS
  189. , localhost->rrd_update_every
  190. , RRDSET_TYPE_AREA
  191. );
  192. rd_arrays = rrddim_add(st_arrays, "arrays", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  193. }
  194. // variables
  195. semaphores_max = rrdvar_custom_host_variable_create(localhost, "ipc_semaphores_max");
  196. arrays_max = rrdvar_custom_host_variable_create(localhost, "ipc_semaphores_arrays_max");
  197. }
  198. if(unlikely(read_limits_next < 0)) {
  199. if(unlikely(ipc_sem_get_limits(&limits) == -1)) {
  200. error("Unable to fetch semaphore limits.");
  201. }
  202. else {
  203. if(semaphores_max) rrdvar_custom_host_variable_set(localhost, semaphores_max, limits.semmns);
  204. if(arrays_max) rrdvar_custom_host_variable_set(localhost, arrays_max, limits.semmni);
  205. st_arrays->red = limits.semmni;
  206. st_semaphores->red = limits.semmns;
  207. read_limits_next = 60 / update_every;
  208. }
  209. }
  210. else
  211. read_limits_next--;
  212. if(unlikely(ipc_sem_get_status(&status) == -1)) {
  213. error("Unable to get semaphore statistics");
  214. return 0;
  215. }
  216. if(st_semaphores->counter_done) rrdset_next(st_semaphores);
  217. rrddim_set_by_pointer(st_semaphores, rd_semaphores, status.semaem);
  218. rrdset_done(st_semaphores);
  219. if(st_arrays->counter_done) rrdset_next(st_arrays);
  220. rrddim_set_by_pointer(st_arrays, rd_arrays, status.semusz);
  221. rrdset_done(st_arrays);
  222. return 0;
  223. }