Annotation of tme/host/posix/posix-disk.c, revision 1.1.1.3

1.1.1.3 ! root        1: /* $Id: posix-disk.c,v 1.6 2010/06/05 14:28:57 fredette Exp $ */
1.1       root        2: 
                      3: /* host/posix/posix-disk.c - implementation of disks on a POSIX system: */
                      4: 
                      5: /*
                      6:  * Copyright (c) 2003 Matt Fredette
                      7:  * All rights reserved.
                      8:  *
                      9:  * Redistribution and use in source and binary forms, with or without
                     10:  * modification, are permitted provided that the following conditions
                     11:  * are met:
                     12:  * 1. Redistributions of source code must retain the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer.
                     14:  * 2. Redistributions in binary form must reproduce the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer in the
                     16:  *    documentation and/or other materials provided with the distribution.
                     17:  * 3. All advertising materials mentioning features or use of this software
                     18:  *    must display the following acknowledgement:
                     19:  *      This product includes software developed by Matt Fredette.
                     20:  * 4. The name of the author may not be used to endorse or promote products
                     21:  *    derived from this software without specific prior written permission.
                     22:  *
                     23:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     24:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
                     25:  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
                     26:  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
                     27:  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
                     28:  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
                     29:  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     30:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
                     31:  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
                     32:  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     33:  * POSSIBILITY OF SUCH DAMAGE.
                     34:  */
                     35: 
1.1.1.3 ! root       36: /* this might enable large-file support: */
        !            37: #define _FILE_OFFSET_BITS 64
        !            38: 
1.1       root       39: #include <tme/common.h>
1.1.1.3 ! root       40: _TME_RCSID("$Id: posix-disk.c,v 1.6 2010/06/05 14:28:57 fredette Exp $");
1.1       root       41: 
                     42: /* includes: */
                     43: #include <tme/generic/disk.h>
                     44: #include <tme/generic/bus.h>
                     45: #include <fcntl.h>
                     46: #include <stdlib.h>
                     47: #include <strings.h>
                     48: #include <sys/stat.h>
                     49: #include <sys/uio.h>
                     50: #ifdef HAVE_MMAP
                     51: #include <sys/types.h>
                     52: #include <sys/mman.h>
                     53: #endif /* HAVE_MMAP */
                     54: #ifdef HAVE_STDARG_H
                     55: #include <stdarg.h>
                     56: #else  /* HAVE_STDARG_H */
                     57: #include <varargs.h>
                     58: #endif /* HAVE_STDARG_H */
                     59: 
                     60: /* macros: */
                     61: 
                     62: /* the maximum block size: */
                     63: #define TME_POSIX_DISK_BLOCK_SIZE_MAX  (16384)
                     64: 
                     65: /* disk flags: */
                     66: #define TME_POSIX_DISK_FLAG_RO         TME_BIT(0)
                     67: 
                     68: /* buffer flags: */
                     69: #define TME_POSIX_DISK_BUFFER_READABLE TME_BIT(0)
                     70: #define TME_POSIX_DISK_BUFFER_DIRTY    TME_BIT(1)
                     71: #define TME_POSIX_DISK_BUFFER_MMAPPED  TME_BIT(2)
                     72: 
                     73: /* default buffer parameters: */
                     74: #define TME_POSIX_DISK_BUFFER_DEFAULT_COUNT    (16)
                     75: #define TME_POSIX_DISK_BUFFER_DEFAULT_AGG_PRE  (128UL * 1024UL)
                     76: #define TME_POSIX_DISK_BUFFER_DEFAULT_AGG_POST (1UL * 1024UL * 1024UL)
                     77: 
                     78: /* types: */
                     79: 
                     80: /* a posix disk buffer: */
                     81: struct tme_posix_disk_buffer {
                     82: 
                     83:   /* buffers are kept on a doubly linked list: */
                     84:   struct tme_posix_disk_buffer *tme_posix_disk_buffer_next;
                     85:   struct tme_posix_disk_buffer **tme_posix_disk_buffer_prev;
                     86: 
                     87:   /* buffer flags: */
                     88:   int tme_posix_disk_buffer_flags;
                     89: 
                     90:   /* the file position, size, and data of this buffer.  a size of zero
                     91:      means this buffer is free: */
                     92: 
                     93:   /* we're paranoid about autoconf's ability to actually determine if
                     94:      off_t and size_t are available, and it's possible that a system
                     95:      that predates off_t and size_t may use other types to achieve
                     96:      large file support anyways, that autoconf's default off_t = long
                     97:      and size_t = unsigned definitions may not match.  the bizarre
                     98:      solution is to declare structs with appropriately-typed members,
                     99:      and then use those members: */
                    100:   struct stat _tme_posix_disk_buffer_stat;
                    101:   struct iovec _tme_posix_disk_buffer_iov;
                    102: #define tme_posix_disk_buffer_pos _tme_posix_disk_buffer_stat.st_size
                    103: #define tme_posix_disk_buffer_size _tme_posix_disk_buffer_iov.iov_len
                    104: #define tme_posix_disk_buffer_data _tme_posix_disk_buffer_iov.iov_base
                    105: };
                    106: 
                    107: /* a posix disk: */
                    108: struct tme_posix_disk {
                    109: 
                    110:   /* backpointer to our element: */
                    111:   struct tme_element *tme_posix_disk_element;
                    112: 
                    113:   /* our mutex: */
                    114:   tme_mutex_t tme_posix_disk_mutex;
                    115: 
                    116:   /* our flags: */
                    117:   int tme_posix_disk_flags;
                    118: 
                    119:   /* the file descriptor: */
                    120:   int tme_posix_disk_fd;
                    121: 
                    122:   /* the stat buffer: */
                    123:   struct stat tme_posix_disk_stat;
                    124: 
                    125:   /* our connection: */
                    126:   struct tme_disk_connection *tme_posix_disk_connection;
                    127: 
                    128:   /* our disk buffers.  the most-recently-used buffer
                    129:      is at the front of the list: */
                    130:   struct tme_posix_disk_buffer *tme_posix_disk_buffers;
                    131: 
                    132:   /* how much we aggregate behind and ahead when we will
                    133:      a new buffer: */
                    134:   struct stat _tme_posix_disk_stat0;
                    135:   struct stat _tme_posix_disk_stat1;
                    136: #define tme_posix_disk_buffer_agg_pre _tme_posix_disk_stat0.st_size
                    137: #define tme_posix_disk_buffer_agg_post _tme_posix_disk_stat1.st_size
                    138: };
                    139: 
