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