File:  [Coherent Logic Development] / freem / src / shmmgr.c
Revision 1.2: download - view: text, annotated - select for diffs
Fri Feb 28 20:51:20 2025 UTC (7 months, 1 week ago) by snw
Branches: MAIN
CVS tags: HEAD
Fix a bunch of warnings

    1: /*
    2:  *                            *
    3:  *                           * *
    4:  *                          *   *
    5:  *                     ***************
    6:  *                      * *       * *
    7:  *                       *  MUMPS  *
    8:  *                      * *       * *
    9:  *                     ***************
   10:  *                          *   *
   11:  *                           * *
   12:  *                            *
   13:  *
   14:  *   shmmgr.c
   15:  *    shared memory manager
   16:  *
   17:  *  
   18:  *   Author: Serena Willis <jpw@coherent-logic.com>
   19:  *    Copyright (C) 1998 MUG Deutschland
   20:  *    Copyright (C) 2020 Coherent Logic Development LLC
   21:  *
   22:  *
   23:  *   This file is part of FreeM.
   24:  *
   25:  *   FreeM is free software: you can redistribute it and/or modify
   26:  *   it under the terms of the GNU Affero Public License as published by
   27:  *   the Free Software Foundation, either version 3 of the License, or
   28:  *   (at your option) any later version.
   29:  *
   30:  *   FreeM is distributed in the hope that it will be useful,
   31:  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   32:  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   33:  *   GNU Affero Public License for more details.
   34:  *
   35:  *   You should have received a copy of the GNU Affero Public License
   36:  *   along with FreeM.  If not, see <https://www.gnu.org/licenses/>.
   37:  *
   38:  **/
   39: 
   40: #include <stdlib.h>
   41: #include <stdio.h>
   42: #include <assert.h>
   43: #include <string.h>
   44: #include <unistd.h>
   45: #include <errno.h>
   46: #include <signal.h>
   47: 
   48: #include "shmmgr.h"
   49: #include "mpsdef.h"
   50: #include "locktab.h"
   51: 
   52: #include <sys/types.h>
   53: #include <sys/ipc.h>
   54: #include <sys/sem.h>
   55: 
   56: #if !defined(__OpenBSD__) && !defined(__APPLE__)
   57: union semun {
   58:     int              val;    /* Value for SETVAL */
   59:     struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
   60:     unsigned short  *array;  /* Array for GETALL, SETALL */
   61:     struct seminfo  *__buf;  /* Buffer for IPC_INFO
   62:                                 (Linux-specific) */
   63: };
   64: #endif
   65: 
   66: int semid_shm;
   67: extern int semid_locktab;
   68: extern int semid_jobtab;
   69: extern int semid_tp;
   70: extern int semid_symtab;
   71: 
   72: void shm_daemon_init(void);
   73: 
   74: shm_config_t *shm_config = (shm_config_t *) NULL;
   75: 
   76: short shm_init(const size_t seg_size)
   77: {
   78:     union semun arg;
   79:     size_t alloc_map_size;
   80:     long pg_size;
   81:     register int i;
   82:     key_t shm_sk;
   83: 
   84:     shm_sk = ftok (config_file, 5);    
   85:     pg_size = sysconf (_SC_PAGESIZE);
   86:     
   87:     shm_config = (shm_config_t *) malloc (sizeof (shm_config_t));
   88:     NULLPTRCHK(shm_config,"shm_init");    
   89:     
   90:     /* figure out how many pages we can fit in the segment, accounting for header size */
   91:     shm_config->pgct = (seg_size / pg_size) - sizeof (shm_hdr_t);
   92:     
   93:     /* how big will the alloc map be? */
   94:     alloc_map_size = shm_config->pgct * sizeof (shm_page_t);
   95:     
   96:     shm_config->segsiz = seg_size + alloc_map_size + pg_size;
   97:     shm_config->key = ftok (config_file, 1);
   98:     shm_config->pgsiz = pg_size;
   99:     
  100:     shm_config->seg_id = shmget (shm_config->key, shm_config->segsiz, 0660 | IPC_CREAT);
  101:     if (shm_config->seg_id == -1) {
  102:         if (errno == 22) {
  103:             fprintf (stderr, "shm_init:  cannot get shared memory segment of %ld bytes\r\n\r\n", (unsigned long) shm_config->segsiz);
  104:             fprintf (stderr, "You may need to tune your kernel parameters, or manually set a smaller shared memory segment size in both the FreeM daemon and each interpreter process by using the `-S` command-line flag.\r\n\r\nPlease refer to the FreeM Platform Notes for your operating system for details.\r\n\r\n"); 
  105:         }
  106:         return SHMS_GET_ERR;
  107:     }
  108: 
  109: #if !defined(__arm__)    
  110:     shm_config->dta = shmat (shm_config->seg_id, NULL, 0);
  111: #else
  112:     shm_config->dta = shmat (shm_config->seg_id, (void *) 0x1000000, 0);
  113: #endif
  114:     
  115:     if (shm_config->dta == (void *) -1) {     
  116:         return SHMS_ATTACH_ERR;
  117:     }
  118:     /* view the first sizeof (shm_hdr_t) bytes of the data area as an shm_hdr_t */
  119:     shm_config->hdr = (shm_hdr_t *) shm_config->dta;
  120:     
  121:     if (shm_config->hdr->magic != shm_config->key) {
  122: 
  123:         /* the shm segment is brand new */
  124:         first_process = TRUE;
  125: 
  126:         shm_daemon_init ();
  127:         
  128:     }
  129:     else {
  130: 
  131:         /* this shared mem segment was initialized before */
  132:         int daemon_chk;
  133: 
  134:         /* check if the daemon recorded in the header is actually running */
  135:         daemon_chk = kill (shm_config->hdr->first_process, 0);
  136: 
  137:         if (daemon_chk == -1 && errno == ESRCH) {
  138: 
  139:             fprintf (stderr, "shm_init:  recovering from crashed daemon pid %d\r\n", shm_config->hdr->first_process);
  140: 
  141:             first_process = TRUE;
  142: 
  143:             shm_daemon_init ();
  144: 
  145:         }
  146:         else {
  147:         
  148: 
  149:             first_process = FALSE;
  150: 
  151:             semid_shm = semget (shm_sk, 1, 0);
  152:             if (semid_shm == -1) {
  153:                 fprintf (stderr, "shm_init:  could not attach to shared memory semaphore\r\n");
  154:                 exit (1);
  155:             }
  156:             
  157:             /* we are NOT the initial process. if addresses don't match, re-attach! */
  158:             /* (again, borrowed from RSM) */
  159:             if (shm_config->hdr->shmad != shm_config->dta) {
  160:                 
  161:                 /* grab the pointers we need */
  162:                 void *old_addr = shm_config->dta;
  163:                 void *new_addr = shm_config->hdr->shmad;
  164:                 
  165:                 /* detach and reattach */
  166:                 if (shmdt (old_addr) == -1) {
  167:                     fprintf (stderr, "shm_init:  detach failed during detach/reattach [shmdt error %s]\r\n", strerror (errno));
  168:                     exit (1);
  169:                 }
  170:                 
  171:                 shm_config->dta = shmat (shm_config->seg_id, new_addr, 0);
  172: 
  173:                 if (shm_config->dta == (void *) -1) {
  174:                     fprintf (stderr, "shm_init:  fatal error attaching shared memory segment [shmat error '%s']\r\n", strerror (errno));
  175:                     exit (1);
  176:                 }
  177:                 
  178:                 shm_config->hdr = (shm_hdr_t *) shm_config->dta;
  179: 
  180:                 /* allocator buffer at the next page-aligned address after the header and allocation map */
  181:                 shm_config->buf = SHMALIGN(shm_config->dta + (sizeof (shm_hdr_t) * shm_config->pgct));
  182:             }
  183:             else {
  184:                 shm_config->buf = SHMALIGN(shm_config->dta + (sizeof (shm_hdr_t) * shm_config->pgct));
  185:             }
  186:         
  187:         }
  188: 
  189:     }
  190:     
  191:     locktab_init ();
  192:     
  193:     assert(shm_address_to_page_num(shm_page_num_to_address(20)) == 20);
  194:     
  195: 
  196:     return TRUE;
  197: }
  198: 
  199: void shm_daemon_init(void)
  200: {
  201:     union semun arg;
  202:     size_t alloc_map_size;
  203:     key_t shm_sk;
  204:     register int i;
  205: 
  206:     shm_sk = ftok (config_file, 5);
  207:     
  208:     semid_shm = semget (shm_sk, 1, 0660 | IPC_CREAT);
  209:     if (semid_shm == -1) {
  210:         fprintf (stderr, "shm_init:  failed to create shared memory semaphore\r\n");
  211:         exit (1);
  212:     }
  213:     
  214:     arg.val = 1;
  215:     if (semctl (semid_shm, 0, SETVAL, arg) == -1) {
  216:         fprintf (stderr, "shm_init:  failed to initialize shared memory semaphore\r\n");
  217:         exit (1);
  218:     }
  219:     
  220:     /* zero out the segment */
  221:     memset (shm_config->dta, 0, shm_config->segsiz);
  222:     
  223:     /* we are the process that created the segment: initialize everything */
  224:     shm_config->hdr->magic = shm_config->key;
  225:     shm_config->hdr->first_process = pid;
  226:     
  227:     /* store the address we got into the shm_hdr (borrowed from RSM) */
  228:     shm_config->hdr->shmad = shm_config->dta;
  229:     shm_config->hdr->maintenance_mode = 0;
  230:     
  231:     /* alloc_map comes after the header */
  232: /*
  233:     shm_config->alloc_map = (shm_page_t *) (shm_config->dta + sizeof (shm_hdr_t));
  234: */    
  235:     shm_config->buf = SHMALIGN(shm_config->dta + (sizeof (shm_hdr_t) * shm_config->pgct));
  236:     printf ("shm_daemon_init:  allocator buffer aligned at %p (system page size %ld)\r\n", shm_config->buf, sysconf (_SC_PAGESIZE));
  237:     
  238:     for (i = 0; i < shm_config->pgct; i++) {
  239:         shm_config->hdr->alloc_map[i].is_first = FALSE;
  240:         shm_config->hdr->alloc_map[i].is_last = FALSE;
  241:         shm_config->hdr->alloc_map[i].pg_state = PG_FREE;
  242:     }
  243:     
  244: }
  245: 
  246: short shm_exit(void)
  247: {
  248:     int res;
  249:     union semun arg;
  250: 
  251:     res = shmdt (shm_config->dta);
  252: 
  253:     if (res == -1) {
  254:         fprintf (stderr, "shm_exit:  failure in shmdt()\r\n");
  255:         return FALSE;
  256:     }
  257:     
  258:     if (first_process) {
  259: 
  260:         res = shmctl (shm_config->seg_id, IPC_RMID, 0);
  261: 
  262:         if (res == -1) {
  263:             fprintf (stderr, "shm_exit:  failure in shmctl()\r\n");
  264:             return FALSE;
  265:         }
  266: 
  267:         semctl (semid_shm, 0, IPC_RMID, arg);
  268:         semctl (semid_locktab, 0, IPC_RMID, arg);
  269:         semctl (semid_jobtab, 0, IPC_RMID, arg);
  270:         semctl (semid_tp, 0, IPC_RMID, arg);
  271:         semctl (semid_symtab, 0, IPC_RMID, arg);
  272:         
  273:     }
  274: 
  275:     return TRUE;
  276:     
  277: }
  278: 
  279: short shm_get_sem(void)
  280: {
  281:     
  282:     int tries;
  283:     struct sembuf s = {0, -1, 0};
  284:     
  285:     for (tries = 0; tries < 3; tries++) {
  286: 
  287:         if (semop (semid_shm, &s, 1) != -1) {
  288:             return TRUE;
  289:         }
  290: 
  291:         sleep (1);
  292: 
  293:     }
  294: 
  295:     return FALSE;
  296:     
  297: }
  298: 
  299: short shm_release_sem(void)
  300: {
  301:     struct sembuf s = {0, 1, 0};
  302: 
  303:     if (semop (semid_shm, &s, 1) != -1) {
  304:         return TRUE;
  305:     }
  306: 
  307:     return FALSE;
  308: }
  309: 
  310: void shm_set_maintenance_mode (const short maintenance_mode)
  311: {
  312:     if (shm_get_sem () == TRUE) {
  313:         shm_config->hdr->maintenance_mode = maintenance_mode;
  314: 
  315:         shm_release_sem ();
  316:     }
  317: }
  318: 
  319: shm_page_t *shm_get_alloc_map_entry(const int page_number)
  320: {
  321:     return &(shm_config->hdr->alloc_map[page_number]);
  322: }
  323: 
  324: void *shm_page_num_to_address(const int page_num)
  325: {
  326:     return (void *) shm_config->buf + (shm_config->pgsiz * page_num);
  327: }
  328: 
  329: int shm_address_to_page_num(const void *address)
  330: {
  331:     unsigned long val = (unsigned long) address - (unsigned long) shm_config->buf;
  332:     unsigned long new_val = val / shm_config->pgsiz;
  333:     
  334:     return (int) new_val;
  335: }
  336: 
  337: void *shm_alloc_pages(const int page_count)
  338: {
  339:     
  340:     register int i;
  341:     register int j;
  342: 
  343:     int candidate_page = 0;
  344:     int free_pages_gotten = 0;
  345:     
  346:     shm_page_t *pg;
  347: 
  348:     if (shm_get_sem () == FALSE) {
  349:         fprintf (stderr, "shm_alloc_pages:  could not get exclusive access to shared memory\r\n");
  350:         exit (1);
  351:     }
  352: 
  353:     
  354:     for (i = 0; i < shm_config->pgct; i++) {
  355: 
  356:         pg = shm_get_alloc_map_entry (i);
  357:         NULLPTRCHK(pg,"shm_alloc_pages");
  358: 
  359:         free_pages_gotten = 0;
  360:         
  361:         if (pg->pg_state == PG_FREE) {
  362: 
  363:             candidate_page = i;
  364:             
  365:             for (j = i; ((j < (i + page_count)) && (j < shm_config->pgct)); j++) {
  366:                 pg = shm_get_alloc_map_entry (j);
  367: 
  368:                 if (pg->pg_state == PG_FREE) free_pages_gotten++;
  369:                 
  370:             }
  371: 
  372:             if (free_pages_gotten == page_count) {
  373: 
  374:                 for (j = candidate_page; j < (candidate_page + page_count); j++) {
  375:                     pg = shm_get_alloc_map_entry (j);
  376: 
  377:                     pg->pg_state = PG_ALLOC;
  378:                     pg->pid = pid;
  379:                     
  380:                     if (j == candidate_page) {
  381:                         pg->is_first = TRUE;
  382:                     }
  383: 
  384:                     if (j == candidate_page + (page_count - 1)) {
  385:                         pg->is_last = TRUE;
  386:                     }
  387:                     
  388:                 }
  389: 
  390:                 shm_release_sem ();
  391:                 
  392:                 return (void *) shm_config->buf + (shm_config->pgsiz * candidate_page);
  393:                 
  394:             }
  395:             
  396:         }
  397: 
  398:     }
  399: 
  400:     shm_release_sem ();
  401:     
  402:     return (void *) NULL;
  403:     
  404: }
  405: 
  406: 
  407: void *shm_alloc(const size_t bytes)
  408: {
  409:     int pages_needed = bytes / shm_config->pgsiz;
  410:     float extra = bytes % shm_config->pgsiz;
  411: 
  412:     if (extra > 0) {
  413:         pages_needed++;
  414:     }
  415: 
  416:     return shm_alloc_pages (pages_needed);
  417: }
  418: 
  419: void shm_free_page(const int page_number)
  420: {
  421:     register int i;
  422:     shm_page_t *a = shm_get_alloc_map_entry (page_number);
  423: 
  424:     if (a->is_first == FALSE) {
  425:         fprintf (stderr, "shm_free_page:  attempt to free page in the middle of allocation chain\r\n");
  426:         return;
  427:     }
  428: 
  429:     if (a->pg_state == PG_FREE) {
  430:         fprintf (stderr, "shm_free_page:  double free attempted in page %d\r\n", page_number);
  431:         exit (1);
  432:     }
  433:     
  434:     if (shm_get_sem () == FALSE) {
  435:         fprintf (stderr, "shm_free_page:  could not get exclusive access to shared memory\r\n");
  436:         exit (1);
  437:     }
  438: 
  439:     
  440:     for (i = page_number; i < shm_config->pgct; i++) {
  441: 
  442:         a = shm_get_alloc_map_entry (i);        
  443:         
  444:         if (a->is_last) {
  445:             a->is_first = FALSE;
  446:             a->pg_state = PG_FREE;
  447:             a->pid = 0;
  448:             a->is_last = FALSE;
  449: 
  450:             shm_release_sem ();
  451:             
  452:             return;
  453:         }
  454:         else {
  455:             a->is_first = FALSE;
  456:             a->pg_state = PG_FREE;
  457:             a->pid = 0;
  458:             a->is_last = FALSE;
  459:         }
  460:         
  461:     }
  462: 
  463:     shm_release_sem ();
  464:    
  465: }
  466: 
  467: void shm_free(const void *addr)
  468: {
  469:     shm_free_page (shm_address_to_page_num (addr));
  470: }
  471: 
  472: void shm_dump(void)
  473: {
  474: 
  475:     printf ("SHARED MEMORY CONFIGURATION\r\n");
  476:     printf ("  pgsiz                   %ld\r\n", (unsigned long) shm_config->pgsiz);
  477:     printf ("  pgct                    %d\r\n", shm_config->pgct);
  478:     printf ("  key                     %ld\r\n", shm_config->key);
  479:     printf ("  segid                   %d\r\n", shm_config->seg_id);
  480:     printf ("  sizeof shm_page_t       %ld\r\n", (long) sizeof (shm_page_t));
  481:     printf ("  segsiz                  %ld\r\n", (long) shm_config->segsiz);
  482:     printf ("  shm address             %p\r\n", shm_config->dta);
  483:     printf ("  alloc_map size          %ld\r\n", (unsigned long) sizeof (shm_page_t) * shm_config->pgct);
  484:     printf ("  buf address             %p\r\n", shm_config->buf);
  485: }
  486: 
  487: void shm_dump_pages(void)
  488: {
  489: 
  490:     register int i;
  491:     shm_page_t *p;
  492: 
  493:     printf ("%-10s%-10s%-10s%-10s%-10s\r\n", "PAGE", "PID", "BMHEAD", "BMTAIL", "STATE");
  494:     
  495:     for (i = 0; i < shm_config->pgct; i++) {
  496: 
  497:         p = shm_get_alloc_map_entry (i);
  498: 
  499:         printf ("%-10d%-10d%-10s%-10s%-10s\r\n",
  500:                 i,
  501:                 p->pid,
  502:                 (p->is_first == TRUE) ? "Y" : "N",
  503:                 (p->is_last == TRUE) ? "Y" : "N",
  504:                 (p->pg_state == PG_FREE) ? "PG_FREE" : "PG_ALLOC");
  505:         
  506:     }
  507: 
  508: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>