1.1.1.3 ! root      140: /* this frees a buffer: */
        !           141: static void
        !           142: _tme_posix_disk_buffer_free(struct tme_posix_disk *posix_disk,
        !           143:                            struct tme_posix_disk_buffer *buffer)
        !           144: {
        !           145:   int rc;
        !           146:   struct iovec iovecs[1];
        !           147: #define ssize iovecs[0].iov_len
        !           148: 
        !           149:   /* if this buffer is mmapped: */
        !           150:   if (buffer->tme_posix_disk_buffer_flags 
        !           151:       & TME_POSIX_DISK_BUFFER_MMAPPED) {
        !           152: #ifdef HAVE_MMAP
        !           153:        
        !           154:     /* munmap the buffer: */
        !           155:     rc = munmap(buffer->tme_posix_disk_buffer_data,
        !           156:                buffer->tme_posix_disk_buffer_size);
        !           157:     assert (rc == 0);
        !           158:        
        !           159:     /* free this buffer: */
        !           160:     buffer->tme_posix_disk_buffer_size = 0;
        !           161: #endif /* HAVE_MMAP */
        !           162:   }
        !           163: 
        !           164:   /* otherwise, this buffer is not mmapped: */
        !           165:   else {
        !           166: 
        !           167:     /* if this buffer is dirty, we need to write it out: */
        !           168:     if (buffer->tme_posix_disk_buffer_flags 
        !           169:        & TME_POSIX_DISK_BUFFER_DIRTY) {
        !           170:            
        !           171:       /* seek to the buffer's position: */
        !           172:       rc = (lseek(posix_disk->tme_posix_disk_fd,
        !           173:                  buffer->tme_posix_disk_buffer_pos,
        !           174:                  SEEK_SET) < 0);
        !           175:       assert (rc == 0);
        !           176:            
        !           177:       /* write out the buffer: */
        !           178:       ssize = write(posix_disk->tme_posix_disk_fd,
        !           179:                    buffer->tme_posix_disk_buffer_data,
        !           180:                    buffer->tme_posix_disk_buffer_size);
        !           181:       assert (ssize == buffer->tme_posix_disk_buffer_size);
        !           182:     }
        !           183: 
        !           184:     /* free this buffer: */
        !           185:     buffer->tme_posix_disk_buffer_flags = 0;
        !           186:   }
        !           187: #undef ssize
        !           188: }
        !           189: 
