FreeBSD ZFS
The Zettabyte File System
|
00001 /* 00002 * CDDL HEADER START 00003 * 00004 * The contents of this file are subject to the terms of the 00005 * Common Development and Distribution License (the "License"). 00006 * You may not use this file except in compliance with the License. 00007 * 00008 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 00009 * or http://www.opensolaris.org/os/licensing. 00010 * See the License for the specific language governing permissions 00011 * and limitations under the License. 00012 * 00013 * When distributing Covered Code, include this CDDL HEADER in each 00014 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 00015 * If applicable, add the following below this CDDL HEADER, with the 00016 * fields enclosed by brackets "[]" replaced with your own identifying 00017 * information: Portions Copyright [yyyy] [name of copyright owner] 00018 * 00019 * CDDL HEADER END 00020 */ 00021 /* 00022 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 00023 * Copyright (c) 2012 by Delphix. All rights reserved. 00024 */ 00025 00026 #include <sys/bpobj.h> 00027 #include <sys/zfs_context.h> 00028 #include <sys/refcount.h> 00029 #include <sys/dsl_pool.h> 00030 #include <sys/zfeature.h> 00031 #include <sys/zap.h> 00032 00036 uint64_t 00037 bpobj_alloc_empty(objset_t *os, int blocksize, dmu_tx_t *tx) 00038 { 00039 zfeature_info_t *empty_bpobj_feat = 00040 &spa_feature_table[SPA_FEATURE_EMPTY_BPOBJ]; 00041 spa_t *spa = dmu_objset_spa(os); 00042 dsl_pool_t *dp = dmu_objset_pool(os); 00043 00044 if (spa_feature_is_enabled(spa, empty_bpobj_feat)) { 00045 if (!spa_feature_is_active(spa, empty_bpobj_feat)) { 00046 ASSERT0(dp->dp_empty_bpobj); 00047 dp->dp_empty_bpobj = 00048 bpobj_alloc(os, SPA_MAXBLOCKSIZE, tx); 00049 VERIFY(zap_add(os, 00050 DMU_POOL_DIRECTORY_OBJECT, 00051 DMU_POOL_EMPTY_BPOBJ, sizeof (uint64_t), 1, 00052 &dp->dp_empty_bpobj, tx) == 0); 00053 } 00054 spa_feature_incr(spa, empty_bpobj_feat, tx); 00055 ASSERT(dp->dp_empty_bpobj != 0); 00056 return (dp->dp_empty_bpobj); 00057 } else { 00058 return (bpobj_alloc(os, blocksize, tx)); 00059 } 00060 } 00061 00062 void 00063 bpobj_decr_empty(objset_t *os, dmu_tx_t *tx) 00064 { 00065 zfeature_info_t *empty_bpobj_feat = 00066 &spa_feature_table[SPA_FEATURE_EMPTY_BPOBJ]; 00067 dsl_pool_t *dp = dmu_objset_pool(os); 00068 00069 spa_feature_decr(dmu_objset_spa(os), empty_bpobj_feat, tx); 00070 if (!spa_feature_is_active(dmu_objset_spa(os), empty_bpobj_feat)) { 00071 VERIFY3U(0, ==, zap_remove(dp->dp_meta_objset, 00072 DMU_POOL_DIRECTORY_OBJECT, 00073 DMU_POOL_EMPTY_BPOBJ, tx)); 00074 VERIFY3U(0, ==, dmu_object_free(os, dp->dp_empty_bpobj, tx)); 00075 dp->dp_empty_bpobj = 0; 00076 } 00077 } 00078 00079 uint64_t 00080 bpobj_alloc(objset_t *os, int blocksize, dmu_tx_t *tx) 00081 { 00082 int size; 00083 00084 if (spa_version(dmu_objset_spa(os)) < SPA_VERSION_BPOBJ_ACCOUNT) 00085 size = BPOBJ_SIZE_V0; 00086 else if (spa_version(dmu_objset_spa(os)) < SPA_VERSION_DEADLISTS) 00087 size = BPOBJ_SIZE_V1; 00088 else 00089 size = sizeof (bpobj_phys_t); 00090 00091 return (dmu_object_alloc(os, DMU_OT_BPOBJ, blocksize, 00092 DMU_OT_BPOBJ_HDR, size, tx)); 00093 } 00094 00095 void 00096 bpobj_free(objset_t *os, uint64_t obj, dmu_tx_t *tx) 00097 { 00098 int64_t i; 00099 bpobj_t bpo; 00100 dmu_object_info_t doi; 00101 int epb; 00102 dmu_buf_t *dbuf = NULL; 00103 00104 ASSERT(obj != dmu_objset_pool(os)->dp_empty_bpobj); 00105 VERIFY3U(0, ==, bpobj_open(&bpo, os, obj)); 00106 00107 mutex_enter(&bpo.bpo_lock); 00108 00109 if (!bpo.bpo_havesubobj || bpo.bpo_phys->bpo_subobjs == 0) 00110 goto out; 00111 00112 VERIFY3U(0, ==, dmu_object_info(os, bpo.bpo_phys->bpo_subobjs, &doi)); 00113 epb = doi.doi_data_block_size / sizeof (uint64_t); 00114 00115 for (i = bpo.bpo_phys->bpo_num_subobjs - 1; i >= 0; i--) { 00116 uint64_t *objarray; 00117 uint64_t offset, blkoff; 00118 00119 offset = i * sizeof (uint64_t); 00120 blkoff = P2PHASE(i, epb); 00121 00122 if (dbuf == NULL || dbuf->db_offset > offset) { 00123 if (dbuf) 00124 dmu_buf_rele(dbuf, FTAG); 00125 VERIFY3U(0, ==, dmu_buf_hold(os, 00126 bpo.bpo_phys->bpo_subobjs, offset, FTAG, &dbuf, 0)); 00127 } 00128 00129 ASSERT3U(offset, >=, dbuf->db_offset); 00130 ASSERT3U(offset, <, dbuf->db_offset + dbuf->db_size); 00131 00132 objarray = dbuf->db_data; 00133 bpobj_free(os, objarray[blkoff], tx); 00134 } 00135 if (dbuf) { 00136 dmu_buf_rele(dbuf, FTAG); 00137 dbuf = NULL; 00138 } 00139 VERIFY3U(0, ==, dmu_object_free(os, bpo.bpo_phys->bpo_subobjs, tx)); 00140 00141 out: 00142 mutex_exit(&bpo.bpo_lock); 00143 bpobj_close(&bpo); 00144 00145 VERIFY3U(0, ==, dmu_object_free(os, obj, tx)); 00146 } 00147 00148 int 00149 bpobj_open(bpobj_t *bpo, objset_t *os, uint64_t object) 00150 { 00151 dmu_object_info_t doi; 00152 int err; 00153 00154 err = dmu_object_info(os, object, &doi); 00155 if (err) 00156 return (err); 00157 00158 bzero(bpo, sizeof (*bpo)); 00159 mutex_init(&bpo->bpo_lock, NULL, MUTEX_DEFAULT, NULL); 00160 00161 ASSERT(bpo->bpo_dbuf == NULL); 00162 ASSERT(bpo->bpo_phys == NULL); 00163 ASSERT(object != 0); 00164 ASSERT3U(doi.doi_type, ==, DMU_OT_BPOBJ); 00165 ASSERT3U(doi.doi_bonus_type, ==, DMU_OT_BPOBJ_HDR); 00166 00167 err = dmu_bonus_hold(os, object, bpo, &bpo->bpo_dbuf); 00168 if (err) 00169 return (err); 00170 00171 bpo->bpo_os = os; 00172 bpo->bpo_object = object; 00173 bpo->bpo_epb = doi.doi_data_block_size >> SPA_BLKPTRSHIFT; 00174 bpo->bpo_havecomp = (doi.doi_bonus_size > BPOBJ_SIZE_V0); 00175 bpo->bpo_havesubobj = (doi.doi_bonus_size > BPOBJ_SIZE_V1); 00176 bpo->bpo_phys = bpo->bpo_dbuf->db_data; 00177 return (0); 00178 } 00179 00180 void 00181 bpobj_close(bpobj_t *bpo) 00182 { 00183 /* Lame workaround for closing a bpobj that was never opened. */ 00184 if (bpo->bpo_object == 0) 00185 return; 00186 00187 dmu_buf_rele(bpo->bpo_dbuf, bpo); 00188 if (bpo->bpo_cached_dbuf != NULL) 00189 dmu_buf_rele(bpo->bpo_cached_dbuf, bpo); 00190 bpo->bpo_dbuf = NULL; 00191 bpo->bpo_phys = NULL; 00192 bpo->bpo_cached_dbuf = NULL; 00193 bpo->bpo_object = 0; 00194 00195 mutex_destroy(&bpo->bpo_lock); 00196 } 00197 00198 static int 00199 bpobj_iterate_impl(bpobj_t *bpo, bpobj_itor_t func, void *arg, dmu_tx_t *tx, 00200 boolean_t free) 00201 { 00202 dmu_object_info_t doi; 00203 int epb; 00204 int64_t i; 00205 int err = 0; 00206 dmu_buf_t *dbuf = NULL; 00207 00208 mutex_enter(&bpo->bpo_lock); 00209 00210 if (free) 00211 dmu_buf_will_dirty(bpo->bpo_dbuf, tx); 00212 00213 for (i = bpo->bpo_phys->bpo_num_blkptrs - 1; i >= 0; i--) { 00214 blkptr_t *bparray; 00215 blkptr_t *bp; 00216 uint64_t offset, blkoff; 00217 00218 offset = i * sizeof (blkptr_t); 00219 blkoff = P2PHASE(i, bpo->bpo_epb); 00220 00221 if (dbuf == NULL || dbuf->db_offset > offset) { 00222 if (dbuf) 00223 dmu_buf_rele(dbuf, FTAG); 00224 err = dmu_buf_hold(bpo->bpo_os, bpo->bpo_object, offset, 00225 FTAG, &dbuf, 0); 00226 if (err) 00227 break; 00228 } 00229 00230 ASSERT3U(offset, >=, dbuf->db_offset); 00231 ASSERT3U(offset, <, dbuf->db_offset + dbuf->db_size); 00232 00233 bparray = dbuf->db_data; 00234 bp = &bparray[blkoff]; 00235 err = func(arg, bp, tx); 00236 if (err) 00237 break; 00238 if (free) { 00239 bpo->bpo_phys->bpo_bytes -= 00240 bp_get_dsize_sync(dmu_objset_spa(bpo->bpo_os), bp); 00241 ASSERT3S(bpo->bpo_phys->bpo_bytes, >=, 0); 00242 if (bpo->bpo_havecomp) { 00243 bpo->bpo_phys->bpo_comp -= BP_GET_PSIZE(bp); 00244 bpo->bpo_phys->bpo_uncomp -= BP_GET_UCSIZE(bp); 00245 } 00246 bpo->bpo_phys->bpo_num_blkptrs--; 00247 ASSERT3S(bpo->bpo_phys->bpo_num_blkptrs, >=, 0); 00248 } 00249 } 00250 if (dbuf) { 00251 dmu_buf_rele(dbuf, FTAG); 00252 dbuf = NULL; 00253 } 00254 if (free) { 00255 i++; 00256 VERIFY3U(0, ==, dmu_free_range(bpo->bpo_os, bpo->bpo_object, 00257 i * sizeof (blkptr_t), -1ULL, tx)); 00258 } 00259 if (err || !bpo->bpo_havesubobj || bpo->bpo_phys->bpo_subobjs == 0) 00260 goto out; 00261 00262 ASSERT(bpo->bpo_havecomp); 00263 err = dmu_object_info(bpo->bpo_os, bpo->bpo_phys->bpo_subobjs, &doi); 00264 if (err) { 00265 mutex_exit(&bpo->bpo_lock); 00266 return (err); 00267 } 00268 epb = doi.doi_data_block_size / sizeof (uint64_t); 00269 00270 for (i = bpo->bpo_phys->bpo_num_subobjs - 1; i >= 0; i--) { 00271 uint64_t *objarray; 00272 uint64_t offset, blkoff; 00273 bpobj_t sublist; 00274 uint64_t used_before, comp_before, uncomp_before; 00275 uint64_t used_after, comp_after, uncomp_after; 00276 00277 offset = i * sizeof (uint64_t); 00278 blkoff = P2PHASE(i, epb); 00279 00280 if (dbuf == NULL || dbuf->db_offset > offset) { 00281 if (dbuf) 00282 dmu_buf_rele(dbuf, FTAG); 00283 err = dmu_buf_hold(bpo->bpo_os, 00284 bpo->bpo_phys->bpo_subobjs, offset, FTAG, &dbuf, 0); 00285 if (err) 00286 break; 00287 } 00288 00289 ASSERT3U(offset, >=, dbuf->db_offset); 00290 ASSERT3U(offset, <, dbuf->db_offset + dbuf->db_size); 00291 00292 objarray = dbuf->db_data; 00293 err = bpobj_open(&sublist, bpo->bpo_os, objarray[blkoff]); 00294 if (err) 00295 break; 00296 if (free) { 00297 err = bpobj_space(&sublist, 00298 &used_before, &comp_before, &uncomp_before); 00299 if (err) 00300 break; 00301 } 00302 err = bpobj_iterate_impl(&sublist, func, arg, tx, free); 00303 if (free) { 00304 VERIFY3U(0, ==, bpobj_space(&sublist, 00305 &used_after, &comp_after, &uncomp_after)); 00306 bpo->bpo_phys->bpo_bytes -= used_before - used_after; 00307 ASSERT3S(bpo->bpo_phys->bpo_bytes, >=, 0); 00308 bpo->bpo_phys->bpo_comp -= comp_before - comp_after; 00309 bpo->bpo_phys->bpo_uncomp -= 00310 uncomp_before - uncomp_after; 00311 } 00312 00313 bpobj_close(&sublist); 00314 if (err) 00315 break; 00316 if (free) { 00317 err = dmu_object_free(bpo->bpo_os, 00318 objarray[blkoff], tx); 00319 if (err) 00320 break; 00321 bpo->bpo_phys->bpo_num_subobjs--; 00322 ASSERT3S(bpo->bpo_phys->bpo_num_subobjs, >=, 0); 00323 } 00324 } 00325 if (dbuf) { 00326 dmu_buf_rele(dbuf, FTAG); 00327 dbuf = NULL; 00328 } 00329 if (free) { 00330 VERIFY3U(0, ==, dmu_free_range(bpo->bpo_os, 00331 bpo->bpo_phys->bpo_subobjs, 00332 (i + 1) * sizeof (uint64_t), -1ULL, tx)); 00333 } 00334 00335 out: 00336 /* If there are no entries, there should be no bytes. */ 00337 ASSERT(bpo->bpo_phys->bpo_num_blkptrs > 0 || 00338 (bpo->bpo_havesubobj && bpo->bpo_phys->bpo_num_subobjs > 0) || 00339 bpo->bpo_phys->bpo_bytes == 0); 00340 00341 mutex_exit(&bpo->bpo_lock); 00342 return (err); 00343 } 00344 00349 int 00350 bpobj_iterate(bpobj_t *bpo, bpobj_itor_t func, void *arg, dmu_tx_t *tx) 00351 { 00352 return (bpobj_iterate_impl(bpo, func, arg, tx, B_TRUE)); 00353 } 00354 00358 int 00359 bpobj_iterate_nofree(bpobj_t *bpo, bpobj_itor_t func, void *arg, dmu_tx_t *tx) 00360 { 00361 return (bpobj_iterate_impl(bpo, func, arg, tx, B_FALSE)); 00362 } 00363 00364 void 00365 bpobj_enqueue_subobj(bpobj_t *bpo, uint64_t subobj, dmu_tx_t *tx) 00366 { 00367 bpobj_t subbpo; 00368 uint64_t used, comp, uncomp, subsubobjs; 00369 00370 ASSERT(bpo->bpo_havesubobj); 00371 ASSERT(bpo->bpo_havecomp); 00372 ASSERT(bpo->bpo_object != dmu_objset_pool(bpo->bpo_os)->dp_empty_bpobj); 00373 00374 if (subobj == dmu_objset_pool(bpo->bpo_os)->dp_empty_bpobj) { 00375 bpobj_decr_empty(bpo->bpo_os, tx); 00376 return; 00377 } 00378 00379 VERIFY3U(0, ==, bpobj_open(&subbpo, bpo->bpo_os, subobj)); 00380 VERIFY3U(0, ==, bpobj_space(&subbpo, &used, &comp, &uncomp)); 00381 00382 if (used == 0) { 00383 /* No point in having an empty subobj. */ 00384 bpobj_close(&subbpo); 00385 bpobj_free(bpo->bpo_os, subobj, tx); 00386 return; 00387 } 00388 00389 dmu_buf_will_dirty(bpo->bpo_dbuf, tx); 00390 if (bpo->bpo_phys->bpo_subobjs == 0) { 00391 bpo->bpo_phys->bpo_subobjs = dmu_object_alloc(bpo->bpo_os, 00392 DMU_OT_BPOBJ_SUBOBJ, SPA_MAXBLOCKSIZE, DMU_OT_NONE, 0, tx); 00393 } 00394 00395 mutex_enter(&bpo->bpo_lock); 00396 dmu_write(bpo->bpo_os, bpo->bpo_phys->bpo_subobjs, 00397 bpo->bpo_phys->bpo_num_subobjs * sizeof (subobj), 00398 sizeof (subobj), &subobj, tx); 00399 bpo->bpo_phys->bpo_num_subobjs++; 00400 00401 /* 00402 * If subobj has only one block of subobjs, then move subobj's 00403 * subobjs to bpo's subobj list directly. This reduces 00404 * recursion in bpobj_iterate due to nested subobjs. 00405 */ 00406 subsubobjs = subbpo.bpo_phys->bpo_subobjs; 00407 if (subsubobjs != 0) { 00408 dmu_object_info_t doi; 00409 00410 VERIFY3U(0, ==, dmu_object_info(bpo->bpo_os, subsubobjs, &doi)); 00411 if (doi.doi_max_offset == doi.doi_data_block_size) { 00412 dmu_buf_t *subdb; 00413 uint64_t numsubsub = subbpo.bpo_phys->bpo_num_subobjs; 00414 00415 VERIFY3U(0, ==, dmu_buf_hold(bpo->bpo_os, subsubobjs, 00416 0, FTAG, &subdb, 0)); 00417 dmu_write(bpo->bpo_os, bpo->bpo_phys->bpo_subobjs, 00418 bpo->bpo_phys->bpo_num_subobjs * sizeof (subobj), 00419 numsubsub * sizeof (subobj), subdb->db_data, tx); 00420 dmu_buf_rele(subdb, FTAG); 00421 bpo->bpo_phys->bpo_num_subobjs += numsubsub; 00422 00423 dmu_buf_will_dirty(subbpo.bpo_dbuf, tx); 00424 subbpo.bpo_phys->bpo_subobjs = 0; 00425 VERIFY3U(0, ==, dmu_object_free(bpo->bpo_os, 00426 subsubobjs, tx)); 00427 } 00428 } 00429 bpo->bpo_phys->bpo_bytes += used; 00430 bpo->bpo_phys->bpo_comp += comp; 00431 bpo->bpo_phys->bpo_uncomp += uncomp; 00432 mutex_exit(&bpo->bpo_lock); 00433 00434 bpobj_close(&subbpo); 00435 } 00436 00437 void 00438 bpobj_enqueue(bpobj_t *bpo, const blkptr_t *bp, dmu_tx_t *tx) 00439 { 00440 blkptr_t stored_bp = *bp; 00441 uint64_t offset; 00442 int blkoff; 00443 blkptr_t *bparray; 00444 00445 ASSERT(!BP_IS_HOLE(bp)); 00446 ASSERT(bpo->bpo_object != dmu_objset_pool(bpo->bpo_os)->dp_empty_bpobj); 00447 00448 /* We never need the fill count. */ 00449 stored_bp.blk_fill = 0; 00450 00451 /* The bpobj will compress better if we can leave off the checksum */ 00452 if (!BP_GET_DEDUP(bp)) 00453 bzero(&stored_bp.blk_cksum, sizeof (stored_bp.blk_cksum)); 00454 00455 mutex_enter(&bpo->bpo_lock); 00456 00457 offset = bpo->bpo_phys->bpo_num_blkptrs * sizeof (stored_bp); 00458 blkoff = P2PHASE(bpo->bpo_phys->bpo_num_blkptrs, bpo->bpo_epb); 00459 00460 if (bpo->bpo_cached_dbuf == NULL || 00461 offset < bpo->bpo_cached_dbuf->db_offset || 00462 offset >= bpo->bpo_cached_dbuf->db_offset + 00463 bpo->bpo_cached_dbuf->db_size) { 00464 if (bpo->bpo_cached_dbuf) 00465 dmu_buf_rele(bpo->bpo_cached_dbuf, bpo); 00466 VERIFY3U(0, ==, dmu_buf_hold(bpo->bpo_os, bpo->bpo_object, 00467 offset, bpo, &bpo->bpo_cached_dbuf, 0)); 00468 } 00469 00470 dmu_buf_will_dirty(bpo->bpo_cached_dbuf, tx); 00471 bparray = bpo->bpo_cached_dbuf->db_data; 00472 bparray[blkoff] = stored_bp; 00473 00474 dmu_buf_will_dirty(bpo->bpo_dbuf, tx); 00475 bpo->bpo_phys->bpo_num_blkptrs++; 00476 bpo->bpo_phys->bpo_bytes += 00477 bp_get_dsize_sync(dmu_objset_spa(bpo->bpo_os), bp); 00478 if (bpo->bpo_havecomp) { 00479 bpo->bpo_phys->bpo_comp += BP_GET_PSIZE(bp); 00480 bpo->bpo_phys->bpo_uncomp += BP_GET_UCSIZE(bp); 00481 } 00482 mutex_exit(&bpo->bpo_lock); 00483 } 00484 00485 struct space_range_arg { 00486 spa_t *spa; 00487 uint64_t mintxg; 00488 uint64_t maxtxg; 00489 uint64_t used; 00490 uint64_t comp; 00491 uint64_t uncomp; 00492 }; 00493 00494 /* ARGSUSED */ 00495 static int 00496 space_range_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx) 00497 { 00498 struct space_range_arg *sra = arg; 00499 00500 if (bp->blk_birth > sra->mintxg && bp->blk_birth <= sra->maxtxg) { 00501 if (dsl_pool_sync_context(spa_get_dsl(sra->spa))) 00502 sra->used += bp_get_dsize_sync(sra->spa, bp); 00503 else 00504 sra->used += bp_get_dsize(sra->spa, bp); 00505 sra->comp += BP_GET_PSIZE(bp); 00506 sra->uncomp += BP_GET_UCSIZE(bp); 00507 } 00508 return (0); 00509 } 00510 00511 int 00512 bpobj_space(bpobj_t *bpo, uint64_t *usedp, uint64_t *compp, uint64_t *uncompp) 00513 { 00514 mutex_enter(&bpo->bpo_lock); 00515 00516 *usedp = bpo->bpo_phys->bpo_bytes; 00517 if (bpo->bpo_havecomp) { 00518 *compp = bpo->bpo_phys->bpo_comp; 00519 *uncompp = bpo->bpo_phys->bpo_uncomp; 00520 mutex_exit(&bpo->bpo_lock); 00521 return (0); 00522 } else { 00523 mutex_exit(&bpo->bpo_lock); 00524 return (bpobj_space_range(bpo, 0, UINT64_MAX, 00525 usedp, compp, uncompp)); 00526 } 00527 } 00528 00529 /* 00530 * Return the amount of space in the bpobj which is: 00531 * mintxg < blk_birth <= maxtxg 00532 */ 00533 int 00534 bpobj_space_range(bpobj_t *bpo, uint64_t mintxg, uint64_t maxtxg, 00535 uint64_t *usedp, uint64_t *compp, uint64_t *uncompp) 00536 { 00537 struct space_range_arg sra = { 0 }; 00538 int err; 00539 00540 /* 00541 * As an optimization, if they want the whole txg range, just 00542 * get bpo_bytes rather than iterating over the bps. 00543 */ 00544 if (mintxg < TXG_INITIAL && maxtxg == UINT64_MAX && bpo->bpo_havecomp) 00545 return (bpobj_space(bpo, usedp, compp, uncompp)); 00546 00547 sra.spa = dmu_objset_spa(bpo->bpo_os); 00548 sra.mintxg = mintxg; 00549 sra.maxtxg = maxtxg; 00550 00551 err = bpobj_iterate_nofree(bpo, space_range_cb, &sra, NULL); 00552 *usedp = sra.used; 00553 *compp = sra.comp; 00554 *uncompp = sra.uncomp; 00555 return (err); 00556 }