root/trunk/src/apps/ucache/ucached.c @ 9178

Revision 9178, 20.8 KB (checked in by denton, 16 months ago)

ucached logging now prints to the ucached.log file. Also, ucached logging now uses gossip
instead of fprintf. The ucached_cmd executable can now call the ucache_info function to
generate info concerning the ucache.

Line 
1#include <stdio.h>
2#include <usrint.h>
3#include "ucached.h"
4
5/* FIFO  */
6static int readfd = 0;  /* Command File Descriptor */
7static int writefd = 0; /* Response File Descriptor */
8static char buffer[BUFF_SIZE]; /* For FIFO reads and writes */
9char buff[LOG_LEN];
10
11/* Time Structures For Log
12static time_t rawtime;
13static struct tm * timeinfo;
14*/
15
16/* Booleans */
17/* 1 if ucache is available for use */
18static unsigned char ucache_avail = 0;
19/* Set this to one if the ucache doesn't get created, and the
20 * create_ucache_shmem function should be run again.
21 */
22//static unsigned char tryAgain = 0;
23
24/* Use this global to determine if the atexit registered function (clean_up)
25 * needs to run. A child process is created to create shmem. This facilitates
26 * destruction later on, since segments hang around until their creator exits.
27 */
28pid_t pid = -1;
29
30/* Hung Lock Detection */
31time_t locked_time[BLOCKS_IN_CACHE+1];
32
33/* Forward Function Declarations */
34static int run_as_child(char c); /* Run as child of ucached */
35static int execute_cmd(char command);
36static int create_ucache_shmem(void);
37static int destroy_ucache_shmem(char dest_locks, char dest_ucache);
38static void clean_up(void);
39static int ucached_lockchk(void);
40
41void check_rc(int rc)
42{
43    memset(buffer, 0, BUFF_SIZE);
44    if(rc >= 0)
45    {
46        strcpy(buffer, "SUCCESS");
47    }
48    else
49    {
50        strcpy(buffer, "FAILURE: check log:" UCACHED_LOG_FILE);
51    }
52}
53
54/** Function to be run upon successful termination from an exit call */
55static void clean_up(void)
56{
57    int rc = 0;
58    /* Only the parent process should execute these lines.
59     * Must check the pid since the atexit function registered
60     * clean_up. This registration is passed on to any child
61     * processes forked off of the parent. We don't want to execute
62     * these lines when any of the children exit. Run only when parent.
63     */
64    if(pid !=0)
65    {
66        if(DEST_AT_EXIT)
67        {
68            rc = destroy_ucache_shmem(1, 1);
69        }
70        gossip_debug(GOSSIP_UCACHED_DEBUG,
71            "INFO: ucached exiting...PID=%d\n", pid);
72        rc = unlink(FIFO1);
73        rc = unlink(FIFO2);
74    }
75}
76
77/** Checks ucache lock shmem region for hung locks.
78 * Returns 0 when no hung locks are detected.
79 * Returns 1 when 1 or more hung locks are detected and all are gracefully
80 * handled.
81 * Returns -1 when 1 or more  hung locks are detected and couldn't
82 * be handled properly. (error)
83 */
84static int ucached_lockchk(void)
85{
86    int rc = 0;
87    int i;
88    for(i = 0; i < (BLOCKS_IN_CACHE + 1); i++)
89    {
90        ucache_lock_t * currlock = get_lock((uint16_t)i);
91        if(lock_trylock(currlock) == 0)
92        {
93            /* Lock wasn't held, so set the timer to zero for this lock */
94            locked_time[i] = 0;       
95        }
96        else
97        {
98            /* Lock was held, so calculate if lock timeout has occured */
99            /* First check to see if this lock's timer has been set at all */
100            if(!locked_time[i])
101            {
102                /* Timer for this lock isn't currently set */
103                time(&locked_time[i]);
104                continue;
105            }
106            else
107            {
108                /* Timer was previously set meaning the block had been locked*/
109                double time_diff = difftime(time((time_t *)NULL), locked_time[i]);
110                if((int)time_diff >= BLOCK_LOCK_TIMEOUT)
111                {
112                    /*
113                    gossip_debug(GOSSIP_UCACHED_DEBUG,
114                        "WARNING: HUNG LOCK DETECTED @ block index = %d\n", i);
115                    TODO: what to do with hung locks?
116                    rc = pick_lock(ucache_lock_t * currlock);
117                    if(rc == 1)
118                    {
119                        locked_time[i] = (time_t)0;
120                    }
121                    */
122                }
123            }
124        }
125    }
126
127    return rc;
128}
129
130
131/** Runs the command in a child process */
132static int run_as_child(char c)
133{
134    pid = fork();
135    int rc = 0;
136    /* Fork Error? */
137    if(pid < 0)
138    {
139        exit(EXIT_FAILURE);
140    }
141    /* Child Process */
142    else if(pid == 0)
143    {
144        rc = execute_cmd(c);
145        if(rc < 0)
146        {
147            exit(EXIT_FAILURE);
148        }
149        exit(EXIT_SUCCESS);
150    }
151    /* Parent Process */
152    else
153    {
154        wait(&rc);
155        if(WIFEXITED(rc))
156        {
157            if(WEXITSTATUS(rc) != 0)
158            {
159                return -1;
160            }                 
161        }
162    }
163    return rc;
164}
165
166
167static int execute_cmd(char cmd)
168{
169    int rc = 0;
170    switch(cmd)
171    {
172        /* Create the shared memory required by the ucache */
173        case 'c':
174            rc = create_ucache_shmem();
175            break;
176        /* Destroy the shared memory required by the ucache */
177        case 'd':
178            rc = destroy_ucache_shmem(1, 1);
179            break;
180        case 'i':
181            rc = ucache_info(stdout, "sav");
182            break;
183        /* Close Daemon */
184        case 'x':
185            writefd = open(FIFO2, O_WRONLY);
186            rc = write(writefd, "SUCCESS\tExiting ucached", BUFF_SIZE);
187            while(rc <= 0)
188            {
189                rc = write(writefd, "SUCCESS\tExiting ucached", BUFF_SIZE);
190            }
191            close(writefd);
192            exit(EXIT_SUCCESS);
193            break;
194        default:
195            strcpy(buffer, "FAILURE\tInvalid command character");
196            break;
197     }
198    return rc;
199}
200
201/* Returns -1 on failure, 1 on success */
202static int create_ucache_shmem(void)
203{
204    int rc = 0;
205
206    int old_locks_present = 0;
207
208    /* attempt setup of shmem region for locks (inlcude SYSV later? */
209    int id = SHM_ID1;
210    key_t key = ftok(KEY_FILE, id);
211    size_t size = LOCKS_SIZE;
212    int shmflg = SVSHM_MODE;
213    int lock_shmid = shmget(key, size, shmflg);
214
215    if(lock_shmid == -1)
216    {
217        gossip_debug(GOSSIP_UCACHED_DEBUG,
218            "INFO: shmget on lock_shmid returned -1 on first try\n");
219
220        /* Shared memory segment used for locks was not previosly created,
221         * so create it.
222         */
223        shmflg = shmflg | IPC_CREAT | IPC_EXCL;
224        lock_shmid = shmget(key, size, shmflg);
225        if(lock_shmid == -1)
226        {
227            gossip_debug(GOSSIP_UCACHED_DEBUG,
228                "ERROR: shmget (IPC_CREATE, IPC_EXCL)"
229                " on lock_shmid returned -1\n");
230            /* Couldn't create the required segment */
231            return -1;
232        }
233        else
234        {
235            gossip_debug(GOSSIP_UCACHED_DEBUG,
236            "INFO: shmget (using IPC_CREATE, IPC_EXCL)"
237                " on lock_shmid returned shmid = %d\n", lock_shmid);
238
239            /* Attach to shmem and initialize all the locks */
240            shmflg = 0;
241            /* ucache_locks is defined in src/client/usrint/ucache.h */
242            ucache_locks = shmat(lock_shmid, NULL, shmflg);
243            if (!ucache_locks)
244            {
245                gossip_debug(GOSSIP_UCACHED_DEBUG,
246                    "ERROR: shmat on lock_shmid returned NULL");
247                return -1;
248            }
249
250            int i;
251            /* Initialize Block Level Locks */
252            for(i = 0; i < (BLOCKS_IN_CACHE + 1); i++)
253            {
254                rc = lock_init(get_lock(i));
255                if (rc == -1)
256                {
257                    gossip_debug(GOSSIP_UCACHED_DEBUG,
258                        "ERROR: lock_init returned -1 @ lock index = %d\n", i);
259                    rc = -1;
260                }
261            }
262        }   
263    }
264    else
265    {
266        gossip_debug(GOSSIP_UCACHED_DEBUG,
267            "INFO: first shmget on lock_shmid found segment"
268            ": shmid = %d\n", lock_shmid);
269        old_locks_present = 1;
270        /* Shmem for locks was already created, so just attach to it */
271        shmflg = 0;
272        ucache_locks = shmat(lock_shmid, NULL, shmflg);
273        if (!ucache_locks)
274        {
275            gossip_debug(GOSSIP_UCACHED_DEBUG,
276                "ERROR: shmat on lock_shmid returned NULL\n");
277            return -1;
278        }   
279    }
280
281    /* At this point all the locks should be aquired and initialized.
282     * They could also be locked or unlocked */
283
284    /* Set the global lock point to the address of the last lock in the locks
285     * shmem segment. Then lock it.
286     */
287    ucache_lock = get_lock(BLOCKS_IN_CACHE);
288    lock_lock(ucache_lock);
289
290    gossip_debug(GOSSIP_UCACHED_DEBUG,
291        "INFO: lock segment successfully retrieved and global lock locked.\n");
292
293    /* Try to get/create the shmem required for the ucache */
294    id = SHM_ID2;
295    key = ftok(KEY_FILE, id);
296    size = CACHE_SIZE;
297    shmflg = SVSHM_MODE;
298    int ucache_shmid = shmget(key, size, shmflg);
299   
300    if(ucache_shmid == -1)
301    {
302        gossip_debug(GOSSIP_UCACHED_DEBUG,
303            "INFO: shmget on ucache_shmid returned -1 first try\n");
304
305        /* Remember if there was an old lock region detected */
306        if(old_locks_present)
307        {
308            gossip_debug(GOSSIP_UCACHED_DEBUG,
309                "INFO: old locks discovered, attempting destruction of old"
310                " locks and starting\n");
311
312            /* Destroy old lock region and start function over */
313            rc = shmctl(lock_shmid, IPC_RMID, (struct shmid_ds *) NULL);
314
315            /* Let this child process exit, since exiting is required to get
316             * the shmem segment to be completely removed. Try to create the
317             * shmem again later in another child process.
318             */
319            return -1;
320        }
321
322        /* Shared memory segmet used for ucache was not previosly created,
323         * so create it.
324         */
325        shmflg = shmflg | IPC_CREAT | IPC_EXCL;
326        ucache_shmid = shmget(key, size, shmflg);
327        if(ucache_shmid == -1)
328        {
329            /* Couldn't create the required segment */
330            gossip_debug(GOSSIP_UCACHED_DEBUG,
331                "ERROR: shmget (using IPC_CREATE, IPC_EXCL)"
332                " on ucache_shmid returned -1\n");
333
334            rc = -1;
335            goto errout;
336        }
337        else
338        {
339            gossip_debug(GOSSIP_UCACHED_DEBUG,
340                "INFO: shmget (using IPC_CREATE, IPC_EXCL)"
341                " on ucache_shmid returned shmid = %d\n", ucache_shmid);
342
343            /* Attach to the ucache shmem region */
344            shmflg = 0;
345            /* ucache is defined in src/client/usrint/ucache.h */
346            ucache = shmat(ucache_shmid, NULL, shmflg);
347            if (!ucache)
348            {
349                gossip_debug(GOSSIP_UCACHED_DEBUG,
350                    "ERROR: shmat on ucache_shmid returned NULL\n");
351                rc = -1;
352                goto errout;
353            }
354 
355            /* Initialize the file table */
356            rc = ucache_init_file_table(0);
357            if(rc != 0)
358            {
359                gossip_debug(GOSSIP_UCACHED_DEBUG,
360                    "ERROR: file table initialization failed\n");
361                /* Couldn't Initialize File Table */
362                rc = -1;
363                goto errout;
364            }
365        }
366    }
367    else
368    {
369        gossip_debug(GOSSIP_UCACHED_DEBUG,
370            "INFO: first shmget on ucache_shmid found segment"
371            ": shmid = %d\n", ucache_shmid);
372
373        /* Previously created ucache segment present. Need more info. */
374        /* See if marked for deletion, but has users attached still */
375        struct shmid_ds buf;     
376        int cmd = IPC_STAT;
377        rc = shmctl(ucache_shmid, cmd, &buf);
378        if(rc == -1)
379        {
380            gossip_debug(GOSSIP_UCACHED_DEBUG,
381                "ERROR: shmctl failed to IPC_STAT ucache_shmid\n");
382            goto errout;
383        }
384
385        /* Determine the count of processes attached to this shm segment */
386        char hasAttached = (buf.shm_nattch > 0);
387
388        /* Determine if the ucache shmem segment is marked for destruction*/
389        uint16_t currentMode = buf.shm_perm.mode;
390        char markedForDest = ((currentMode & SHM_DEST) == SHM_DEST);
391
392        if(markedForDest && hasAttached)
393        {
394            gossip_debug(GOSSIP_UCACHED_DEBUG,
395                "INFO: detected previous ucache shmem segment"
396                " marked for destruction that still has"
397                " one or more processes attached to it.\n");
398
399            shmflg = shmflg | IPC_CREAT; /* Note: CREAT w/o EXCL */
400            ucache_shmid = shmget(key, size, shmflg);
401            if(ucache_shmid == -1)
402            {
403                /* Couldn't create the required segment */
404                gossip_debug(GOSSIP_UCACHED_DEBUG,
405                    "ERROR: shmget (using IPC_CREAT && !EXCL)"
406                    " on ucache_shmid returned -1\n");
407                rc = -1;
408                goto errout;
409            }
410            /* Attach to the ucache shmem region */
411            shmflg = 0;
412            /* ucache is defined in src/client/usrint/ucache.h */
413            ucache = shmat(ucache_shmid, NULL, shmflg);
414            if (!ucache)
415            {
416                gossip_debug(GOSSIP_UCACHED_DEBUG,
417                    "ERROR: shmat on ucache_shmid returned NULL\n");
418                rc = -1;
419                goto errout;
420            }
421
422            /* Initialize the ftbl, and force the creation of it
423             * since the init boolean is set to 1.
424             */
425            rc = ucache_init_file_table(1);
426            if(rc != 0)
427            {
428                /* Couldn't Initialize File Table */
429                gossip_debug(GOSSIP_UCACHED_DEBUG,
430                    "ERROR: file table initialization failed\n");
431                rc = -1;   
432                goto errout;
433            }
434        }
435        else
436        {
437            /* Asume we will keep using the previously allocated segment */
438            /* Attach to the ucache shmem region */
439            shmflg = 0;
440            /* ucache is defined in src/client/usrint/ucache.h */
441            ucache = shmat(ucache_shmid, NULL, shmflg);
442            if (!ucache)
443            {
444                gossip_debug(GOSSIP_UCACHED_DEBUG,
445                    "ERROR: shmat on ucache_shmid returned NULL\n");
446                rc = -1;
447                goto errout;
448            }
449        }
450    }
451
452    lock_unlock(ucache_lock);
453    return 1;
454
455errout:
456    lock_unlock(ucache_lock);
457    return rc;
458}
459
460static int destroy_ucache_shmem(char dest_locks, char dest_ucache)
461{
462    int rc = 0;
463    /* Aquire the main lock then attempt to destroy the ucache shmem segment */
464    if(ucache_lock)
465    {
466        lock_lock(ucache_lock);
467    }
468
469    if(dest_ucache)
470    {
471        gossip_debug(GOSSIP_UCACHED_DEBUG,
472            "INFO: destroying ucache shmem\n");
473
474        /* Destroy shmem segment containing ucache */
475        int id = SHM_ID2;
476        key_t key = ftok(KEY_FILE, id);
477        int shmflg = SVSHM_MODE;
478        int ucache_shmid = shmget(key, 0, shmflg);
479        if(ucache_shmid == -1)
480        {
481            gossip_debug(GOSSIP_UCACHED_DEBUG,
482                "ERROR: shmget on ucache_shmid returned -1\n");
483            return -1;
484        }
485        rc = shmctl(ucache_shmid, IPC_RMID, (struct shmid_ds *) NULL);
486        if(rc == -1)
487        {
488            gossip_debug(GOSSIP_UCACHED_DEBUG,
489                "WARNING: ucache shmem_destroy: errno == %d\n", errno);
490        }
491    }
492
493    if(dest_locks)
494    {
495        gossip_debug(GOSSIP_UCACHED_DEBUG,
496            "INFO: destroying locks' shmem\n");
497
498        /* Destroy shmem segment containing locks */
499        int id = SHM_ID1;
500        key_t key = ftok(KEY_FILE, id);
501        int shmflg = SVSHM_MODE;
502        int lock_shmid = shmget(key, 0, shmflg);
503        if(lock_shmid == -1)
504        {
505            gossip_debug(GOSSIP_UCACHED_DEBUG,
506                "ERROR: shmget on lock_shmid returned -1\n");
507            return -1;
508        }
509        rc = shmctl(lock_shmid, IPC_RMID, (struct shmid_ds *) NULL);
510        if(rc == -1)
511        {
512            gossip_debug(GOSSIP_UCACHED_DEBUG,
513                "WARNING: ucache_locks shmem_destroy: errno == %d\n", errno);
514        }
515    }
516
517    gossip_debug(GOSSIP_UCACHED_DEBUG,
518        "INFO: both shmem segments marked for destruction.\n");
519
520    return rc;
521}
522
523/** This program should be run as root on startup to initialize the shared
524 * memory segments required by the user cache in PVFS.
525 */
526int main(int argc, char **argv)
527{
528    int rc = 0;
529    void *rp;
530
531    gossip_enable_file(UCACHED_LOG_FILE, "a");
532    uint64_t curr_mask;
533    int debug_on;
534    gossip_get_debug_mask(&debug_on, &curr_mask);
535    /* Enable the writing of the error message and write the message to file. */
536    gossip_set_debug_mask(1, GOSSIP_UCACHED_DEBUG);
537    //printf("now gossip_debug_mask = 0x%016lx\n", gossip_debug_mask);
538    /* restore previous gossip_debug_mask */
539    //gossip_set_debug_mask(debug_on, curr_mask);
540
541    memset(locked_time, 0, (sizeof(time_t) * (BLOCKS_IN_CACHE + 1)));
542
543    /* Direct output of ucache library, TODO: change this later */
544    if (!out)
545    {
546        out = stdout;
547    }
548
549    /* Continue ucached if it's the only ucached */
550    char ps_buff1[256];
551    char ps_buff2[256];
552    FILE *pipe = popen("ps -e | grep -w ucached", "r");
553
554    /* Should catch 1 line result, but not 2 */
555    rp = fgets(ps_buff1, 256, pipe);
556    rp = fgets(ps_buff2, 256, pipe); /* Should be zero if only 1 ucached */
557    if(rp == NULL)
558    {
559        /* Remove old FIFOs in case daemon was killed last time */
560        remove(FIFO1);
561        remove(FIFO2);
562    }
563    else
564    { 
565        puts("FAILURE: Daemon already started");
566        puts(ps_buff1);
567        puts(ps_buff2);
568        exit(EXIT_FAILURE);
569    }
570
571    /* Daemonize! */
572    rc = daemon(1, 1);
573
574    if(rc != 0)
575    {
576       
577        perror("daemon-izing failed");
578        exit(EXIT_FAILURE);
579    }
580
581    gossip_debug(GOSSIP_UCACHED_DEBUG,
582        "INFO: ucached started\n");
583
584    /* Start up with shared memory initialized */
585    if(CREATE_AT_START)
586    {
587        run_as_child('c');
588        atexit(clean_up);
589    }
590
591    /* Create 2 fifos */
592    rc = mkfifo(FIFO1, FILE_MODE);
593    if(rc != 0)
594    {
595        /* Couldn't create FIFO */
596        return -1;
597    }
598    rc = mkfifo(FIFO2, FILE_MODE);
599    if(rc != 0)
600    {
601        /* Couldn't create FIFO */
602        return -1;
603    }
604
605    while(1)
606    {
607        readfd = open(FIFO1, O_RDONLY | O_NONBLOCK);
608        struct pollfd fds[1];
609        fds[0].fd = readfd;
610        fds[0].events = POLLIN;
611
612        rc = poll(fds, 1, FIFO_TIMEOUT * 1000);
613
614        if(rc == -1)
615        {
616            gossip_debug(GOSSIP_UCACHED_DEBUG,
617                "ERROR: poll: errno = %d\n", errno);
618        }
619
620        if(fds[0].revents & POLLIN)
621        {
622            /* Data to be read */
623            memset(buffer, 0, BUFF_SIZE);
624            int count = read(readfd, buffer, BUFF_SIZE);
625            while(count <= 0)
626            {
627                if(count == -1)
628                {
629                    gossip_debug(GOSSIP_UCACHED_DEBUG,
630                        "ERROR: caught error while trying to read cmd: errno = %d\n",
631                        errno);
632                }
633                /* Try to read again */
634                count = read(readfd, buffer, BUFF_SIZE);
635            }
636            if(count > 0)
637            {
638                /* Data read into buffer*/
639                char c = buffer[0];
640                /* Valid Command? */
641                if(c == 'c' || c == 'd' || c == 'x' || 'i')
642                {
643                    gossip_debug(GOSSIP_UCACHED_DEBUG,
644                        "INFO: Command Received: %c\n", c);
645                    /* Run creation in child process */
646                    if(c == 'c')
647                    {
648                        run_as_child(c);
649                    }
650                    else
651                    {
652                        rc = execute_cmd(c);
653                    }
654                    check_rc(rc);                 
655                }
656                /* Invalid Command */
657                else
658                {
659                    gossip_debug(GOSSIP_UCACHED_DEBUG,
660                        "ERROR: Invalid Command Received: %c\n", c);
661                    rc = -1;
662                    check_rc(rc);
663                }
664
665                /* Data can be written, not guaranteed anything to write */
666                int responseLength = 0;
667                if((responseLength = strlen(buffer)) != 0)
668                {
669                    writefd = open(FIFO2, O_WRONLY);
670                    if(writefd == -1)
671                    {
672                        gossip_debug(GOSSIP_UCACHED_DEBUG,
673                           "ERROR: opening write FIFO: errno = %d\n", errno);
674                    }
675                    rc = write(writefd, buffer, BUFF_SIZE);
676                    while(rc <= 0)
677                    {
678                        rc = write(writefd, buffer, BUFF_SIZE);
679                    }
680                    memset(buffer, 0, BUFF_SIZE);
681                    close(writefd);
682                }
683            }
684        }
685        close(readfd);
686
687        if(ucache_avail)
688        {
689            /* Gather stats */
690            /* TODO: which stats? */
691
692            /* Write some dirty blocks out */
693            /* TODO: create function to do this or do i already have one that will suffice? */
694
695            /* Check for hung locks */
696            rc = ucached_lockchk();
697        }
698    }
699}
Note: See TracBrowser for help on using the browser.