1.1       root      190: /* this gets a buffer: */
                    191: static int
                    192: _tme_posix_disk_buffer_get(struct tme_posix_disk *posix_disk,
                    193:                           const union tme_value64 *_pos,
                    194:                           unsigned long _size,
                    195:                           int readable,
                    196:                           tme_uint8_t **_buffer)
                    197: {
                    198:   struct stat statbufs[3];
                    199: #define pos_least statbufs[0].st_size
                    200: #define pos_most statbufs[1].st_size
                    201: #define pos_last statbufs[2].st_size
                    202:   struct iovec iovecs[3];
                    203: #define data iovecs[0].iov_base
                    204: #define size iovecs[0].iov_len
                    205: #define size_agg iovecs[1].iov_len
                    206: #define ssize iovecs[2].iov_len
                    207:   unsigned long agg_pre, agg_post;
                    208:   struct tme_posix_disk_buffer *buffer;
                    209:   struct tme_posix_disk_buffer *buffer_free_nosize;
                    210:   struct tme_posix_disk_buffer *buffer_free_sized;
                    211:   int have_least, have_most;
                    212:   int rc;
                    213: 
                    214:   /* form the size, and least and most positions: */
                    215:   size = _size;
                    216:   assert (size > 0);
                    217: #ifdef TME_HAVE_INT64_T
                    218:   pos_least = _pos->tme_value64_int;
                    219: #else  /* !TME_HAVE_INT64_T */
                    220:   pos_least = _pos->tme_value64_int32_hi;
                    221:   pos_least
                    222:     = ((pos_least << 32)
                    223:        | _pos->tme_value64_int32_lo);
                    224: #endif /* !TME_HAVE_INT64_T */
                    225:   pos_most = (pos_least + size) - 1;
                    226: 
                    227:   /* if we have to fill a new buffer, decide from where and how much
                    228:      we are going to fill: */
                    229:   agg_pre
                    230:     = TME_MIN(pos_least,
                    231:              posix_disk->tme_posix_disk_buffer_agg_pre);
                    232:   agg_pre
                    233:     += ((pos_least - agg_pre)
                    234:        & (posix_disk->tme_posix_disk_stat.st_blksize - 1));
                    235:   agg_post
                    236:     = posix_disk->tme_posix_disk_buffer_agg_post;
                    237:   size_agg
                    238:     = (((agg_pre + size) + agg_post
                    239:        + (posix_disk->tme_posix_disk_stat.st_blksize - 1))
                    240:        & ~(posix_disk->tme_posix_disk_stat.st_blksize - 1));
                    241:   agg_post = (size_agg - (agg_pre + size));
                    242: 
                    243:   /* start with no best free buffers: */
                    244:   buffer_free_nosize = NULL;
                    245:   buffer_free_sized = NULL;
                    246: 
                    247:   /* walk all of the buffers: */
                    248:   for (buffer = posix_disk->tme_posix_disk_buffers;
                    249:        buffer != NULL; ) {
                    250: 
                    251:     /* a buffer with no size is free: */
                    252:     if (buffer->tme_posix_disk_buffer_size == 0) {
                    253:       
                    254:       /* remember this nosize free buffer and continue: */
                    255:       buffer_free_nosize = buffer;
                    256:       buffer = buffer->tme_posix_disk_buffer_next;
                    257:       continue;
                    258:     }
                    259: 
                    260:     /* a buffer with some size, but no flags, is also free: */
                    261:     else if (buffer->tme_posix_disk_buffer_flags == 0) {
                    262: 
                    263:       /* take this buffer as our best sized free buffer, */
                    264: 
                    265:       /* if we have no best sized free buffer yet: */
                    266:       if (buffer_free_sized == NULL
                    267: 
                    268:          || ((buffer->tme_posix_disk_buffer_size
                    269:               >= buffer_free_sized->tme_posix_disk_buffer_size)
                    270: 
                    271:              /* if this buffer is bigger than our best sized free
                    272:                 buffer, and the best sized free buffer is smaller
                    273:                 than the new-fill aggregate buffer size: */
                    274:              ? (buffer_free_sized->tme_posix_disk_buffer_size
                    275:                 < size_agg)
                    276: 
                    277:              /* if this buffer is smaller than our best sized
                    278:                 free buffer, but it is still at least as big as
                    279:                 the new-fill aggregate buffer size: */
                    280:              : (buffer->tme_posix_disk_buffer_size
                    281:                 >= size_agg))) {
                    282:        buffer_free_sized = buffer;
                    283:       }
                    284: 
                    285:       /* continue: */
                    286:       buffer = buffer->tme_posix_disk_buffer_next;
                    287:       continue;
                    288:     }
                    289: 
                    290:     /* calculate the last position in this buffer: */
                    291:     pos_last = ((buffer->tme_posix_disk_buffer_pos
                    292:                 + buffer->tme_posix_disk_buffer_size)
                    293:                - 1);
                    294: 
                    295:     /* see if this buffer contains the least position we want: */
                    296:     have_least
                    297:       = (buffer->tme_posix_disk_buffer_pos <= pos_least
                    298:         && pos_least <= pos_last);
                    299: 
                    300:     /* see if this buffer contains the most position we want: */
                    301:     have_most
                    302:       = (buffer->tme_posix_disk_buffer_pos <= pos_most
                    303:         && pos_most <= pos_last);
                    304: 
                    305:     /* if this buffer covers all of the positions we want, and
                    306:        is readable or we don't need a readable buffer, stop: */
                    307:     if (have_least
                    308:        && have_most
                    309:        && ((buffer->tme_posix_disk_buffer_flags
                    310:             & TME_POSIX_DISK_BUFFER_READABLE)
                    311:            || !readable)) {
                    312:       break;
                    313:     }
                    314:     
                    315:     /* otherwise, if this buffer covers any of the positions we want,
                    316:        or if this is the last buffer on the list and we don't have any
                    317:        best free buffer, we need to free this buffer: */
                    318:     else if (have_least
                    319:             || have_most
                    320:             || (buffer->tme_posix_disk_buffer_next == NULL
                    321:                 && buffer_free_nosize == NULL
                    322:                 && buffer_free_sized == NULL)) {
                    323: 
1.1.1.3 ! root      324:       /* free this buffer: */
        !           325:       _tme_posix_disk_buffer_free(posix_disk,
        !           326:                                  buffer);
1.1       root      327:       
                    328:       /* continue without updating buffer, so that this now-free
                    329:         buffer is revisited, to possibly become a best free buffer: */
                    330:     }
                    331: 
                    332:     /* otherwise, this is a non-free buffer that doesn't
                    333:        cover any of the positions we want.  just continue: */
                    334:     else {
                    335:       buffer = buffer->tme_posix_disk_buffer_next;
                    336:     }
                    337:   }
                    338: 
                    339:   /* if we didn't find an applicable buffer: */
                    340:   if (buffer == NULL) {
                    341:     
                    342:     /* we must have some free buffer: */
                    343:     assert (buffer_free_nosize != NULL
                    344:            || buffer_free_sized != NULL);
                    345: 
                    346: #ifdef HAVE_MMAP
                    347: 
                    348:     /* try to mmap this region.  if the map fails with more read-ahead
                    349:        than is needed to meet the block size, try the map one more
                    350:        time with just that needed read-ahead: */
                    351:     data = mmap(NULL,
                    352:                size_agg,
                    353:                PROT_READ
                    354:                | ((posix_disk->tme_posix_disk_flags
                    355:                    & TME_POSIX_DISK_FLAG_RO)
                    356:                   ? 0
                    357:                   : PROT_WRITE),
                    358:                MAP_SHARED,
                    359:                posix_disk->tme_posix_disk_fd,
                    360:                (pos_least
                    361:                 - agg_pre));
                    362:     if (data == MAP_FAILED) {
                    363:       size_agg
                    364:        = (((agg_pre + size)
                    365:            + (posix_disk->tme_posix_disk_stat.st_blksize - 1))
                    366:           & ~(posix_disk->tme_posix_disk_stat.st_blksize - 1));
                    367:       data = mmap(NULL,
                    368:                  size_agg,
                    369:                  PROT_READ
                    370:                  | ((posix_disk->tme_posix_disk_flags
                    371:                      & TME_POSIX_DISK_FLAG_RO)
                    372:                     ? 0
                    373:                     : PROT_WRITE),
                    374:                  MAP_SHARED,
                    375:                  posix_disk->tme_posix_disk_fd,
                    376:                  (pos_least
                    377:                   - agg_pre));
                    378:       if (data == MAP_FAILED) {
                    379:        size_agg = (agg_pre + size) + agg_post;
                    380:       }
                    381:       else {
                    382:        agg_post = size_agg - (agg_pre + size);
                    383:       }
                    384:     }
                    385: 
                    386:     /* if we were able to mmap this region: */
                    387:     if (data != MAP_FAILED) {
                    388: 
                    389:       /* if we have a free nosize buffer, reuse it, else free the
                    390:         free sized buffer's data and reuse that: */
                    391:       if (buffer_free_nosize != NULL) {
                    392:        buffer = buffer_free_nosize;
                    393:       }
                    394:       else {
                    395:        tme_free(buffer_free_sized->tme_posix_disk_buffer_data);
                    396:        buffer = buffer_free_sized;
                    397:       }
                    398: 
                    399:       /* do the mmapped-specific initialization of this buffer: */
                    400:       buffer->tme_posix_disk_buffer_flags
                    401:        = (TME_POSIX_DISK_BUFFER_READABLE
                    402:           | TME_POSIX_DISK_BUFFER_MMAPPED);
                    403:     }
                    404: 
                    405:     /* otherwise, we were unable to map this region: */
                    406:     else
                    407: #endif /* HAVE_MMAP */
                    408:       {
                    409: 
                    410:        /* if we have a free sized buffer, resize it, else malloc 
                    411:           data for the free nosize buffer: */
                    412:        if (buffer_free_sized != NULL) {
                    413:          data = buffer_free_sized->tme_posix_disk_buffer_data;
                    414:          if (buffer_free_sized->tme_posix_disk_buffer_size
                    415:              != size_agg) {
                    416:            data = tme_realloc(data, size_agg);
                    417:          }
                    418:          buffer = buffer_free_sized;
                    419:        }
                    420:        else {
                    421:          data = tme_malloc(size_agg);
                    422:          buffer = buffer_free_nosize;
                    423:        }
                    424: 
                    425:        /* do the malloced-specific initialization of this buffer: */
                    426:        buffer->tme_posix_disk_buffer_flags = 0;
                    427:        if (readable) {
                    428:          buffer->tme_posix_disk_buffer_flags
                    429:            = TME_POSIX_DISK_BUFFER_READABLE;
                    430: 
                    431:          /* seek to the buffer's position: */
                    432:          rc = (lseek(posix_disk->tme_posix_disk_fd,
                    433:                      (pos_least
                    434:                       - agg_pre),
                    435:                      SEEK_SET) < 0);
                    436:          assert (rc == 0);
                    437:          
                    438:          /* read in the buffer.  if the read fails with more
                    439:             read-ahead than is needed to meet the block size, try the
                    440:             read one more time with just that needed read-ahead: */
                    441:          for (;;) {
                    442:            ssize = read(posix_disk->tme_posix_disk_fd,
                    443:                         data,
                    444:                         size_agg);
                    445:            if (ssize == size_agg) {
                    446:              break;
                    447:            }
                    448:            size_agg
                    449:              = (((agg_pre + size)
                    450:                  + (posix_disk->tme_posix_disk_stat.st_blksize - 1))
                    451:                 & ~(posix_disk->tme_posix_disk_stat.st_blksize - 1));
                    452:            assert (agg_post > (size_agg - (agg_pre + size)));
                    453:            agg_post = (size_agg - (agg_pre + size));
                    454:          }
                    455:        }
                    456:       }
                    457: 
                    458:     /* do the common initialization of this buffer: */
                    459:     buffer->tme_posix_disk_buffer_pos
                    460:       = (pos_least
                    461:         - agg_pre);
                    462:     buffer->tme_posix_disk_buffer_size
                    463:       = size_agg;
                    464:     buffer->tme_posix_disk_buffer_data
                    465:       = data;
                    466:   }
                    467: 
                    468:   /* if this buffer doesn't need to be readable, that means that it's
                    469:      being written to, so mark it dirty: */
                    470:   if (!readable) {
                    471:     buffer->tme_posix_disk_buffer_flags
                    472:       |= TME_POSIX_DISK_BUFFER_DIRTY;
                    473:   }
                    474: 
                    475:   /* remove this buffer from the list: */
                    476:   *buffer->tme_posix_disk_buffer_prev
                    477:     = buffer->tme_posix_disk_buffer_next;
                    478:   if (buffer->tme_posix_disk_buffer_next != NULL) {
                    479:     buffer->tme_posix_disk_buffer_next->tme_posix_disk_buffer_prev
                    480:       = buffer->tme_posix_disk_buffer_prev;
                    481:   }
                    482: 
                    483:   /* add this buffer to the front of the list: */
                    484:   buffer->tme_posix_disk_buffer_prev
                    485:     = &posix_disk->tme_posix_disk_buffers;
                    486:   buffer->tme_posix_disk_buffer_next
                    487:     = posix_disk->tme_posix_disk_buffers;
                    488:   if (buffer->tme_posix_disk_buffer_next != NULL) {
                    489:     buffer->tme_posix_disk_buffer_next->tme_posix_disk_buffer_prev
                    490:       = &buffer->tme_posix_disk_buffer_next;
                    491:   }
                    492:   *buffer->tme_posix_disk_buffer_prev
                    493:     = buffer;
                    494: 
                    495:   /* return the desired pointer into the buffer: */
                    496:   *_buffer
                    497:     = (buffer->tme_posix_disk_buffer_data
                    498:        + (pos_least
                    499:          - buffer->tme_posix_disk_buffer_pos));
                    500: 
                    501:   return (TME_OK);
                    502: #undef pos_least
                    503: #undef pos_most
                    504: #undef data
                    505: #undef size
                    506: #undef size_agg
                    507: }
                    508: 
                    509: /* this returns a read buffer: */
                    510: static int
                    511: _tme_posix_disk_read(struct tme_disk_connection *conn_disk,
                    512:                     const union tme_value64 *pos,
                    513:                     unsigned long size,
                    514:                     const tme_uint8_t **_buffer)
                    515: {
                    516:   struct tme_posix_disk *posix_disk;
                    517:   tme_uint8_t *buffer;
                    518:   int rc;
                    519: 
                    520:   /* recover our data structure: */
                    521:   posix_disk = (struct tme_posix_disk *) conn_disk->tme_disk_connection.tme_connection_element->tme_element_private;
                    522: 
                    523:   /* lock the mutex: */
                    524:   tme_mutex_lock(&posix_disk->tme_posix_disk_mutex);
                    525: 
                    526:   /* get the buffer: */
                    527:   rc = _tme_posix_disk_buffer_get(posix_disk,
                    528:                                  pos,
                    529:                                  size,
                    530:                                  TRUE,
                    531:                                  &buffer);
                    532:   assert (rc == TME_OK);
                    533: 
                    534:   /* unlock the mutex: */
                    535:   tme_mutex_unlock(&posix_disk->tme_posix_disk_mutex);
                    536: 
                    537:   /* return the buffer: */
                    538:   *_buffer = buffer;
                    539:   return (TME_OK);
                    540: }
                    541: 
                    542: /* this returns a write buffer: */
                    543: static int
                    544: _tme_posix_disk_write(struct tme_disk_connection *conn_disk,
                    545:                      const union tme_value64 *pos,
                    546:                      unsigned long size,
                    547:                      tme_uint8_t **_buffer)
                    548: {
                    549:   struct tme_posix_disk *posix_disk;
                    550:   int rc;
                    551: 
                    552:   /* recover our data structure: */
                    553:   posix_disk = (struct tme_posix_disk *) conn_disk->tme_disk_connection.tme_connection_element->tme_element_private;
                    554: 
                    555:   /* lock the mutex: */
                    556:   tme_mutex_lock(&posix_disk->tme_posix_disk_mutex);
                    557: 
                    558:   /* get the buffer: */
                    559:   rc = _tme_posix_disk_buffer_get(posix_disk,
                    560:                                  pos,
                    561:                                  size,
                    562:                                  FALSE,
                    563:                                  _buffer);
                    564:   assert (rc == TME_OK);
                    565: 
                    566:   /* unlock the mutex: */
                    567:   tme_mutex_unlock(&posix_disk->tme_posix_disk_mutex);
                    568: 
                    569:   return (TME_OK);
                    570: }
                    571: 
                    572: /* the disk control handler: */
                    573: #ifdef HAVE_STDARG_H
                    574: static int _tme_posix_disk_control(struct tme_disk_connection *conn_disk,
                    575:                                   unsigned int control,
                    576:                                   ...)
                    577: #else  /* HAVE_STDARG_H */
                    578: static int _tme_posix_disk_control(conn_disk, control, va_alist)
                    579:      struct tme_disk_connection *conn_disk;
                    580:      unsigned int control;
                    581:      va_dcl
                    582: #endif /* HAVE_STDARG_H */
                    583: {
                    584:   struct tme_posix_disk *posix_disk;
                    585: 
                    586:   /* recover our data structure: */
                    587:   posix_disk = (struct tme_posix_disk *) conn_disk->tme_disk_connection.tme_connection_element->tme_element_private;
                    588: 
                    589:   /* lock the mutex: */
                    590:   tme_mutex_lock(&posix_disk->tme_posix_disk_mutex);
                    591: 
                    592:   /* unlock the mutex: */
                    593:   tme_mutex_unlock(&posix_disk->tme_posix_disk_mutex);
                    594: 
                    595:   return (TME_OK);
                    596: }
                    597: 
