source: XIOS/dev/dev_olga/extern/src_netcdf4/mmapio.c @ 1620

Last change on this file since 1620 was 409, checked in by ymipsl, 11 years ago

Add improved nectdf internal library src

YM

  • Property svn:eol-style set to native
File size: 16.1 KB
Line 
1/*
2 *      Copyright 1996, University Corporation for Atmospheric Research
3 *      See netcdf/COPYRIGHT file for copying and redistribution conditions.
4 */
5
6#include "config.h"
7#include <assert.h>
8#include <stdlib.h>
9#include <errno.h>
10#include <string.h>
11#ifdef _MSC_VER /* Microsoft Compilers */
12#include <io.h>
13#endif
14#ifdef HAVE_UNISTD_H
15#include <unistd.h>
16#endif
17#ifdef HAVE_FCNTL_H
18#include <fcntl.h>
19#endif
20#include "nc.h"
21
22#undef DEBUG
23
24#ifdef DEBUG
25#include <stdio.h>
26#endif
27
28#include <sys/mman.h>
29
30#ifndef MAP_ANONYMOUS
31#  ifdef MAP_ANON
32#    define MAP_ANONYMOUS MAP_ANON
33#  endif
34#endif
35
36/* !MAP_ANONYMOUS => !HAVE_MMAP */
37#ifndef MAP_ANONYMOUS
38#error mmap not fully implemented: missing MAP_ANONYMOUS
39#endif
40
41#ifdef HAVE_MMAP
42  /* This is conditionalized by __USE_GNU ; why? */
43  extern void *mremap(void*,size_t,size_t,int);
44# ifndef MREMAP_MAYMOVE
45#   define MREMAP_MAYMOVE 1
46# endif
47#endif /*HAVE_MMAP*/
48
49#ifndef HAVE_SSIZE_T
50#define ssize_t int
51#endif
52
53#ifndef SEEK_SET
54#define SEEK_SET 0
55#define SEEK_CUR 1
56#define SEEK_END 2
57#endif
58
59/* Define the mode flags for create: let umask decide */
60#define OPENMODE 0666
61
62#include "ncio.h"
63#include "fbits.h"
64#include "rnd.h"
65
66/* #define INSTRUMENT 1 */
67#if INSTRUMENT /* debugging */
68#undef NDEBUG
69#include <stdio.h>
70/*#include "instr.h"*/
71#endif
72
73#ifndef MMAP_MAXBLOCKSIZE
74#define MMAP_MAXBLOCKSIZE 268435456 /* sanity check, about X_SIZE_T_MAX/8 */
75#endif
76
77#undef MIN  /* system may define MIN somewhere and complain */
78#define MIN(mm,nn) (((mm) < (nn)) ? (mm) : (nn))
79
80#if !defined(NDEBUG) && !defined(X_INT_MAX)
81#define  X_INT_MAX 2147483647
82#endif
83
84#if 0 /* !defined(NDEBUG) && !defined(X_ALIGN) */
85#define  X_ALIGN 4
86#else
87#undef X_ALIGN
88#endif
89
90/* Private data for mmap */
91
92typedef struct NCMMAPIO {
93    int locked; /* => we cannot realloc */
94    int persist; /* => save to a file; triggered by NC_WRITE */
95    char* memory;
96    off_t alloc;
97    off_t size;
98    off_t pos;
99    int mapfd;
100} NCMMAPIO;
101
102/* Forward */
103static int mmapio_rel(ncio *const nciop, off_t offset, int rflags);
104static int mmapio_get(ncio *const nciop, off_t offset, size_t extent, int rflags, void **const vpp);
105static int mmapio_move(ncio *const nciop, off_t to, off_t from, size_t nbytes, int rflags);
106static int mmapio_sync(ncio *const nciop);
107static int mmapio_filesize(ncio* nciop, off_t* filesizep);
108static int mmapio_pad_length(ncio* nciop, off_t length);
109static int mmapio_close(ncio* nciop, int);
110
111/* Mnemonic */
112#define DOOPEN 1
113
114static long pagesize = 0;
115
116/* Create a new ncio struct to hold info about the file. */
117static int
118mmapio_new(const char* path, int ioflags, off_t initialsize, ncio** nciopp, NCMMAPIO** mmapp)
119{
120    int status = NC_NOERR;
121    ncio* nciop = NULL;
122    NCMMAPIO* mmapio = NULL;
123    int openfd = -1;
124
125    if(pagesize == 0) {
126#if defined HAVE_SYSCONF
127        pagesize = sysconf(_SC_PAGE_SIZE);
128#elif defined HAVE_GETPAGESIZE
129        pagesize = getpagesize();
130#else
131        pagesize = 4096; /* good guess */
132#endif
133    }
134
135    errno = 0;
136
137    /* Always force the allocated size to be a multiple of pagesize */
138    if(initialsize == 0) initialsize = pagesize;
139    if((initialsize % pagesize) != 0)
140        initialsize += (pagesize - (initialsize % pagesize));
141
142    nciop = (ncio* )calloc(1,sizeof(ncio));
143    if(nciop == NULL) {status = NC_ENOMEM; goto fail;}
144   
145    nciop->ioflags = ioflags;
146    *((int*)&nciop->fd) = -1; /* caller will fix */
147
148    *((char**)&nciop->path) = strdup(path);
149    if(nciop->path == NULL) {status = NC_ENOMEM; goto fail;}
150
151    *((ncio_relfunc**)&nciop->rel) = mmapio_rel;
152    *((ncio_getfunc**)&nciop->get) = mmapio_get;
153    *((ncio_movefunc**)&nciop->move) = mmapio_move;
154    *((ncio_syncfunc**)&nciop->sync) = mmapio_sync;
155    *((ncio_filesizefunc**)&nciop->filesize) = mmapio_filesize;
156    *((ncio_pad_lengthfunc**)&nciop->pad_length) = mmapio_pad_length;
157    *((ncio_closefunc**)&nciop->close) = mmapio_close;
158
159    mmapio = (NCMMAPIO*)calloc(1,sizeof(NCMMAPIO));
160    if(mmapio == NULL) {status = NC_ENOMEM; goto fail;}
161    *((void* *)&nciop->pvt) = mmapio;
162
163    mmapio->alloc = initialsize;
164
165    mmapio->memory = NULL;
166    mmapio->size = 0;
167    mmapio->pos = 0;
168    mmapio->persist = fIsSet(ioflags,NC_WRITE);
169
170    /* See if ok to use mmap */
171    if(sizeof(void*) < 8 && fIsSet(ioflags,NC_64BIT_OFFSET))
172        return NC_DISKLESS; /* cannot support */
173    mmapio->mapfd = -1;
174
175    if(nciopp) *nciopp = nciop;
176    if(mmapp) *mmapp = mmapio;
177
178done:
179    if(openfd >= 0) close(openfd);
180    return status;
181
182fail:
183    if(nciop != NULL) {
184        if(nciop->path != NULL) free((char*)nciop->path);
185    }
186    goto done;
187}
188
189/* Create a file, and the ncio struct to go with it. This function is
190   only called from nc__create_mp.
191
192   path - path of file to create.
193   ioflags - flags from nc_create
194   initialsz - From the netcdf man page: "The argument
195   Iinitialsize sets the initial size of the file at creation time."
196   igeto -
197   igetsz -
198   sizehintp - the size of a page of data for buffered reads and writes.
199   nciopp - pointer to a pointer that will get location of newly
200   created and inited ncio struct.
201   mempp - pointer to pointer to the initial memory read.
202*/
203int
204mmapio_create(const char* path, int ioflags,
205    size_t initialsz,
206    off_t igeto, size_t igetsz, size_t* sizehintp,
207    ncio* *nciopp, void** const mempp)
208{
209    ncio* nciop;
210    int fd;
211    int status;
212    NCMMAPIO* mmapio = NULL;
213    int persist = (ioflags & NC_WRITE?1:0);
214    int oflags;
215
216    if(path == NULL ||* path == 0)
217        return NC_EINVAL;
218
219    /* For diskless open has, the file must be classic version 1 or 2.*/
220    if(fIsSet(ioflags,NC_NETCDF4))
221        return NC_EDISKLESS; /* violates constraints */
222
223    status = mmapio_new(path, ioflags, initialsz, &nciop, &mmapio);
224    if(status != NC_NOERR)
225        return status;
226    mmapio->size = 0;
227
228    if(!persist) {
229        mmapio->mapfd = -1;
230        mmapio->memory = (char*)mmap(NULL,mmapio->alloc,
231                                    PROT_READ|PROT_WRITE,
232                                    MAP_PRIVATE|MAP_ANONYMOUS,
233                                    mmapio->mapfd,0);
234        {mmapio->memory[0] = 0;} /* test writing of the mmap'd memory */
235    } else { /*persist */
236        /* Open the file, but make sure we can write it if needed */
237        oflags = (persist ? O_RDWR : O_RDONLY);   
238#ifdef O_BINARY
239        fSet(oflags, O_BINARY);
240#endif
241        oflags |= (O_CREAT|O_TRUNC);
242        if(fIsSet(ioflags,NC_NOCLOBBER))
243            oflags |= O_EXCL;
244#ifdef vms
245        fd = open(path, oflags, 0, "ctx=stm");
246#else
247        fd  = open(path, oflags, OPENMODE);
248#endif
249        if(fd < 0) {status = errno; goto unwind_open;}
250        mmapio->mapfd = fd;
251
252        { /* Cause the output file to have enough allocated space */
253        lseek(fd,mmapio->alloc-1,SEEK_SET); /* cause file to appear */
254        write(fd,"",mmapio->alloc);
255        lseek(fd,0,SEEK_SET); /* rewind */
256        }
257        mmapio->memory = (char*)mmap(NULL,mmapio->alloc,
258                                    PROT_READ|PROT_WRITE,
259                                    MAP_SHARED,
260                                    mmapio->mapfd,0);
261        if(mmapio->memory == NULL) {
262            return NC_EDISKLESS;
263        }
264    } /*!persist*/
265
266#ifdef DEBUG
267fprintf(stderr,"mmap_create: initial memory: %lu/%lu\n",(unsigned long)mmapio->memory,(unsigned long)mmapio->alloc);
268#endif
269
270    fd = nc__pseudofd();
271    *((int* )&nciop->fd) = fd; 
272
273    fSet(nciop->ioflags, NC_WRITE);
274
275    if(igetsz != 0)
276    {
277        status = nciop->get(nciop,
278                igeto, igetsz,
279                RGN_WRITE,
280                mempp);
281        if(status != NC_NOERR)
282            goto unwind_open;
283    }
284
285    /* Pick a default sizehint */
286    if(sizehintp) *sizehintp = pagesize;
287
288    *nciopp = nciop;
289    return NC_NOERR;
290
291unwind_open:
292    mmapio_close(nciop,1);
293    return status;
294}
295
296/* This function opens the data file. It is only called from nc.c,
297   from nc__open_mp and nc_delete_mp.
298
299   path - path of data file.
300   ioflags - flags passed into nc_open.
301   igeto - looks like this function can do an initial page get, and
302   igeto is going to be the offset for that. But it appears to be
303   unused
304   igetsz - the size in bytes of initial page get (a.k.a. extent). Not
305   ever used in the library.
306   sizehintp - the size of a page of data for buffered reads and writes.
307   nciopp - pointer to pointer that will get address of newly created
308   and inited ncio struct.
309   mempp - pointer to pointer to the initial memory read.
310*/
311int
312mmapio_open(const char* path,
313    int ioflags,
314    off_t igeto, size_t igetsz, size_t* sizehintp,
315    ncio* *nciopp, void** const mempp)
316{
317    ncio* nciop;
318    int fd;
319    int status;
320    int persist = (fIsSet(ioflags,NC_WRITE)?1:0);
321    int oflags;
322    NCMMAPIO* mmapio = NULL;
323    size_t sizehint;
324    off_t filesize;
325
326    if(path == NULL ||* path == 0)
327        return EINVAL;
328
329    assert(sizehintp != NULL);
330    sizehint = *sizehintp;
331
332    /* Open the file, but make sure we can write it if needed */
333    oflags = (persist ? O_RDWR : O_RDONLY);   
334#ifdef O_BINARY
335    fSet(oflags, O_BINARY);
336#endif
337    oflags |= O_EXCL;
338#ifdef vms
339    fd = open(path, oflags, 0, "ctx=stm");
340#else
341    fd  = open(path, oflags, OPENMODE);
342#endif
343    if(fd < 0) {status = errno; goto unwind_open;}
344
345    /* get current filesize  = max(|file|,initialize)*/
346    filesize = lseek(fd,0,SEEK_END);
347    if(filesize < 0) {status = errno; goto unwind_open;}
348    /* move pointer back to beginning of file */
349    (void)lseek(fd,0,SEEK_SET);
350    if(filesize < (off_t)sizehint)
351        filesize = (off_t)sizehint;
352
353    status = mmapio_new(path, ioflags, filesize, &nciop, &mmapio);
354    if(status != NC_NOERR)
355        return status;
356    mmapio->size = filesize;
357
358    mmapio->mapfd = fd;
359    mmapio->memory = (char*)mmap(NULL,mmapio->alloc,
360                                    persist?(PROT_READ|PROT_WRITE):(PROT_READ),
361                                    MAP_SHARED,
362                                    mmapio->mapfd,0);
363#ifdef DEBUG
364fprintf(stderr,"mmapio_open: initial memory: %lu/%lu\n",(unsigned long)mmapio->memory,(unsigned long)mmapio->alloc);
365#endif
366
367    /* Use half the filesize as the blocksize */
368    sizehint = filesize/2;
369
370    fd = nc__pseudofd();
371    *((int* )&nciop->fd) = fd; 
372
373    if(igetsz != 0)
374    {
375        status = nciop->get(nciop,
376                igeto, igetsz,
377                0,
378                mempp);
379        if(status != NC_NOERR)
380            goto unwind_open;
381    }
382
383    *sizehintp = sizehint;
384    *nciopp = nciop;
385    return NC_NOERR;
386
387unwind_open:
388    mmapio_close(nciop,0);
389    return status;
390}
391
392
393/*
394 *  Get file size in bytes.
395 */
396static int
397mmapio_filesize(ncio* nciop, off_t* filesizep)
398{
399    NCMMAPIO* mmapio;
400    if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
401    mmapio = (NCMMAPIO*)nciop->pvt;
402    if(filesizep != NULL) *filesizep = mmapio->size;
403    return NC_NOERR;
404}
405
406/*
407 *  Sync any changes to disk, then truncate or extend file so its size
408 *  is length.  This is only intended to be called before close, if the
409 *  file is open for writing and the actual size does not match the
410 *  calculated size, perhaps as the result of having been previously
411 *  written in NOFILL mode.
412 */
413static int
414mmapio_pad_length(ncio* nciop, off_t length)
415{
416    NCMMAPIO* mmapio;
417    if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
418    mmapio = (NCMMAPIO*)nciop->pvt;
419
420    if(!fIsSet(nciop->ioflags, NC_WRITE))
421        return EPERM; /* attempt to write readonly file*/
422
423    if(mmapio->locked > 0)
424        return NC_EDISKLESS;
425
426    if(length > mmapio->alloc) {
427        /* Realloc the allocated memory to a multiple of the pagesize*/
428        off_t newsize = length;
429        void* newmem = NULL;
430        /* Round to a multiple of pagesize */
431        if((newsize % pagesize) != 0)
432            newsize += (pagesize - (newsize % pagesize));
433
434        /* Force file size to be properly extended */
435        { /* Cause the output file to have enough allocated space */
436        off_t pos = lseek(mmapio->mapfd,0,SEEK_CUR); /* save current position*/
437        /* cause file to be extended in size */
438        lseek(mmapio->mapfd,newsize-1,SEEK_SET);
439        write(mmapio->mapfd,"",mmapio->alloc);
440        lseek(mmapio->mapfd,pos,SEEK_SET); /* reset position */
441        }
442
443        newmem = (char*)mremap(mmapio->memory,mmapio->alloc,newsize,MREMAP_MAYMOVE);
444        if(newmem == NULL) return NC_ENOMEM;
445
446#ifdef DEBUG
447fprintf(stderr,"realloc: %lu/%lu -> %lu/%lu\n",
448(unsigned long)mmapio->memory,(unsigned long)mmapio->alloc,
449(unsigned long)newmem,(unsigned long)newsize);
450#endif
451        mmapio->memory = newmem;
452        mmapio->alloc = newsize;
453    } 
454    mmapio->size = length;
455    return NC_NOERR;
456}
457
458/* Write out any dirty buffers to disk and
459   ensure that next read will get data from disk.
460   Sync any changes, then close the open file associated with the ncio
461   struct, and free its memory.
462   nciop - pointer to ncio to close.
463   doUnlink - if true, unlink file
464*/
465
466static int 
467mmapio_close(ncio* nciop, int doUnlink)
468{
469    int status = NC_NOERR;
470    NCMMAPIO* mmapio;
471    if(nciop == NULL || nciop->pvt == NULL) return NC_NOERR;
472
473    mmapio = (NCMMAPIO*)nciop->pvt;
474    assert(mmapio != NULL);
475
476    /* Since we are using mmap, persisting to a file should be automatic */
477    status = munmap(mmapio->memory,mmapio->alloc);
478    mmapio->memory = NULL; /* so we do not try to free it */
479
480    /* Close file if it was open */
481    if(mmapio->mapfd >= 0)
482        close(mmapio->mapfd);
483
484    /* do cleanup  */
485    if(mmapio != NULL) free(mmapio);
486    if(nciop->path != NULL) free((char*)nciop->path);
487    free(nciop);
488    return status;
489}
490
491static int
492guarantee(ncio* nciop, off_t endpoint)
493{
494    NCMMAPIO* mmapio = (NCMMAPIO*)nciop->pvt;
495    if(endpoint > mmapio->alloc) {
496        /* extend the allocated memory and size */
497        int status = mmapio_pad_length(nciop,endpoint);
498        if(status != NC_NOERR) return status;
499    }
500    if(mmapio->size < endpoint)
501        mmapio->size = endpoint;
502    return NC_NOERR;
503}
504
505/*
506 * Request that the region (offset, extent)
507 * be made available through *vpp.
508 */
509static int
510mmapio_get(ncio* const nciop, off_t offset, size_t extent, int rflags, void** const vpp)
511{
512    int status = NC_NOERR;
513    NCMMAPIO* mmapio;
514    if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
515    mmapio = (NCMMAPIO*)nciop->pvt;
516    status = guarantee(nciop, offset+extent);
517    mmapio->locked++;
518    if(status != NC_NOERR) return status;
519    if(vpp) *vpp = mmapio->memory+offset;
520    return NC_NOERR;
521}
522
523/*
524 * Like memmove(), safely move possibly overlapping data.
525 */
526static int
527mmapio_move(ncio* const nciop, off_t to, off_t from, size_t nbytes, int ignored)
528{
529    int status = NC_NOERR;
530    NCMMAPIO* mmapio;
531
532    if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
533    mmapio = (NCMMAPIO*)nciop->pvt;
534    if(from < to) {
535       /* extend if "to" is not currently allocated */
536       status = guarantee(nciop,to+nbytes);
537       if(status != NC_NOERR) return status;
538    }
539    /* check for overlap */
540    if((to + nbytes) > from || (from + nbytes) > to) {
541        /* Ranges overlap */
542#ifdef HAVE_MEMMOVE
543        memmove((void*)(mmapio->memory+to),(void*)(mmapio->memory+from),nbytes);
544#else
545        off_t overlap;
546        off_t nbytes1;
547        if((from + nbytes) > to) {
548            overlap = ((from + nbytes) - to); /* # bytes of overlap */
549            nbytes1 = (nbytes - overlap); /* # bytes of non-overlap */
550            /* move the non-overlapping part */
551            memcpy((void*)(mmapio->memory+(to+overlap)),
552                   (void*)(mmapio->memory+(from+overlap)),
553                   nbytes1);
554            /* move the overlapping part */
555            memcpy((void*)(mmapio->memory+to),
556                   (void*)(mmapio->memory+from),
557                   overlap);
558        } else { /*((to + nbytes) > from) */
559            overlap = ((to + nbytes) - from); /* # bytes of overlap */
560            nbytes1 = (nbytes - overlap); /* # bytes of non-overlap */
561            /* move the non-overlapping part */
562            memcpy((void*)(mmapio->memory+to),
563                   (void*)(mmapio->memory+from),
564                   nbytes1);
565            /* move the overlapping part */
566            memcpy((void*)(mmapio->memory+(to+nbytes1)),
567                   (void*)(mmapio->memory+(from+nbytes1)),
568                   overlap);
569        }
570#endif
571    } else {/* no overlap */
572        memcpy((void*)(mmapio->memory+to),(void*)(mmapio->memory+from),nbytes);
573    }
574    return status;
575}
576
577static int
578mmapio_rel(ncio* const nciop, off_t offset, int rflags)
579{
580    NCMMAPIO* mmapio;
581    if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
582    mmapio = (NCMMAPIO*)nciop->pvt;
583    mmapio->locked--;
584    return NC_NOERR; /* do nothing */
585}
586
587/*
588 * Write out any dirty buffers to disk and
589 * ensure that next read will get data from disk.
590 */
591static int
592mmapio_sync(ncio* const nciop)
593{
594    return NC_NOERR; /* do nothing */
595}
Note: See TracBrowser for help on using the repository browser.