1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2  * Copyright by The HDF Group.                                               *
3  * Copyright by the Board of Trustees of the University of Illinois.         *
4  * All rights reserved.                                                      *
5  *                                                                           *
6  * This file is part of HDF5.  The full HDF5 copyright notice, including     *
7  * terms governing use, modification, and redistribution, is contained in    *
8  * the COPYING file, which can be found at the root of the source code       *
9  * distribution tree, or in https://www.hdfgroup.org/licenses.               *
10  * If you do not have access to either file, you may request a copy from     *
11  * help@hdfgroup.org.                                                        *
12  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
13 
14 /*-------------------------------------------------------------------------
15  *
16  * Created:             H5Faccum.c
17  *                      Jan 10 2008
18  *                      Quincey Koziol
19  *
20  * Purpose:             File metadata "accumulator" routines.  (Used to
21  *                      cache small metadata I/Os and group them into a
22  *                      single larger I/O)
23  *
24  *-------------------------------------------------------------------------
25  */
26 
27 /****************/
28 /* Module Setup */
29 /****************/
30 
31 #include "H5Fmodule.h" /* This source code file is part of the H5F module */
32 
33 /***********/
34 /* Headers */
35 /***********/
36 #include "H5private.h"   /* Generic Functions			*/
37 #include "H5Eprivate.h"  /* Error handling		  	*/
38 #include "H5Fpkg.h"      /* File access				*/
39 #include "H5FDprivate.h" /* File drivers				*/
40 #include "H5MMprivate.h" /* Memory management			*/
41 #include "H5VMprivate.h" /* Vectors and arrays 			*/
42 
43 /****************/
44 /* Local Macros */
45 /****************/
46 
47 /* Metadata accumulator controls */
48 #define H5F_ACCUM_THROTTLE  8
49 #define H5F_ACCUM_THRESHOLD 2048
50 #define H5F_ACCUM_MAX_SIZE  (1024 * 1024) /* Max. accum. buf size (max. I/Os will be 1/2 this size) */
51 
52 /******************/
53 /* Local Typedefs */
54 /******************/
55 
56 /* Enumerated type to indicate how data will be added to accumulator */
57 typedef enum {
58     H5F_ACCUM_PREPEND, /* Data will be prepended to accumulator */
59     H5F_ACCUM_APPEND   /* Data will be appended to accumulator */
60 } H5F_accum_adjust_t;
61 
62 /********************/
63 /* Package Typedefs */
64 /********************/
65 
66 /********************/
67 /* Local Prototypes */
68 /********************/
69 
70 /*********************/
71 /* Package Variables */
72 /*********************/
73 
74 /*****************************/
75 /* Library Private Variables */
76 /*****************************/
77 
78 /*******************/
79 /* Local Variables */
80 /*******************/
81 
82 /* Declare a PQ free list to manage the metadata accumulator buffer */
83 H5FL_BLK_DEFINE_STATIC(meta_accum);
84 
85 /*-------------------------------------------------------------------------
86  * Function:	H5F__accum_read
87  *
88  * Purpose:	Attempts to read some data from the metadata accumulator for
89  *              a file into a buffer.
90  *
91  * Note:	We can't change (or add to) the metadata accumulator, because
92  *		this might be a speculative read and could possibly read raw
93  *		data into the metadata accumulator.
94  *
95  * Return:	Non-negative on success/Negative on failure
96  *
97  * Programmer:	Quincey Koziol
98  *		Jan 10 2008
99  *
100  *-------------------------------------------------------------------------
101  */
102 herr_t
H5F__accum_read(H5F_shared_t * f_sh,H5FD_mem_t map_type,haddr_t addr,size_t size,void * buf)103 H5F__accum_read(H5F_shared_t *f_sh, H5FD_mem_t map_type, haddr_t addr, size_t size, void *buf /*out*/)
104 {
105     H5FD_t *file;                /* File driver pointer */
106     herr_t  ret_value = SUCCEED; /* Return value */
107 
108     FUNC_ENTER_PACKAGE
109 
110     /* Sanity checks */
111     HDassert(f_sh);
112     HDassert(buf);
113 
114     /* Translate to file driver I/O info object */
115     file = f_sh->lf;
116 
117     /* Check if this information is in the metadata accumulator */
118     if ((f_sh->feature_flags & H5FD_FEAT_ACCUMULATE_METADATA) && map_type != H5FD_MEM_DRAW) {
119         H5F_meta_accum_t *accum; /* Alias for file's metadata accumulator */
120 
121         /* Set up alias for file's metadata accumulator info */
122         accum = &f_sh->accum;
123 
124         if (size < H5F_ACCUM_MAX_SIZE) {
125             /* Sanity check */
126             HDassert(!accum->buf || (accum->alloc_size >= accum->size));
127 
128             /* Current read adjoins or overlaps with metadata accumulator */
129             if (H5F_addr_overlap(addr, size, accum->loc, accum->size) || ((addr + size) == accum->loc) ||
130                 (accum->loc + accum->size) == addr) {
131                 size_t  amount_before; /* Amount to read before current accumulator */
132                 haddr_t new_addr;      /* New address of the accumulator buffer */
133                 size_t  new_size;      /* New size of the accumulator buffer */
134 
135                 /* Compute new values for accumulator */
136                 new_addr = MIN(addr, accum->loc);
137                 new_size = (size_t)(MAX((addr + size), (accum->loc + accum->size)) - new_addr);
138 
139                 /* Check if we need more buffer space */
140                 if (new_size > accum->alloc_size) {
141                     size_t new_alloc_size; /* New size of accumulator */
142 
143                     /* Adjust the buffer size to be a power of 2 that is large enough to hold data */
144                     new_alloc_size = (size_t)1 << (1 + H5VM_log2_gen((uint64_t)(new_size - 1)));
145 
146                     /* Reallocate the metadata accumulator buffer */
147                     if (NULL == (accum->buf = H5FL_BLK_REALLOC(meta_accum, accum->buf, new_alloc_size)))
148                         HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
149                                     "unable to allocate metadata accumulator buffer")
150 
151                     /* Note the new buffer size */
152                     accum->alloc_size = new_alloc_size;
153 
154                     /* Clear the memory */
155                     HDmemset(accum->buf + accum->size, 0, (accum->alloc_size - accum->size));
156                 } /* end if */
157 
158                 /* Read the part before the metadata accumulator */
159                 if (addr < accum->loc) {
160                     /* Set the amount to read */
161                     H5_CHECKED_ASSIGN(amount_before, size_t, (accum->loc - addr), hsize_t);
162 
163                     /* Make room for the metadata to read in */
164                     HDmemmove(accum->buf + amount_before, accum->buf, accum->size);
165 
166                     /* Adjust dirty region tracking info, if present */
167                     if (accum->dirty)
168                         accum->dirty_off += amount_before;
169 
170                     /* Dispatch to driver */
171                     if (H5FD_read(file, map_type, addr, amount_before, accum->buf) < 0)
172                         HGOTO_ERROR(H5E_IO, H5E_READERROR, FAIL, "driver read request failed")
173                 } /* end if */
174                 else
175                     amount_before = 0;
176 
177                 /* Read the part after the metadata accumulator */
178                 if ((addr + size) > (accum->loc + accum->size)) {
179                     size_t amount_after; /* Amount to read at a time */
180 
181                     /* Set the amount to read */
182                     H5_CHECKED_ASSIGN(amount_after, size_t, ((addr + size) - (accum->loc + accum->size)),
183                                       hsize_t);
184 
185                     /* Dispatch to driver */
186                     if (H5FD_read(file, map_type, (accum->loc + accum->size), amount_after,
187                                   (accum->buf + accum->size + amount_before)) < 0)
188                         HGOTO_ERROR(H5E_IO, H5E_READERROR, FAIL, "driver read request failed")
189                 } /* end if */
190 
191                 /* Copy the data out of the buffer */
192                 H5MM_memcpy(buf, accum->buf + (addr - new_addr), size);
193 
194                 /* Adjust the accumulator address & size */
195                 accum->loc  = new_addr;
196                 accum->size = new_size;
197             } /* end if */
198             /* Current read doesn't overlap with metadata accumulator, read it from file */
199             else {
200                 /* Dispatch to driver */
201                 if (H5FD_read(file, map_type, addr, size, buf) < 0)
202                     HGOTO_ERROR(H5E_IO, H5E_READERROR, FAIL, "driver read request failed")
203             } /* end else */
204         }     /* end if */
205         else {
206             /* Read the data */
207             if (H5FD_read(file, map_type, addr, size, buf) < 0)
208                 HGOTO_ERROR(H5E_IO, H5E_READERROR, FAIL, "driver read request failed")
209 
210             /* Check for overlap w/dirty accumulator */
211             /* (Note that this could be improved by updating the non-dirty
212              *  information in the accumulator with [some of] the information
213              *  just read in. -QAK)
214              */
215             if (accum->dirty &&
216                 H5F_addr_overlap(addr, size, accum->loc + accum->dirty_off, accum->dirty_len)) {
217                 haddr_t dirty_loc = accum->loc + accum->dirty_off; /* File offset of dirty information */
218                 size_t  buf_off;                                   /* Offset of dirty region in buffer */
219                 size_t  dirty_off;                                 /* Offset within dirty region */
220                 size_t  overlap_size;                              /* Size of overlap with dirty region */
221 
222                 /* Check for read starting before beginning dirty region */
223                 if (H5F_addr_le(addr, dirty_loc)) {
224                     /* Compute offset of dirty region within buffer */
225                     buf_off = (size_t)(dirty_loc - addr);
226 
227                     /* Compute offset within dirty region */
228                     dirty_off = 0;
229 
230                     /* Check for read ending within dirty region */
231                     if (H5F_addr_lt(addr + size, dirty_loc + accum->dirty_len))
232                         overlap_size = (size_t)((addr + size) - buf_off);
233                     else /* Access covers whole dirty region */
234                         overlap_size = accum->dirty_len;
235                 }      /* end if */
236                 else { /* Read starts after beginning of dirty region */
237                     /* Compute dirty offset within buffer and overlap size */
238                     buf_off      = 0;
239                     dirty_off    = (size_t)(addr - dirty_loc);
240                     overlap_size = (size_t)((dirty_loc + accum->dirty_len) - addr);
241                 } /* end else */
242 
243                 /* Copy the dirty region to buffer */
244                 H5MM_memcpy((unsigned char *)buf + buf_off,
245                             (unsigned char *)accum->buf + accum->dirty_off + dirty_off, overlap_size);
246             } /* end if */
247         }     /* end else */
248     }         /* end if */
249     else {
250         /* Read the data */
251         if (H5FD_read(file, map_type, addr, size, buf) < 0)
252             HGOTO_ERROR(H5E_IO, H5E_READERROR, FAIL, "driver read request failed")
253     } /* end else */
254 
255 done:
256     FUNC_LEAVE_NOAPI(ret_value)
257 } /* end H5F__accum_read() */
258 
259 /*-------------------------------------------------------------------------
260  * Function:	H5F__accum_adjust
261  *
262  * Purpose:	Adjust accumulator size, if necessary
263  *
264  * Return:	Non-negative on success/Negative on failure
265  *
266  * Programmer:	Quincey Koziol
267  *		Jun 11 2009
268  *
269  *-------------------------------------------------------------------------
270  */
271 static herr_t
H5F__accum_adjust(H5F_meta_accum_t * accum,H5FD_t * file,H5F_accum_adjust_t adjust,size_t size)272 H5F__accum_adjust(H5F_meta_accum_t *accum, H5FD_t *file, H5F_accum_adjust_t adjust, size_t size)
273 {
274     herr_t ret_value = SUCCEED; /* Return value */
275 
276     FUNC_ENTER_STATIC
277 
278     HDassert(accum);
279     HDassert(file);
280     HDassert(H5F_ACCUM_APPEND == adjust || H5F_ACCUM_PREPEND == adjust);
281     HDassert(size > 0);
282     HDassert(size <= H5F_ACCUM_MAX_SIZE);
283 
284     /* Check if we need more buffer space */
285     if ((size + accum->size) > accum->alloc_size) {
286         size_t new_size; /* New size of accumulator */
287 
288         /* Adjust the buffer size to be a power of 2 that is large enough to hold data */
289         new_size = (size_t)1 << (1 + H5VM_log2_gen((uint64_t)((size + accum->size) - 1)));
290 
291         /* Check for accumulator getting too big */
292         if (new_size > H5F_ACCUM_MAX_SIZE) {
293             size_t shrink_size;  /* Amount to shrink accumulator by */
294             size_t remnant_size; /* Amount left in accumulator */
295 
296             /* Cap the accumulator's growth, leaving some room */
297 
298             /* Determine the amounts to work with */
299             if (size > (H5F_ACCUM_MAX_SIZE / 2)) {
300                 new_size     = H5F_ACCUM_MAX_SIZE;
301                 shrink_size  = accum->size;
302                 remnant_size = 0;
303             } /* end if */
304             else {
305                 if (H5F_ACCUM_PREPEND == adjust) {
306                     new_size     = (H5F_ACCUM_MAX_SIZE / 2);
307                     shrink_size  = (H5F_ACCUM_MAX_SIZE / 2);
308                     remnant_size = accum->size - shrink_size;
309                 } /* end if */
310                 else {
311                     size_t adjust_size = size + accum->dirty_len;
312 
313                     /* Check if we can slide the dirty region down, to accommodate the request */
314                     if (accum->dirty && (adjust_size <= H5F_ACCUM_MAX_SIZE)) {
315                         if ((ssize_t)(H5F_ACCUM_MAX_SIZE - (accum->dirty_off + adjust_size)) >=
316                             (ssize_t)(2 * size))
317                             shrink_size = accum->dirty_off / 2;
318                         else
319                             shrink_size = accum->dirty_off;
320                         remnant_size = accum->size - shrink_size;
321                         new_size     = remnant_size + size;
322                     } /* end if */
323                     else {
324                         new_size     = (H5F_ACCUM_MAX_SIZE / 2);
325                         shrink_size  = (H5F_ACCUM_MAX_SIZE / 2);
326                         remnant_size = accum->size - shrink_size;
327                     } /* end else */
328                 }     /* end else */
329             }         /* end else */
330 
331             /* Check if we need to flush accumulator data to file */
332             if (accum->dirty) {
333                 /* Check whether to accumulator will be prepended or appended */
334                 if (H5F_ACCUM_PREPEND == adjust) {
335                     /* Check if the dirty region overlaps the region to eliminate from the accumulator */
336                     if ((accum->size - shrink_size) < (accum->dirty_off + accum->dirty_len)) {
337                         /* Write out the dirty region from the metadata accumulator, with dispatch to driver
338                          */
339                         if (H5FD_write(file, H5FD_MEM_DEFAULT, (accum->loc + accum->dirty_off),
340                                        accum->dirty_len, (accum->buf + accum->dirty_off)) < 0)
341                             HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL, "file write failed")
342 
343                         /* Reset accumulator dirty flag */
344                         accum->dirty = FALSE;
345                     } /* end if */
346                 }     /* end if */
347                 else {
348                     /* Check if the dirty region overlaps the region to eliminate from the accumulator */
349                     if (shrink_size > accum->dirty_off) {
350                         /* Write out the dirty region from the metadata accumulator, with dispatch to driver
351                          */
352                         if (H5FD_write(file, H5FD_MEM_DEFAULT, (accum->loc + accum->dirty_off),
353                                        accum->dirty_len, (accum->buf + accum->dirty_off)) < 0)
354                             HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL, "file write failed")
355 
356                         /* Reset accumulator dirty flag */
357                         accum->dirty = FALSE;
358                     } /* end if */
359 
360                     /* Adjust dirty region tracking info */
361                     accum->dirty_off -= shrink_size;
362                 } /* end else */
363             }     /* end if */
364 
365             /* Trim the accumulator's use of its buffer */
366             accum->size = remnant_size;
367 
368             /* When appending, need to adjust location of accumulator */
369             if (H5F_ACCUM_APPEND == adjust) {
370                 /* Move remnant of accumulator down */
371                 HDmemmove(accum->buf, (accum->buf + shrink_size), remnant_size);
372 
373                 /* Adjust accumulator's location */
374                 accum->loc += shrink_size;
375             } /* end if */
376         }     /* end if */
377 
378         /* Check for accumulator needing to be reallocated */
379         if (new_size > accum->alloc_size) {
380             unsigned char *new_buf; /* New buffer to hold the accumulated metadata */
381 
382             /* Reallocate the metadata accumulator buffer */
383             if (NULL == (new_buf = H5FL_BLK_REALLOC(meta_accum, accum->buf, new_size)))
384                 HGOTO_ERROR(H5E_FILE, H5E_CANTALLOC, FAIL, "unable to allocate metadata accumulator buffer")
385 
386             /* Update accumulator info */
387             accum->buf        = new_buf;
388             accum->alloc_size = new_size;
389 
390             /* Clear the memory */
391             HDmemset(accum->buf + accum->size, 0, (accum->alloc_size - (accum->size + size)));
392         } /* end if */
393     }     /* end if */
394 
395 done:
396     FUNC_LEAVE_NOAPI(ret_value)
397 } /* end H5F__accum_adjust() */
398 
399 /*-------------------------------------------------------------------------
400  * Function:	H5F__accum_write
401  *
402  * Purpose:	Attempts to write some data to the metadata accumulator for
403  *              a file from a buffer.
404  *
405  * Return:	Non-negative on success/Negative on failure
406  *
407  * Programmer:	Quincey Koziol
408  *		Jan 10 2008
409  *
410  *-------------------------------------------------------------------------
411  */
412 herr_t
H5F__accum_write(H5F_shared_t * f_sh,H5FD_mem_t map_type,haddr_t addr,size_t size,const void * buf)413 H5F__accum_write(H5F_shared_t *f_sh, H5FD_mem_t map_type, haddr_t addr, size_t size, const void *buf)
414 {
415     H5FD_t *file;                /* File driver pointer */
416     herr_t  ret_value = SUCCEED; /* Return value */
417 
418     FUNC_ENTER_NOAPI(FAIL)
419 
420     /* Sanity checks */
421     HDassert(f_sh);
422     HDassert(H5F_SHARED_INTENT(f_sh) & H5F_ACC_RDWR);
423     HDassert(buf);
424 
425     /* Translate to file driver pointer */
426     file = f_sh->lf;
427 
428     /* Check for accumulating metadata */
429     if ((f_sh->feature_flags & H5FD_FEAT_ACCUMULATE_METADATA) && map_type != H5FD_MEM_DRAW) {
430         H5F_meta_accum_t *accum; /* Alias for file's metadata accumulator */
431 
432         /* Set up alias for file's metadata accumulator info */
433         accum = &f_sh->accum;
434 
435         if (size < H5F_ACCUM_MAX_SIZE) {
436             /* Sanity check */
437             HDassert(!accum->buf || (accum->alloc_size >= accum->size));
438 
439             /* Check if there is already metadata in the accumulator */
440             if (accum->size > 0) {
441                 /* Check if the new metadata adjoins the beginning of the current accumulator */
442                 if ((addr + size) == accum->loc) {
443                     /* Check if we need to adjust accumulator size */
444                     if (H5F__accum_adjust(accum, file, H5F_ACCUM_PREPEND, size) < 0)
445                         HGOTO_ERROR(H5E_IO, H5E_CANTRESIZE, FAIL, "can't adjust metadata accumulator")
446 
447                     /* Move the existing metadata to the proper location */
448                     HDmemmove(accum->buf + size, accum->buf, accum->size);
449 
450                     /* Copy the new metadata at the front */
451                     H5MM_memcpy(accum->buf, buf, size);
452 
453                     /* Set the new size & location of the metadata accumulator */
454                     accum->loc = addr;
455                     accum->size += size;
456 
457                     /* Adjust the dirty region and mark accumulator dirty */
458                     if (accum->dirty)
459                         accum->dirty_len = size + accum->dirty_off + accum->dirty_len;
460                     else {
461                         accum->dirty_len = size;
462                         accum->dirty     = TRUE;
463                     } /* end else */
464                     accum->dirty_off = 0;
465                 } /* end if */
466                 /* Check if the new metadata adjoins the end of the current accumulator */
467                 else if (addr == (accum->loc + accum->size)) {
468                     /* Check if we need to adjust accumulator size */
469                     if (H5F__accum_adjust(accum, file, H5F_ACCUM_APPEND, size) < 0)
470                         HGOTO_ERROR(H5E_IO, H5E_CANTRESIZE, FAIL, "can't adjust metadata accumulator")
471 
472                     /* Copy the new metadata to the end */
473                     H5MM_memcpy(accum->buf + accum->size, buf, size);
474 
475                     /* Adjust the dirty region and mark accumulator dirty */
476                     if (accum->dirty)
477                         accum->dirty_len = size + (accum->size - accum->dirty_off);
478                     else {
479                         accum->dirty_off = accum->size;
480                         accum->dirty_len = size;
481                         accum->dirty     = TRUE;
482                     } /* end else */
483 
484                     /* Set the new size of the metadata accumulator */
485                     accum->size += size;
486                 } /* end if */
487                 /* Check if the piece of metadata being written overlaps the metadata accumulator */
488                 else if (H5F_addr_overlap(addr, size, accum->loc, accum->size)) {
489                     size_t add_size; /* New size of the accumulator buffer */
490 
491                     /* Check if the new metadata is entirely within the current accumulator */
492                     if (addr >= accum->loc && (addr + size) <= (accum->loc + accum->size)) {
493                         size_t dirty_off = (size_t)(addr - accum->loc);
494 
495                         /* Copy the new metadata to the proper location within the accumulator */
496                         H5MM_memcpy(accum->buf + dirty_off, buf, size);
497 
498                         /* Adjust the dirty region and mark accumulator dirty */
499                         if (accum->dirty) {
500                             /* Check for new metadata starting before current dirty region */
501                             if (dirty_off <= accum->dirty_off) {
502                                 if ((dirty_off + size) <= (accum->dirty_off + accum->dirty_len))
503                                     accum->dirty_len = (accum->dirty_off + accum->dirty_len) - dirty_off;
504                                 else
505                                     accum->dirty_len = size;
506                                 accum->dirty_off = dirty_off;
507                             } /* end if */
508                             else {
509                                 if ((dirty_off + size) <= (accum->dirty_off + accum->dirty_len))
510                                     ; /* accum->dirty_len doesn't change */
511                                 else
512                                     accum->dirty_len = (dirty_off + size) - accum->dirty_off;
513                             } /* end else */
514                         }     /* end if */
515                         else {
516                             accum->dirty_off = dirty_off;
517                             accum->dirty_len = size;
518                             accum->dirty     = TRUE;
519                         } /* end else */
520                     }     /* end if */
521                     /* Check if the new metadata overlaps the beginning of the current accumulator */
522                     else if (addr < accum->loc && (addr + size) <= (accum->loc + accum->size)) {
523                         size_t old_offset; /* Offset of old data within the accumulator buffer */
524 
525                         /* Calculate the amount we will need to add to the accumulator size, based on the
526                          * amount of overlap */
527                         H5_CHECKED_ASSIGN(add_size, size_t, (accum->loc - addr), hsize_t);
528 
529                         /* Check if we need to adjust accumulator size */
530                         if (H5F__accum_adjust(accum, file, H5F_ACCUM_PREPEND, add_size) < 0)
531                             HGOTO_ERROR(H5E_IO, H5E_CANTRESIZE, FAIL, "can't adjust metadata accumulator")
532 
533                         /* Calculate the proper offset of the existing metadata */
534                         H5_CHECKED_ASSIGN(old_offset, size_t, (addr + size) - accum->loc, hsize_t);
535 
536                         /* Move the existing metadata to the proper location */
537                         HDmemmove(accum->buf + size, accum->buf + old_offset, (accum->size - old_offset));
538 
539                         /* Copy the new metadata at the front */
540                         H5MM_memcpy(accum->buf, buf, size);
541 
542                         /* Set the new size & location of the metadata accumulator */
543                         accum->loc = addr;
544                         accum->size += add_size;
545 
546                         /* Adjust the dirty region and mark accumulator dirty */
547                         if (accum->dirty) {
548                             size_t curr_dirty_end = add_size + accum->dirty_off + accum->dirty_len;
549 
550                             accum->dirty_off = 0;
551                             if (size <= curr_dirty_end)
552                                 accum->dirty_len = curr_dirty_end;
553                             else
554                                 accum->dirty_len = size;
555                         } /* end if */
556                         else {
557                             accum->dirty_off = 0;
558                             accum->dirty_len = size;
559                             accum->dirty     = TRUE;
560                         } /* end else */
561                     }     /* end if */
562                     /* Check if the new metadata overlaps the end of the current accumulator */
563                     else if (addr >= accum->loc && (addr + size) > (accum->loc + accum->size)) {
564                         size_t dirty_off; /* Offset of dirty region */
565 
566                         /* Calculate the amount we will need to add to the accumulator size, based on the
567                          * amount of overlap */
568                         H5_CHECKED_ASSIGN(add_size, size_t, (addr + size) - (accum->loc + accum->size),
569                                           hsize_t);
570 
571                         /* Check if we need to adjust accumulator size */
572                         if (H5F__accum_adjust(accum, file, H5F_ACCUM_APPEND, add_size) < 0)
573                             HGOTO_ERROR(H5E_IO, H5E_CANTRESIZE, FAIL, "can't adjust metadata accumulator")
574 
575                         /* Compute offset of dirty region (after adjusting accumulator) */
576                         dirty_off = (size_t)(addr - accum->loc);
577 
578                         /* Copy the new metadata to the end */
579                         H5MM_memcpy(accum->buf + dirty_off, buf, size);
580 
581                         /* Set the new size of the metadata accumulator */
582                         accum->size += add_size;
583 
584                         /* Adjust the dirty region and mark accumulator dirty */
585                         if (accum->dirty) {
586                             /* Check for new metadata starting before current dirty region */
587                             if (dirty_off <= accum->dirty_off) {
588                                 accum->dirty_off = dirty_off;
589                                 accum->dirty_len = size;
590                             } /* end if */
591                             else {
592                                 accum->dirty_len = (dirty_off + size) - accum->dirty_off;
593                             } /* end else */
594                         }     /* end if */
595                         else {
596                             accum->dirty_off = dirty_off;
597                             accum->dirty_len = size;
598                             accum->dirty     = TRUE;
599                         } /* end else */
600                     }     /* end if */
601                     /* New metadata overlaps both ends of the current accumulator */
602                     else {
603                         /* Check if we need more buffer space */
604                         if (size > accum->alloc_size) {
605                             size_t new_alloc_size; /* New size of accumulator */
606 
607                             /* Adjust the buffer size to be a power of 2 that is large enough to hold data */
608                             new_alloc_size = (size_t)1 << (1 + H5VM_log2_gen((uint64_t)(size - 1)));
609 
610                             /* Reallocate the metadata accumulator buffer */
611                             if (NULL ==
612                                 (accum->buf = H5FL_BLK_REALLOC(meta_accum, accum->buf, new_alloc_size)))
613                                 HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
614                                             "unable to allocate metadata accumulator buffer")
615 
616                             /* Note the new buffer size */
617                             accum->alloc_size = new_alloc_size;
618 
619                             /* Clear the memory */
620                             HDmemset(accum->buf + size, 0, (accum->alloc_size - size));
621                         } /* end if */
622 
623                         /* Copy the new metadata to the buffer */
624                         H5MM_memcpy(accum->buf, buf, size);
625 
626                         /* Set the new size & location of the metadata accumulator */
627                         accum->loc  = addr;
628                         accum->size = size;
629 
630                         /* Adjust the dirty region and mark accumulator dirty */
631                         accum->dirty_off = 0;
632                         accum->dirty_len = size;
633                         accum->dirty     = TRUE;
634                     } /* end else */
635                 }     /* end if */
636                 /* New piece of metadata doesn't adjoin or overlap the existing accumulator */
637                 else {
638                     /* Write out the existing metadata accumulator, with dispatch to driver */
639                     if (accum->dirty) {
640                         if (H5FD_write(file, H5FD_MEM_DEFAULT, accum->loc + accum->dirty_off,
641                                        accum->dirty_len, accum->buf + accum->dirty_off) < 0)
642                             HGOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "file write failed")
643 
644                         /* Reset accumulator dirty flag */
645                         accum->dirty = FALSE;
646                     } /* end if */
647 
648                     /* Cache the new piece of metadata */
649                     /* Check if we need to resize the buffer */
650                     if (size > accum->alloc_size) {
651                         size_t new_size;   /* New size of accumulator */
652                         size_t clear_size; /* Size of memory that needs clearing */
653 
654                         /* Adjust the buffer size to be a power of 2 that is large enough to hold data */
655                         new_size = (size_t)1 << (1 + H5VM_log2_gen((uint64_t)(size - 1)));
656 
657                         /* Grow the metadata accumulator buffer */
658                         if (NULL == (accum->buf = H5FL_BLK_REALLOC(meta_accum, accum->buf, new_size)))
659                             HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
660                                         "unable to allocate metadata accumulator buffer")
661 
662                         /* Note the new buffer size */
663                         accum->alloc_size = new_size;
664 
665                         /* Clear the memory */
666                         clear_size = MAX(accum->size, size);
667                         HDmemset(accum->buf + clear_size, 0, (accum->alloc_size - clear_size));
668                     } /* end if */
669                     else {
670                         /* Check if we should shrink the accumulator buffer */
671                         if (size < (accum->alloc_size / H5F_ACCUM_THROTTLE) &&
672                             accum->alloc_size > H5F_ACCUM_THRESHOLD) {
673                             size_t tmp_size =
674                                 (accum->alloc_size / H5F_ACCUM_THROTTLE); /* New size of accumulator buffer */
675 
676                             /* Shrink the accumulator buffer */
677                             if (NULL == (accum->buf = H5FL_BLK_REALLOC(meta_accum, accum->buf, tmp_size)))
678                                 HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
679                                             "unable to allocate metadata accumulator buffer")
680 
681                             /* Note the new buffer size */
682                             accum->alloc_size = tmp_size;
683                         } /* end if */
684                     }     /* end else */
685 
686                     /* Update the metadata accumulator information */
687                     accum->loc  = addr;
688                     accum->size = size;
689 
690                     /* Store the piece of metadata in the accumulator */
691                     H5MM_memcpy(accum->buf, buf, size);
692 
693                     /* Adjust the dirty region and mark accumulator dirty */
694                     accum->dirty_off = 0;
695                     accum->dirty_len = size;
696                     accum->dirty     = TRUE;
697                 } /* end else */
698             }     /* end if */
699             /* No metadata in the accumulator, grab this piece and keep it */
700             else {
701                 /* Check if we need to reallocate the buffer */
702                 if (size > accum->alloc_size) {
703                     size_t new_size; /* New size of accumulator */
704 
705                     /* Adjust the buffer size to be a power of 2 that is large enough to hold data */
706                     new_size = (size_t)1 << (1 + H5VM_log2_gen((uint64_t)(size - 1)));
707 
708                     /* Reallocate the metadata accumulator buffer */
709                     if (NULL == (accum->buf = H5FL_BLK_REALLOC(meta_accum, accum->buf, new_size)))
710                         HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
711                                     "unable to allocate metadata accumulator buffer")
712 
713                     /* Note the new buffer size */
714                     accum->alloc_size = new_size;
715 
716                     /* Clear the memory */
717                     HDmemset(accum->buf + size, 0, (accum->alloc_size - size));
718                 } /* end if */
719 
720                 /* Update the metadata accumulator information */
721                 accum->loc  = addr;
722                 accum->size = size;
723 
724                 /* Store the piece of metadata in the accumulator */
725                 H5MM_memcpy(accum->buf, buf, size);
726 
727                 /* Adjust the dirty region and mark accumulator dirty */
728                 accum->dirty_off = 0;
729                 accum->dirty_len = size;
730                 accum->dirty     = TRUE;
731             } /* end else */
732         }     /* end if */
733         else {
734             /* Make certain that data in accumulator is visible before new write */
735             if ((H5F_SHARED_INTENT(f_sh) & H5F_ACC_SWMR_WRITE) > 0)
736                 /* Flush if dirty and reset accumulator */
737                 if (H5F__accum_reset(f_sh, TRUE) < 0)
738                     HGOTO_ERROR(H5E_IO, H5E_CANTRESET, FAIL, "can't reset accumulator")
739 
740             /* Write the data */
741             if (H5FD_write(file, map_type, addr, size, buf) < 0)
742                 HGOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "file write failed")
743 
744             /* Check for overlap w/accumulator */
745             /* (Note that this could be improved by updating the accumulator
746              *  with [some of] the information just read in. -QAK)
747              */
748             if (H5F_addr_overlap(addr, size, accum->loc, accum->size)) {
749                 /* Check for write starting before beginning of accumulator */
750                 if (H5F_addr_le(addr, accum->loc)) {
751                     /* Check for write ending within accumulator */
752                     if (H5F_addr_le(addr + size, accum->loc + accum->size)) {
753                         size_t overlap_size; /* Size of overlapping region */
754 
755                         /* Compute overlap size */
756                         overlap_size = (size_t)((addr + size) - accum->loc);
757 
758                         /* Check for dirty region */
759                         if (accum->dirty) {
760                             haddr_t dirty_start =
761                                 accum->loc + accum->dirty_off; /* File address of start of dirty region */
762                             haddr_t dirty_end =
763                                 dirty_start + accum->dirty_len; /* File address of end of dirty region */
764 
765                             /* Check if entire dirty region is overwritten */
766                             if (H5F_addr_le(dirty_end, addr + size)) {
767                                 accum->dirty     = FALSE;
768                                 accum->dirty_len = 0;
769                             } /* end if */
770                             else {
771                                 /* Check for dirty region falling after write */
772                                 if (H5F_addr_le(addr + size, dirty_start))
773                                     accum->dirty_off = overlap_size;
774                                 else { /* Dirty region overlaps w/written region */
775                                     accum->dirty_off = 0;
776                                     accum->dirty_len -= (size_t)((addr + size) - dirty_start);
777                                 } /* end else */
778                             }     /* end if */
779                         }         /* end if */
780 
781                         /* Trim bottom of accumulator off */
782                         accum->loc += overlap_size;
783                         accum->size -= overlap_size;
784                         HDmemmove(accum->buf, accum->buf + overlap_size, accum->size);
785                     }      /* end if */
786                     else { /* Access covers whole accumulator */
787                         /* Reset accumulator, but don't flush */
788                         if (H5F__accum_reset(f_sh, FALSE) < 0)
789                             HGOTO_ERROR(H5E_IO, H5E_CANTRESET, FAIL, "can't reset accumulator")
790                     }                    /* end else */
791                 }                        /* end if */
792                 else {                   /* Write starts after beginning of accumulator */
793                     size_t overlap_size; /* Size of overlapping region */
794 
795                     /* Sanity check */
796                     HDassert(H5F_addr_gt(addr + size, accum->loc + accum->size));
797 
798                     /* Compute overlap size */
799                     overlap_size = (size_t)((accum->loc + accum->size) - addr);
800 
801                     /* Check for dirty region */
802                     if (accum->dirty) {
803                         haddr_t dirty_start =
804                             accum->loc + accum->dirty_off; /* File address of start of dirty region */
805                         haddr_t dirty_end =
806                             dirty_start + accum->dirty_len; /* File address of end of dirty region */
807 
808                         /* Check if entire dirty region is overwritten */
809                         if (H5F_addr_ge(dirty_start, addr)) {
810                             accum->dirty     = FALSE;
811                             accum->dirty_len = 0;
812                         } /* end if */
813                         else {
814                             /* Check for dirty region falling before write */
815                             if (H5F_addr_le(dirty_end, addr))
816                                 ; /* noop */
817                             else  /* Dirty region overlaps w/written region */
818                                 accum->dirty_len = (size_t)(addr - dirty_start);
819                         } /* end if */
820                     }     /* end if */
821 
822                     /* Trim top of accumulator off */
823                     accum->size -= overlap_size;
824                 } /* end else */
825             }     /* end if */
826         }         /* end else */
827     }             /* end if */
828     else {
829         /* Write the data */
830         if (H5FD_write(file, map_type, addr, size, buf) < 0)
831             HGOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "file write failed")
832     } /* end else */
833 
834 done:
835     FUNC_LEAVE_NOAPI(ret_value)
836 } /* end H5F__accum_write() */
837 
838 /*-------------------------------------------------------------------------
839  * Function:    H5F__accum_free
840  *
841  * Purpose:     Check for free space invalidating [part of] a metadata
842  *              accumulator.
843  *
844  * Return:      Non-negative on success/Negative on failure
845  *
846  * Programmer:  Quincey Koziol
847  *              Jan 10 2008
848  *
849  *-------------------------------------------------------------------------
850  */
851 herr_t
H5F__accum_free(H5F_shared_t * f_sh,H5FD_mem_t H5_ATTR_UNUSED type,haddr_t addr,hsize_t size)852 H5F__accum_free(H5F_shared_t *f_sh, H5FD_mem_t H5_ATTR_UNUSED type, haddr_t addr, hsize_t size)
853 {
854     H5F_meta_accum_t *accum;               /* Alias for file's metadata accumulator */
855     H5FD_t *          file;                /* File driver pointer */
856     herr_t            ret_value = SUCCEED; /* Return value */
857 
858     FUNC_ENTER_PACKAGE
859 
860     /* check arguments */
861     HDassert(f_sh);
862 
863     /* Set up alias for file's metadata accumulator info */
864     accum = &f_sh->accum;
865 
866     /* Translate to file driver pointer */
867     file = f_sh->lf;
868 
869     /* Adjust the metadata accumulator to remove the freed block, if it overlaps */
870     if ((f_sh->feature_flags & H5FD_FEAT_ACCUMULATE_METADATA) &&
871         H5F_addr_overlap(addr, size, accum->loc, accum->size)) {
872         size_t overlap_size; /* Size of overlap with accumulator */
873 
874         /* Sanity check */
875         /* (The metadata accumulator should not intersect w/raw data */
876         HDassert(H5FD_MEM_DRAW != type);
877         HDassert(H5FD_MEM_GHEAP != type); /* (global heap data is being treated as raw data currently) */
878 
879         /* Check for overlapping the beginning of the accumulator */
880         if (H5F_addr_le(addr, accum->loc)) {
881             /* Check for completely overlapping the accumulator */
882             if (H5F_addr_ge(addr + size, accum->loc + accum->size)) {
883                 /* Reset the accumulator, but don't free buffer */
884                 accum->loc   = HADDR_UNDEF;
885                 accum->size  = 0;
886                 accum->dirty = FALSE;
887             } /* end if */
888             /* Block to free must end within the accumulator */
889             else {
890                 size_t new_accum_size; /* Size of new accumulator buffer */
891 
892                 /* Calculate the size of the overlap with the accumulator, etc. */
893                 H5_CHECKED_ASSIGN(overlap_size, size_t, (addr + size) - accum->loc, haddr_t);
894                 new_accum_size = accum->size - overlap_size;
895 
896                 /* Move the accumulator buffer information to eliminate the freed block */
897                 HDmemmove(accum->buf, accum->buf + overlap_size, new_accum_size);
898 
899                 /* Adjust the accumulator information */
900                 accum->loc += overlap_size;
901                 accum->size = new_accum_size;
902 
903                 /* Adjust the dirty region and possibly mark accumulator clean */
904                 if (accum->dirty) {
905                     /* Check if block freed is entirely before dirty region */
906                     if (overlap_size < accum->dirty_off)
907                         accum->dirty_off -= overlap_size;
908                     else {
909                         /* Check if block freed ends within dirty region */
910                         if (overlap_size < (accum->dirty_off + accum->dirty_len)) {
911                             accum->dirty_len = (accum->dirty_off + accum->dirty_len) - overlap_size;
912                             accum->dirty_off = 0;
913                         } /* end if */
914                         /* Block freed encompasses dirty region */
915                         else
916                             accum->dirty = FALSE;
917                     } /* end else */
918                 }     /* end if */
919             }         /* end else */
920         }             /* end if */
921         /* Block to free must start within the accumulator */
922         else {
923             haddr_t dirty_end   = accum->loc + accum->dirty_off + accum->dirty_len;
924             haddr_t dirty_start = accum->loc + accum->dirty_off;
925 
926             /* Calculate the size of the overlap with the accumulator */
927             H5_CHECKED_ASSIGN(overlap_size, size_t, (accum->loc + accum->size) - addr, haddr_t);
928 
929             /* Check if block to free begins before end of dirty region */
930             if (accum->dirty && H5F_addr_lt(addr, dirty_end)) {
931                 haddr_t tail_addr;
932 
933                 /* Calculate the address of the tail to write */
934                 tail_addr = addr + size;
935 
936                 /* Check if the block to free begins before dirty region */
937                 if (H5F_addr_lt(addr, dirty_start)) {
938                     /* Check if block to free is entirely before dirty region */
939                     if (H5F_addr_le(tail_addr, dirty_start)) {
940                         /* Write out the entire dirty region of the accumulator */
941                         if (H5FD_write(file, H5FD_MEM_DEFAULT, dirty_start, accum->dirty_len,
942                                        accum->buf + accum->dirty_off) < 0)
943                             HGOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "file write failed")
944                     } /* end if */
945                     /* Block to free overlaps with some/all of dirty region */
946                     /* Check for unfreed dirty region to write */
947                     else if (H5F_addr_lt(tail_addr, dirty_end)) {
948                         size_t write_size;
949                         size_t dirty_delta;
950 
951                         write_size  = (size_t)(dirty_end - tail_addr);
952                         dirty_delta = accum->dirty_len - write_size;
953 
954                         HDassert(write_size > 0);
955 
956                         /* Write out the unfreed dirty region of the accumulator */
957                         if (H5FD_write(file, H5FD_MEM_DEFAULT, dirty_start + dirty_delta, write_size,
958                                        accum->buf + accum->dirty_off + dirty_delta) < 0)
959                             HGOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "file write failed")
960                     } /* end if */
961 
962                     /* Reset dirty flag */
963                     accum->dirty = FALSE;
964                 } /* end if */
965                 /* Block to free begins at beginning of or in middle of dirty region */
966                 else {
967                     /* Check if block to free ends before end of dirty region */
968                     if (H5F_addr_lt(tail_addr, dirty_end)) {
969                         size_t write_size;
970                         size_t dirty_delta;
971 
972                         write_size  = (size_t)(dirty_end - tail_addr);
973                         dirty_delta = accum->dirty_len - write_size;
974 
975                         HDassert(write_size > 0);
976 
977                         /* Write out the unfreed end of the dirty region of the accumulator */
978                         if (H5FD_write(file, H5FD_MEM_DEFAULT, dirty_start + dirty_delta, write_size,
979                                        accum->buf + accum->dirty_off + dirty_delta) < 0)
980                             HGOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "file write failed")
981                     } /* end if */
982 
983                     /* Check for block to free beginning at same location as dirty region */
984                     if (H5F_addr_eq(addr, dirty_start)) {
985                         /* Reset dirty flag */
986                         accum->dirty = FALSE;
987                     } /* end if */
988                     /* Block to free eliminates end of dirty region */
989                     else {
990                         accum->dirty_len = (size_t)(addr - dirty_start);
991                     } /* end else */
992                 }     /* end else */
993 
994             } /* end if */
995 
996             /* Adjust the accumulator information */
997             accum->size = accum->size - overlap_size;
998         } /* end else */
999     }     /* end if */
1000 
1001 done:
1002     FUNC_LEAVE_NOAPI(ret_value)
1003 } /* end H5F__accum_free() */
1004 
1005 /*-------------------------------------------------------------------------
1006  * Function:	H5F__accum_flush
1007  *
1008  * Purpose:	Flush the metadata accumulator to the file
1009  *
1010  * Return:	Non-negative on success/Negative on failure
1011  *
1012  * Programmer:	Quincey Koziol
1013  *		Jan 10 2008
1014  *
1015  *-------------------------------------------------------------------------
1016  */
1017 herr_t
H5F__accum_flush(H5F_shared_t * f_sh)1018 H5F__accum_flush(H5F_shared_t *f_sh)
1019 {
1020     herr_t ret_value = SUCCEED; /* Return value */
1021 
1022     FUNC_ENTER_NOAPI(FAIL)
1023 
1024     /* Sanity checks */
1025     HDassert(f_sh);
1026 
1027     /* Check if we need to flush out the metadata accumulator */
1028     if ((f_sh->feature_flags & H5FD_FEAT_ACCUMULATE_METADATA) && f_sh->accum.dirty) {
1029         H5FD_t *file; /* File driver pointer */
1030 
1031         /* Translate to file driver pointer */
1032         file = f_sh->lf;
1033 
1034         /* Flush the metadata contents */
1035         if (H5FD_write(file, H5FD_MEM_DEFAULT, f_sh->accum.loc + f_sh->accum.dirty_off, f_sh->accum.dirty_len,
1036                        f_sh->accum.buf + f_sh->accum.dirty_off) < 0)
1037             HGOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "file write failed")
1038 
1039         /* Reset the dirty flag */
1040         f_sh->accum.dirty = FALSE;
1041     } /* end if */
1042 
1043 done:
1044     FUNC_LEAVE_NOAPI(ret_value)
1045 } /* end H5F__accum_flush() */
1046 
1047 /*-------------------------------------------------------------------------
1048  * Function:	H5F__accum_reset
1049  *
1050  * Purpose:	Reset the metadata accumulator for the file
1051  *
1052  * Return:	Non-negative on success/Negative on failure
1053  *
1054  * Programmer:	Quincey Koziol
1055  *		Jan 10 2008
1056  *
1057  *-------------------------------------------------------------------------
1058  */
1059 herr_t
H5F__accum_reset(H5F_shared_t * f_sh,hbool_t flush)1060 H5F__accum_reset(H5F_shared_t *f_sh, hbool_t flush)
1061 {
1062     herr_t ret_value = SUCCEED; /* Return value */
1063 
1064     FUNC_ENTER_PACKAGE
1065 
1066     /* Sanity checks */
1067     HDassert(f_sh);
1068 
1069     /* Flush any dirty data in accumulator, if requested */
1070     if (flush)
1071         if (H5F__accum_flush(f_sh) < 0)
1072             HGOTO_ERROR(H5E_FILE, H5E_CANTFLUSH, FAIL, "can't flush metadata accumulator")
1073 
1074     /* Check if we need to reset the metadata accumulator information */
1075     if (f_sh->feature_flags & H5FD_FEAT_ACCUMULATE_METADATA) {
1076         /* Free the buffer */
1077         if (f_sh->accum.buf)
1078             f_sh->accum.buf = H5FL_BLK_FREE(meta_accum, f_sh->accum.buf);
1079 
1080         /* Reset the buffer sizes & location */
1081         f_sh->accum.alloc_size = f_sh->accum.size = 0;
1082         f_sh->accum.loc                           = HADDR_UNDEF;
1083         f_sh->accum.dirty                         = FALSE;
1084         f_sh->accum.dirty_len                     = 0;
1085     } /* end if */
1086 
1087 done:
1088     FUNC_LEAVE_NOAPI(ret_value)
1089 } /* end H5F__accum_reset() */
1090