1.1.1.3 ! root      598: /* this opens a disk: */
        !           599: static int
        !           600: _tme_posix_disk_open(struct tme_posix_disk *posix_disk,
        !           601:                     const char *filename,
        !           602:                     int flags,
        !           603:                     char **_output)
        !           604: {
        !           605:   int fd;
        !           606:   struct stat statbuf;
        !           607:   tme_uint8_t *block;
        !           608: #ifdef HAVE_MMAP
        !           609:   int page_size;
        !           610: #endif /* HAVE_MMAP */
        !           611: 
        !           612:   /* open the file: */
        !           613:   fd = open(filename,
        !           614:            ((flags
        !           615:              & TME_POSIX_DISK_FLAG_RO)
        !           616:             ? O_RDONLY
        !           617:             : O_RDWR));
        !           618:   if (fd < 0) {
        !           619:     tme_output_append_error(_output,
        !           620:                            "%s",
        !           621:                            filename);
        !           622:     return (errno);
        !           623:   }
        !           624: 
        !           625:   /* stat the file: */
        !           626:   if (fstat(fd, &statbuf) < 0) {
        !           627:     tme_output_append_error(_output,
        !           628:                            "%s",
        !           629:                            filename);
        !           630:     close(fd);
        !           631:     return (errno);
        !           632:   }
        !           633: 
        !           634:   /* we will handle a character device, but not a block device: */
        !           635:   if (S_ISBLK(statbuf.st_mode)) {
        !           636:     tme_output_append_error(_output,
        !           637:                            "%s",
        !           638:                            filename);
        !           639:     close(fd);
        !           640:     return (EINVAL);
        !           641:   }
        !           642: 
        !           643:   /* if this is a character device, determine its block size: */
        !           644:   if (S_ISCHR(statbuf.st_mode)) {
        !           645: 
        !           646:     /* the block size must be at least one: */
        !           647:     statbuf.st_blksize = 1;
        !           648: 
        !           649:     /* allocate space for the block: */
        !           650:     block = tme_new(tme_uint8_t, statbuf.st_blksize);
        !           651: 
        !           652:     /* loop trying to read a block at offset zero, doubling the block
        !           653:        size until we succeed: */
        !           654:     for (; statbuf.st_blksize <= TME_POSIX_DISK_BLOCK_SIZE_MAX; ) {
        !           655: 
        !           656:       /* do the read: */
        !           657:       if (read(fd, block, statbuf.st_blksize) >= 0) {
        !           658:        break;
        !           659:       }
        !           660: 
        !           661:       /* seek back to the beginning: */
        !           662:       if (lseek(fd, 0, SEEK_SET) < 0) {
        !           663:        tme_free(block);
        !           664:        tme_output_append_error(_output,
        !           665:                                "%s",
        !           666:                                filename);
        !           667:        close(fd);
        !           668:        return (errno);
        !           669:       }
        !           670: 
        !           671:       /* resize the block: */
        !           672:       statbuf.st_blksize <<= 1;
        !           673:       block = tme_renew(tme_uint8_t, block, statbuf.st_blksize);
        !           674:     }
        !           675: 
        !           676:     /* free the block: */
        !           677:     tme_free(block);
        !           678: 
        !           679:     /* if we failed: */
        !           680:     if (statbuf.st_blksize > TME_POSIX_DISK_BLOCK_SIZE_MAX) {
        !           681:       tme_output_append_error(_output,
        !           682:                              "%s",
        !           683:                              filename);
        !           684:       close(fd);
        !           685:       return (EINVAL);
        !           686:     }
        !           687:   }
        !           688: 
        !           689: #ifdef HAVE_MMAP
        !           690:   /* if we're mmapping, the block size must be at least the page size: */
        !           691:   for (page_size = getpagesize();
        !           692:        page_size < statbuf.st_blksize;
        !           693:        page_size <<= 1);
        !           694:   statbuf.st_blksize = page_size;
        !           695: #endif /* HAVE_MMAP */
        !           696: 
        !           697:   /* update the disk structure: */
        !           698:   posix_disk->tme_posix_disk_flags = flags;
        !           699:   posix_disk->tme_posix_disk_fd = fd;
        !           700:   posix_disk->tme_posix_disk_stat = statbuf;
        !           701:   return (TME_OK);
        !           702: }
        !           703: 
        !           704: /* this closes a disk: */
        !           705: static void
        !           706: _tme_posix_disk_close(struct tme_posix_disk *posix_disk)
        !           707: {
        !           708:   struct tme_posix_disk_buffer *buffer;
        !           709: 
        !           710:   /* free all of the buffers: */
        !           711:   for (buffer = posix_disk->tme_posix_disk_buffers;
        !           712:        buffer != NULL;
        !           713:        buffer = buffer->tme_posix_disk_buffer_next) {
        !           714:     _tme_posix_disk_buffer_free(posix_disk,
        !           715:                                buffer);
        !           716:   }
        !           717: 
        !           718:   /* close the disk: */
        !           719:   close(posix_disk->tme_posix_disk_fd);
        !           720:   posix_disk->tme_posix_disk_fd = -1;
        !           721: }
        !           722: 
        !           723: /* our internal command function: */
        !           724: static int
        !           725: __tme_posix_disk_command(struct tme_posix_disk *posix_disk,
        !           726:                         const char * const * args, 
        !           727:                         char **_output)
        !           728: {
        !           729:   int usage;
        !           730:   int arg_i;
        !           731:   const char *filename;
        !           732:   int flags;
        !           733:   int rc;
        !           734: 
        !           735:   /* check the command: */
        !           736:   usage = FALSE;
        !           737:   arg_i = 1;
        !           738: 
        !           739:   /* the "load" command: */
        !           740:   if (TME_ARG_IS(args[arg_i], "load")) {
        !           741:     arg_i++;
        !           742: 
        !           743:     /* if a disk is currently loaded, it must be unloaded first: */
        !           744:     if (posix_disk->tme_posix_disk_fd >= 0) {
        !           745:       tme_output_append_error(_output,
        !           746:                              _("%s: disk already loaded; must unload first"),
        !           747:                              args[0]);
        !           748:       return (EBUSY);
        !           749:     }
        !           750: 
        !           751:     /* the first argument is the filename: */
        !           752:     filename = args[arg_i];
        !           753:     arg_i += (filename != NULL);
        !           754: 
        !           755:     /* any remaining arguments are flags: */
        !           756:     flags = 0;
        !           757:     for (;;) {
        !           758:       
        !           759:       /* the "read-only" flag: */
        !           760:       if (TME_ARG_IS(args[arg_i], "read-only")) {
        !           761:        flags |= TME_POSIX_DISK_FLAG_RO;
        !           762:        arg_i++;
        !           763:       }
        !           764: 
        !           765:       else {
        !           766:        break;
        !           767:       }
        !           768:     }
        !           769: 
        !           770:     /* if we don't have a filename, or if there are more arguments: */
        !           771:     if (filename == NULL
        !           772:        || args[arg_i] != NULL) {
        !           773: 
        !           774:       tme_output_append_error(_output,
        !           775:                              "%s %s load { %s | %s } [read-only]",
        !           776:                              _("usage:"),
        !           777:                              args[0],
        !           778:                              _("DEVICE"),
        !           779:                              _("FILENAME"));
        !           780:       rc = EINVAL;
        !           781:     }
        !           782: 
        !           783:     /* otherwise, if we can open the disk: */
        !           784:     else if ((rc = _tme_posix_disk_open(posix_disk,
        !           785:                                        filename,
        !           786:                                        flags,
        !           787:                                        _output)) == TME_OK) {
        !           788: 
        !           789:       /* nothing to do */
        !           790:     }
        !           791:   }
        !           792: 
        !           793:   /* the "unload" command: */
        !           794:   else if (TME_ARG_IS(args[arg_i], "unload")) {
        !           795: 
        !           796:     /* if no disk is currently loaded: */
        !           797:     if (posix_disk->tme_posix_disk_fd < 0) {
        !           798:       tme_output_append_error(_output,
        !           799:                              _("%s: no disk loaded"),
        !           800:                              args[0]);
        !           801:       return (ENXIO);
        !           802:     }
        !           803: 
        !           804:     /* we must have no arguments: */
        !           805:     if (args[arg_i + 1] != NULL) {
        !           806:       tme_output_append_error(_output,
        !           807:                              "%s %s unload",
        !           808:                              _("usage:"),
        !           809:                              args[0]);
        !           810:       return (EINVAL);
        !           811:     }
        !           812: 
        !           813:     /* close the disk: */
        !           814:     _tme_posix_disk_close(posix_disk);
        !           815:     rc = TME_OK;
        !           816:   }
        !           817: 
        !           818:   /* any other command: */
        !           819:   else {
        !           820:     if (args[arg_i] != NULL) {
        !           821:       tme_output_append_error(_output,
        !           822:                              "%s '%s', ",
        !           823:                              _("unknown command"),
        !           824:                              args[1]);
        !           825:     }
        !           826:     tme_output_append_error(_output,
        !           827:                            _("available %s commands: %s"),
        !           828:                            args[0],
        !           829:                            "load unload");
        !           830:     return (EINVAL);
        !           831:   }
        !           832: 
        !           833:   return (rc);
        !           834: }
        !           835: 
        !           836: /* our command function: */
        !           837: static int
        !           838: _tme_posix_disk_command(struct tme_element *element,
        !           839:                        const char * const * args, 
        !           840:                        char **_output)
        !           841: {
        !           842:   struct tme_posix_disk *posix_disk;
        !           843:   int rc;
        !           844: 
        !           845:   /* recover our data structure: */
        !           846:   posix_disk = (struct tme_posix_disk *) element->tme_element_private;
        !           847: 
        !           848:   /* lock the mutex: */
        !           849:   tme_mutex_lock(&posix_disk->tme_posix_disk_mutex);
        !           850: 
        !           851:   /* call the internal command function: */
        !           852:   rc = __tme_posix_disk_command(posix_disk,
        !           853:                                args, 
        !           854:                                _output);
        !           855: 
        !           856:   /* unlock the mutex: */
        !           857:   tme_mutex_unlock(&posix_disk->tme_posix_disk_mutex);
        !           858: 
        !           859:   return (rc);
        !           860: }
        !           861: 
1.1       root      862: /* this breaks a posix disk connection: */
                    863: static int
                    864: _tme_posix_disk_connection_break(struct tme_connection *conn,
                    865:                                 unsigned int state)
                    866: {
                    867:   abort();
                    868: }
                    869: 
                    870: /* this makes a posix disk connection: */
                    871: static int
                    872: _tme_posix_disk_connection_make(struct tme_connection *conn,
                    873:                                unsigned int state)
                    874: {
                    875:   struct tme_posix_disk *posix_disk;
                    876:   struct tme_disk_connection *conn_disk;
                    877: 
                    878:   /* recover our data structure: */
                    879:   posix_disk = (struct tme_posix_disk *) conn->tme_connection_element->tme_element_private;
                    880: 
                    881:   /* both sides must be disk connections: */
                    882:   assert(conn->tme_connection_type == TME_CONNECTION_DISK);
                    883:   assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_DISK);
                    884: 
                    885:   /* we're always set up to answer calls across the connection,
                    886:      so we only have to do work when the connection has gone full,
                    887:      namely taking the other side of the connection: */
                    888:   if (state == TME_CONNECTION_FULL) {
                    889: 
                    890:     /* lock the mutex: */
                    891:     tme_mutex_lock(&posix_disk->tme_posix_disk_mutex);
                    892: 
                    893:     /* save this connection to our list of connections: */
                    894:     conn_disk = (struct tme_disk_connection *) conn->tme_connection_other;
                    895:     posix_disk->tme_posix_disk_connection = conn_disk;
                    896: 
                    897:     /* unlock the mutex: */
                    898:     tme_mutex_unlock(&posix_disk->tme_posix_disk_mutex);
                    899:   }
                    900: 
                    901:   return (TME_OK);
                    902: }
                    903: 
                    904: /* this makes a new connection side for a posix disk: */
                    905: static int
                    906: _tme_posix_disk_connections_new(struct tme_element *element,
                    907:                                const char * const *args,
                    908:                                struct tme_connection **_conns,
                    909:                                char **_output)
                    910: {
                    911:   struct tme_posix_disk *posix_disk;
                    912:   struct tme_disk_connection *conn_disk;
                    913:   struct tme_connection *conn;
                    914: 
                    915:   /* recover our data structure: */
                    916:   posix_disk = (struct tme_posix_disk *) element->tme_element_private;
                    917: 
                    918:   /* if we already have a connection, there's nothing to do: */
                    919:   if (posix_disk->tme_posix_disk_connection != NULL) {
                    920:     return (TME_OK);
                    921:   }
                    922: 
                    923:   /* create our side of a disk connection: */
                    924:   conn_disk = tme_new0(struct tme_disk_connection, 1);
                    925:   conn = &conn_disk->tme_disk_connection;
                    926: 
                    927:   /* fill in the generic connection: */
                    928:   conn->tme_connection_next = *_conns;
                    929:   conn->tme_connection_type = TME_CONNECTION_DISK;
                    930:   conn->tme_connection_score = tme_disk_connection_score;
                    931:   conn->tme_connection_make = _tme_posix_disk_connection_make;
                    932:   conn->tme_connection_break = _tme_posix_disk_connection_break;
                    933: 
                    934:   /* fill in the disk connection: */
                    935:   (void) tme_value64_set(&conn_disk->tme_disk_connection_size,
                    936:                         posix_disk->tme_posix_disk_stat.st_size);
                    937:   conn_disk->tme_disk_connection_read
                    938:     = _tme_posix_disk_read;
                    939:   if (!(posix_disk->tme_posix_disk_flags
                    940:        & TME_POSIX_DISK_FLAG_RO)) {
                    941:     conn_disk->tme_disk_connection_write
                    942:       = _tme_posix_disk_write;
                    943:   }
                    944:   conn_disk->tme_disk_connection_release
                    945:     = NULL;
                    946:   conn_disk->tme_disk_connection_control
                    947:     = _tme_posix_disk_control;
                    948:   
                    949:   /* return the connection side possibility: */
                    950:   *_conns = conn;
                    951:   return (TME_OK);
                    952: }
                    953: 
                    954: /* the new posix disk function: */
                    955: TME_ELEMENT_SUB_NEW_DECL(tme_host_posix,disk) {
                    956:   const char *filename;
                    957:   int flags;
                    958:   unsigned long agg_pre;
                    959:   unsigned long agg_post;
                    960:   int buffers;
                    961:   struct tme_posix_disk *posix_disk;
1.1.1.3 ! root      962:   int rc;
1.1       root      963:   struct tme_posix_disk_buffer *buffer, **_prev;
                    964:   int arg_i;
                    965:   int usage;
                    966: 
                    967:   /* check our arguments: */
                    968:   filename = NULL;
                    969:   flags = 0;
                    970:   arg_i = 1;
                    971:   buffers = TME_POSIX_DISK_BUFFER_DEFAULT_COUNT;
                    972:   agg_pre = TME_POSIX_DISK_BUFFER_DEFAULT_AGG_PRE;
                    973:   agg_post = TME_POSIX_DISK_BUFFER_DEFAULT_AGG_POST;
                    974:   usage = FALSE;
                    975: 
                    976:   /* loop reading our arguments: */
                    977:   for (;;) {
                    978: 
                    979:     /* the filename: */
                    980:     if (TME_ARG_IS(args[arg_i], "file")
                    981:        && args[arg_i + 1] != NULL
                    982:        && filename == NULL) {
                    983:       filename = args[arg_i + 1];
                    984:       arg_i += 2;
                    985:     }
                    986: 
                    987:     /* the read-only flag: */
                    988:     else if (TME_ARG_IS(args[arg_i], "read-only")) {
                    989:       flags |= TME_POSIX_DISK_FLAG_RO;
                    990:       arg_i++;
                    991:     }
                    992: 
                    993:     /* the buffers count: */
                    994:     else if (TME_ARG_IS(args[arg_i + 0], "buffers")
                    995:             && args[arg_i + 1] != NULL
                    996:             && (buffers = atoi(args[arg_i + 1])) > 0) {
                    997:       arg_i += 2;
                    998:     }
                    999: 
                   1000:     /* the read-behind value, also called the aggregate-pre: */
                   1001:     else if (TME_ARG_IS(args[arg_i + 0], "read-behind")) {
                   1002:       agg_pre = tme_bus_addr_parse_any(args[arg_i + 1], &usage);
                   1003:       if (usage) {
                   1004:        break;
                   1005:       }
                   1006:       arg_i += 2;
                   1007:     }
                   1008: 
                   1009:     /* the read-ahead value, also called the aggregate-post: */
                   1010:     else if (TME_ARG_IS(args[arg_i + 0], "read-ahead")) {
                   1011:       agg_post = tme_bus_addr_parse_any(args[arg_i + 1], &usage);
                   1012:       if (usage) {
                   1013:        break;
                   1014:       }
                   1015:       arg_i += 2;
                   1016:     }
                   1017: 
                   1018:     /* if we've run out of arguments: */
                   1019:     else if (args[arg_i + 0] == NULL) {
                   1020: 
                   1021:       /* we must have been given a filename: */
                   1022:       if (filename == NULL) {
                   1023:        usage = TRUE;
                   1024:       }
                   1025:       break;
                   1026:     }
                   1027: 
                   1028:     /* this is a bad argument: */
                   1029:     else {
                   1030:       tme_output_append_error(_output,
                   1031:                              "%s %s", 
                   1032:                              args[arg_i],
                   1033:                              _("unexpected"));
                   1034:       usage = TRUE;
                   1035:       break;
                   1036:     }
                   1037:   }
                   1038: 
                   1039:   if (usage) {
                   1040:     tme_output_append_error(_output, 
                   1041:                            "%s %s file %s [read-only] [buffers %s] [read-behind %s] [read-ahead %s]",
                   1042:                            _("usage:"),
                   1043:                            args[0],
                   1044:                            _("FILENAME"),
                   1045:                            _("BUFFER-COUNT"),
                   1046:                            _("BYTE-COUNT"),
                   1047:                            _("BYTE-COUNT"));
                   1048:     return (EINVAL);
                   1049:   }
                   1050: 
                   1051:   /* start the disk structure: */
                   1052:   posix_disk = tme_new0(struct tme_posix_disk, 1);
                   1053:   posix_disk->tme_posix_disk_element = element;
                   1054:   tme_mutex_init(&posix_disk->tme_posix_disk_mutex);
                   1055:   posix_disk->tme_posix_disk_buffer_agg_pre = agg_pre;
                   1056:   posix_disk->tme_posix_disk_buffer_agg_post = agg_post;
                   1057: 
1.1.1.3 ! root     1058:   /* open the disk: */
        !          1059:   rc = _tme_posix_disk_open(posix_disk,
        !          1060:                            filename,
        !          1061:                            flags,
        !          1062:                            _output);
        !          1063:   if (rc != TME_OK) {
        !          1064:     tme_free(posix_disk);
        !          1065:     return (rc);
        !          1066:   }
        !          1067: 
1.1       root     1068:   /* allocate the buffers: */
                   1069:   for (_prev = &posix_disk->tme_posix_disk_buffers;
                   1070:        buffers-- > 0;
                   1071:        _prev = &buffer->tme_posix_disk_buffer_next) {
                   1072:     buffer = tme_new0(struct tme_posix_disk_buffer, 1);
                   1073:     buffer->tme_posix_disk_buffer_prev = _prev;
                   1074:     *buffer->tme_posix_disk_buffer_prev = buffer;
                   1075:   }
                   1076:   *_prev = NULL;
                   1077: 
                   1078:   /* fill the element: */
                   1079:   element->tme_element_private = posix_disk;
                   1080:   element->tme_element_connections_new = _tme_posix_disk_connections_new;
1.1.1.3 ! root     1081:   element->tme_element_command = _tme_posix_disk_command;
1.1       root     1082: 
                   1083:   return (TME_OK);
                   1084: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.