From f605a39a67f970f41bea01e716413e7f09d6ac5c Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Aug 01 2017 03:31:57 +0000 Subject: import gfs2-utils-3.1.10-3.el7 --- diff --git a/.gfs2-utils.metadata b/.gfs2-utils.metadata index 0a37ce4..19eabf1 100644 --- a/.gfs2-utils.metadata +++ b/.gfs2-utils.metadata @@ -1 +1 @@ -6c500b8cdfd9c16ae2c3a7c40511f6d57bcb87e3 SOURCES/gfs2-utils-3.1.9.tar.gz +d524948b501354a97f2ade4e8f7130d97b92ff32 SOURCES/gfs2-utils-3.1.10.tar.gz diff --git a/.gitignore b/.gitignore index df6925f..b59d86a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/gfs2-utils-3.1.9.tar.gz +SOURCES/gfs2-utils-3.1.10.tar.gz diff --git a/SOURCES/bz1348703-fsck_gfs2_undo_functions_can_stop_too_early_on_duplicates.patch b/SOURCES/bz1348703-fsck_gfs2_undo_functions_can_stop_too_early_on_duplicates.patch deleted file mode 100644 index ff0ae59..0000000 --- a/SOURCES/bz1348703-fsck_gfs2_undo_functions_can_stop_too_early_on_duplicates.patch +++ /dev/null @@ -1,197 +0,0 @@ -commit 0a0d4af99c9be8abc863e272d6e7f46bb6ad4b41 -Author: Bob Peterson -Date: Tue Jun 21 14:52:06 2016 -0500 - - fsck.gfs2: "undo" functions can stop too early on duplicates - - This patch addresses a bug in which blocks may not be freed properly - because the "undo" processing in pass1 can stop early when duplicate - data blocks are encountered. What happens is this: The "undo" process - called from metawalk.c is coded to stop its undo when it encounters - the block containing corruption. However, if the block containing the - corruption is actually the second reference to the same block, inside - the same dinode, the undo process stops early when it encounters the - first reference. The problem is that it's coded to stop when it sees - a reference to the corrupt block. That's wrong. It should really - stop when it sees the reference to that block that triggered the - "unrecoverable" error. In other words, when it hits that exact - metadata block and its offset. This patch scraps the old system of - looking for the corrupt block in favor of looking for the corrupt - reference, including metadata block and offset within that block. - - Signed-off-by: Bob Peterson - - This is a RHEL7 port of this upstream patch: - https://git.fedorahosted.org/cgit/gfs2-utils.git/commit/?id=6b40deabbbbd59a1cbddbe633984214b6752d25a - - rhbz#1348703 - -diff --git a/gfs2/fsck/fsck.h b/gfs2/fsck/fsck.h -index 73cff4c..8af4eb4 100644 ---- a/gfs2/fsck/fsck.h -+++ b/gfs2/fsck/fsck.h -@@ -106,6 +106,12 @@ enum rgindex_trust_level { /* how far can we trust our RG index? */ - must have been converted from gfs2_convert. */ - }; - -+struct error_block { -+ uint64_t metablk; /* metadata block where error was found */ -+ int metaoff; /* offset in that metadata block where error found */ -+ uint64_t errblk; /* error block */ -+}; -+ - extern struct gfs2_inode *fsck_load_inode(struct gfs2_sbd *sdp, uint64_t block); - extern struct gfs2_inode *fsck_inode_get(struct gfs2_sbd *sdp, - struct rgrp_tree *rgd, -diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c -index c0cc2ab..fecf33e 100644 ---- a/gfs2/fsck/metawalk.c -+++ b/gfs2/fsck/metawalk.c -@@ -1387,7 +1387,7 @@ error_undo: /* undo what we've done so far for this block */ - */ - static int check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass, - struct gfs2_buffer_head *bh, int head_size, -- uint64_t *blks_checked, uint64_t *error_blk) -+ uint64_t *blks_checked, struct error_block *error_blk) - { - int error = 0, rc = 0; - uint64_t block, *ptr; -@@ -1418,21 +1418,36 @@ static int check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass, - log_info("\n"); - if (rc < 0) { - /* A fatal error trumps a non-fatal one. */ -- if ((*error_blk == 0) || (rc < error)) { -- log_debug(_("Fatal error on block 0x" -- "%llx preempts non-fatal " -- "error on block 0x%llx\n"), -+ if ((error_blk->errblk == 0) || -+ (rc < error)) { -+ log_debug(_("Fatal error on metadata " -+ "block 0x%llx, offset " -+ "0x%x, referencing block " -+ "0x%llx preempts non-fatal" -+ " error on block 0x%llx\n"), -+ (unsigned long long)metablock, -+ (int)(ptr - ptr_start), - (unsigned long long)block, -- (unsigned long long)*error_blk); -- *error_blk = block; -+ (unsigned long long)error_blk->errblk); -+ error_blk->metablk = metablock; -+ error_blk->metaoff = ptr - ptr_start; -+ error_blk->errblk = block; - } - log_info(_("Unrecoverable ")); - } else { /* nonfatal error */ -- if ((*error_blk) == 0) -- *error_blk = block; -+ if (error_blk->errblk == 0) { -+ error_blk->metablk = metablock; -+ error_blk->metaoff = ptr - ptr_start; -+ error_blk->errblk = block; -+ } - } -- log_info(_("data block error %d on block %llu " -- "(0x%llx).\n"), rc, -+ log_info(_("data block error %d on metadata block " -+ "%lld (0x%llx), offset %d (0x%x), " -+ "referencing data block %lld (0x%llx).\n"), -+ rc, (unsigned long long)metablock, -+ (unsigned long long)metablock, -+ (int)(ptr - ptr_start), -+ (int)(ptr - ptr_start), - (unsigned long long)block, - (unsigned long long)block); - error = rc; -@@ -1445,8 +1460,9 @@ static int check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass, - } - - static int undo_check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass, -+ uint64_t metablock, - uint64_t *ptr_start, char *ptr_end, -- uint64_t error_blk, int error) -+ struct error_block *error_blk, int error) - { - int rc = 0; - uint64_t block, *ptr; -@@ -1460,19 +1476,27 @@ static int undo_check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass, - if (skip_this_pass || fsck_abort) - return 1; - block = be64_to_cpu(*ptr); -- if (block == error_blk) { -+ if (metablock == error_blk->metablk && -+ (ptr - ptr_start == error_blk->metaoff) && -+ block == error_blk->errblk) { - if (error < 0) { /* A fatal error that stopped it? */ - log_debug(_("Stopping the undo process: " - "fatal error block 0x%llx was " -- "found.\n"), -- (unsigned long long)error_blk); -+ "found at metadata block 0x%llx," -+ "offset 0x%x.\n"), -+ (unsigned long long)error_blk->errblk, -+ (unsigned long long)error_blk->metablk, -+ error_blk->metaoff); - return 1; - } - found_error_blk = 1; - log_debug(_("The non-fatal error block 0x%llx was " -- "found, but undo processing will continue " -+ "found at metadata block 0x%llx, offset " -+ "0x%d, but undo processing will continue " - "until the end of this metadata block.\n"), -- (unsigned long long)error_blk); -+ (unsigned long long)error_blk->errblk, -+ (unsigned long long)error_blk->metablk, -+ error_blk->metaoff); - } - rc = pass->undo_check_data(ip, block, pass->private); - if (rc < 0) -@@ -1514,7 +1538,7 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass) - uint64_t blks_checked = 0; - int error, rc; - int metadata_clean = 0; -- uint64_t error_blk = 0; -+ struct error_block error_blk = {0, 0, 0}; - int hit_error_blk = 0; - - if (!height && !is_dir(&ip->i_di, ip->i_sbd->gfs1)) -@@ -1577,10 +1601,15 @@ undo_metalist: - if (!error) - goto out; - log_err( _("Error: inode %llu (0x%llx) had unrecoverable errors at " -+ "metadata block %lld (0x%llx), offset %d (0x%x), block " - "%lld (0x%llx).\n"), - (unsigned long long)ip->i_di.di_num.no_addr, - (unsigned long long)ip->i_di.di_num.no_addr, -- (unsigned long long)error_blk, (unsigned long long)error_blk); -+ (unsigned long long)error_blk.metablk, -+ (unsigned long long)error_blk.metablk, -+ error_blk.metaoff, error_blk.metaoff, -+ (unsigned long long)error_blk.errblk, -+ (unsigned long long)error_blk.errblk); - if (!query( _("Remove the invalid inode? (y/n) "))) { - free_metalist(ip, &metalist[0]); - log_err(_("Invalid inode not deleted.\n")); -@@ -1606,12 +1635,20 @@ undo_metalist: - head_size = hdr_size(bh, height); - if (head_size) { - rc = undo_check_data(ip, pass, -+ bh->b_blocknr, - (uint64_t *) - (bh->b_data + head_size), - (bh->b_data + ip->i_sbd->bsize), -- error_blk, error); -+ &error_blk, -+ error); - if (rc > 0) { - hit_error_blk = 1; -+ log_err("Reached the error " -+ "block undoing work " -+ "for inode %lld " -+ "(0x%llx).\n", -+ (unsigned long long)ip->i_di.di_num.no_addr, -+ (unsigned long long)ip->i_di.di_num.no_addr); - rc = 0; - } - } diff --git a/SOURCES/bz1350597-fsck_gfs2_link_count_checking_wrong_inode_s_formal_inode_number.patch b/SOURCES/bz1350597-fsck_gfs2_link_count_checking_wrong_inode_s_formal_inode_number.patch deleted file mode 100644 index bd97bd7..0000000 --- a/SOURCES/bz1350597-fsck_gfs2_link_count_checking_wrong_inode_s_formal_inode_number.patch +++ /dev/null @@ -1,54 +0,0 @@ -commit 1adddb1098c10766ae2587d393c1612e5670a52d -Author: Bob Peterson -Date: Thu Jun 23 08:16:25 2016 -0500 - - fsck.gfs2: link count checking wrong inode's formal inode number - - This patch fixes a bug whereby inodes that aren't in the dirtree or - the inodetree are checking the wrong formal inode number. Function - incr_link_count checks the wrong inode value, and thus reports an - error where there are none. Also, when the error is reported back, - it reports the wrong value, using a copy of the inum value. In this - case, it should look up the dentry's inode's formal inode number. - This should only hurt performance in error cases. - - Signed-off-by: Bob Peterson - - This is a RHEL7 port of this upstream patch: - https://git.fedorahosted.org/cgit/gfs2-utils.git/commit/?id=ad19203f04caa3fb1c777250a21fe1a968999e8f - - rhbz#1350597 - -diff --git a/gfs2/fsck/link.c b/gfs2/fsck/link.c -index 0243d85..00636d7 100644 ---- a/gfs2/fsck/link.c -+++ b/gfs2/fsck/link.c -@@ -112,7 +112,7 @@ int incr_link_count(struct gfs2_inum no, struct gfs2_inode *ip, - - link_ip = fsck_load_inode(ip->i_sbd, no.no_addr); - /* Check formal ino against dinode before adding to inode tree. */ -- if (no.no_formal_ino != ip->i_di.di_num.no_formal_ino) { -+ if (no.no_formal_ino != link_ip->i_di.di_num.no_formal_ino) { - fsck_inode_put(&link_ip); - return 1; - } -diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c -index f808cea..8ac5547 100644 ---- a/gfs2/fsck/pass2.c -+++ b/gfs2/fsck/pass2.c -@@ -195,8 +195,13 @@ static int bad_formal_ino(struct gfs2_inode *ip, struct gfs2_dirent *dent, - di = dirtree_find(entry.no_addr); - if (di) - inum = di->dinode; -- else if (link1_type(&clink1map, entry.no_addr) == 1) -- inum = entry; -+ else if (link1_type(&clink1map, entry.no_addr) == 1) { -+ struct gfs2_inode *dent_ip; -+ -+ dent_ip = fsck_load_inode(ip->i_sbd, entry.no_addr); -+ inum = dent_ip->i_di.di_num; -+ fsck_inode_put(&dent_ip); -+ } - } - log_err( _("Directory entry '%s' pointing to block %llu (0x%llx) in " - "directory %llu (0x%llx) has the wrong 'formal' inode " diff --git a/SOURCES/bz1350600-fsck_gfs2_check_formal_inode_number_when_links_go_from_1_to_2.patch b/SOURCES/bz1350600-fsck_gfs2_check_formal_inode_number_when_links_go_from_1_to_2.patch deleted file mode 100644 index eab270f..0000000 --- a/SOURCES/bz1350600-fsck_gfs2_check_formal_inode_number_when_links_go_from_1_to_2.patch +++ /dev/null @@ -1,272 +0,0 @@ -commit c78fedfd07a455e934b0a603c9c2e124b9522662 -Author: Bob Peterson -Date: Fri Jun 24 08:53:41 2016 -0500 - - fsck.gfs2: check formal inode number when links go from 1 to 2 - - Before commit f2ffb1e, function basic_dentry_checks had access to a - complete inode tree, so it was able to check the formal inode number - of every dentry against what it previously found in the inode in - pass1. But commit f2ffb1e changed function basic_dentry_checks so - that it bypasses the check if the reference count is 1, which is - most likely, and it's going to be correct 99% of the time. The - comments state: - - "Since we don't have ii or di, the only way to validate formal_ino - is to read in the inode, which would kill performance. So skip it - for now." - - The problem is, problems can slip through, undetected, and will - only be fixed with a second run of fsck.gfs2. For example, during - testing, I found a set of gfs2 metadata that had two dentries - pointing to the same dinode. The first dentry encountered by pass2 - was wrong, but it was not checked for this reason. The second - dentry was correct. The first run of fsck did not catch the problem - and reacted by setting link count to 2 for the dinode, keeping - both dentries. The second run found the problem and fixed it, - changing the link count back to 1 and deleting the bad dentry. - - Note that this problem only applies to non-directories, since - directories will have a value in the dirtree to check against. - - This patch solves the problem with a new "innocent until proven - guilty" approach. When the first dentry reference is found, it is - assumed to be correct (most files will have a single link). - When the second dentry reference is found, it goes back and checks - the original reference. To do that, it takes the (time expensive) - step of traversing the directory tree, searching every directory - for the original reference. Once the original dentry is found, it - checks its formal inode number against the one that has been proven - correct. This situation ought to be quite rare because the vast - number of files will have a single correct link. So hopefully only - valid hard links will cause the slow tree traversal. Once the first - dentry is found, the tree traversal stops and pass2 continues its - work. - - Signed-off-by: Bob Peterson - - This is a RHEL7 port of this upstream patch: - https://git.fedorahosted.org/cgit/gfs2-utils.git/commit/?id=ab92c41323babc6b6578cddfb4324ae545927f88 - - rhbz#1350600 - -diff --git a/gfs2/fsck/link.c b/gfs2/fsck/link.c -index 00636d7..8ea09c7 100644 ---- a/gfs2/fsck/link.c -+++ b/gfs2/fsck/link.c -@@ -88,33 +88,33 @@ int incr_link_count(struct gfs2_inum no, struct gfs2_inode *ip, - di = dirtree_find(no.no_addr); - if (di) { - if (di->dinode.no_formal_ino != no.no_formal_ino) -- return 1; -+ return incr_link_ino_mismatch; - - di->counted_links++; - whyincr(no.no_addr, why, referenced_from, di->counted_links); -- return 0; -+ return incr_link_good; - } - ii = inodetree_find(no.no_addr); - /* If the list has entries, look for one that matches inode_no */ - if (ii) { - if (ii->di_num.no_formal_ino != no.no_formal_ino) -- return 1; -+ return incr_link_ino_mismatch; - - ii->counted_links++; - whyincr(no.no_addr, why, referenced_from, ii->counted_links); -- return 0; -+ return incr_link_good; - } - if (link1_type(&clink1map, no.no_addr) != 1) { - link1_set(&clink1map, no.no_addr, 1); - whyincr(no.no_addr, why, referenced_from, 1); -- return 0; -+ return incr_link_good; - } - - link_ip = fsck_load_inode(ip->i_sbd, no.no_addr); - /* Check formal ino against dinode before adding to inode tree. */ - if (no.no_formal_ino != link_ip->i_di.di_num.no_formal_ino) { - fsck_inode_put(&link_ip); -- return 1; -+ return incr_link_ino_mismatch; /* inode mismatch */ - } - /* Move it from the link1 maps to a real inode tree entry */ - link1_set(&nlink1map, no.no_addr, 0); -@@ -130,7 +130,7 @@ int incr_link_count(struct gfs2_inum no, struct gfs2_inode *ip, - (unsigned long long)referenced_from, - (unsigned long long)no.no_addr); - fsck_inode_put(&link_ip); -- return -1; -+ return incr_link_bad; - } - ii->di_num = link_ip->i_di.di_num; - fsck_inode_put(&link_ip); -@@ -138,7 +138,11 @@ int incr_link_count(struct gfs2_inum no, struct gfs2_inode *ip, - nlink1map */ - ii->counted_links = 2; - whyincr(no.no_addr, why, referenced_from, ii->counted_links); -- return 0; -+ /* We transitioned a dentry link count from 1 to 2, and we know it's -+ not a directory. But the new reference has the correct formal -+ inode number, so the first reference is suspect: we need to -+ check it in case it's a bad reference, and not just a hard link. */ -+ return incr_link_check_orig; - } - - #define whydecr(no_addr, why, referenced_from, counted_links) \ -diff --git a/gfs2/fsck/link.h b/gfs2/fsck/link.h -index 14534e5..a5dd1c8 100644 ---- a/gfs2/fsck/link.h -+++ b/gfs2/fsck/link.h -@@ -4,6 +4,13 @@ - extern struct gfs2_bmap nlink1map; /* map of dinodes with nlink == 1 */ - extern struct gfs2_bmap clink1map; /* map of dinodes w/counted links == 1 */ - -+enum { -+ incr_link_bad = -1, -+ incr_link_good = 0, -+ incr_link_ino_mismatch = 1, -+ incr_link_check_orig = 2, -+}; -+ - int link1_set(struct gfs2_bmap *bmap, uint64_t bblock, int mark); - int set_di_nlink(struct gfs2_inode *ip); - int incr_link_count(struct gfs2_inum no, struct gfs2_inode *ip, -diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c -index 8ac5547..808cf21 100644 ---- a/gfs2/fsck/pass2.c -+++ b/gfs2/fsck/pass2.c -@@ -667,6 +667,113 @@ static int basic_dentry_checks(struct gfs2_inode *ip, struct gfs2_dirent *dent, - return 0; - } - -+static int dirref_find(struct gfs2_inode *ip, struct gfs2_dirent *dent, -+ struct gfs2_dirent *prev, struct gfs2_buffer_head *bh, -+ char *filename, uint32_t *count, int *lindex, -+ void *private) -+{ -+ /* the metawalk_fxn's private field must be set to the dentry -+ * block we want to clear */ -+ struct gfs2_inum *entry = (struct gfs2_inum *)private; -+ struct gfs2_dirent dentry, *de; -+ char fn[MAX_FILENAME]; -+ -+ memset(&dentry, 0, sizeof(struct gfs2_dirent)); -+ gfs2_dirent_in(&dentry, (char *)dent); -+ de = &dentry; -+ -+ if (de->de_inum.no_addr != entry->no_addr) { -+ (*count)++; -+ return 0; -+ } -+ if (de->de_inum.no_formal_ino == dent->de_inum.no_formal_ino) { -+ log_debug("Formal inode number matches; must be a hard " -+ "link.\n"); -+ goto out; -+ } -+ log_err(_("The original reference to inode %lld (0x%llx) from " -+ "directory %lld (0x%llx) has the wrong 'formal' inode " -+ "number.\n"), (unsigned long long)entry->no_addr, -+ (unsigned long long)entry->no_addr, -+ (unsigned long long)ip->i_di.di_num.no_addr, -+ (unsigned long long)ip->i_di.di_num.no_addr); -+ memset(fn, 0, sizeof(fn)); -+ if (de->de_name_len < MAX_FILENAME) -+ strncpy(fn, filename, de->de_name_len); -+ else -+ strncpy(fn, filename, MAX_FILENAME - 1); -+ log_err(_("The bad reference '%s' had formal inode number: %lld " -+ "(0x%llx) but the correct value is: %lld (0x%llx)\n"), -+ fn, (unsigned long long)de->de_inum.no_formal_ino, -+ (unsigned long long)de->de_inum.no_formal_ino, -+ (unsigned long long)entry->no_formal_ino, -+ (unsigned long long)entry->no_formal_ino); -+ if (!query(_("Delete the bad directory entry? (y/n) "))) { -+ log_err(_("The corrupt directory entry was not fixed.\n")); -+ goto out; -+ } -+ decr_link_count(entry->no_addr, ip->i_di.di_num.no_addr, -+ ip->i_sbd->gfs1, _("bad original reference")); -+ dirent2_del(ip, bh, prev, dent); -+ log_err(_("The corrupt directory entry '%s' was deleted.\n"), fn); -+out: -+ return -1; /* force check_dir to stop; don't waste time. */ -+} -+ -+/** -+ * check_suspicious_dirref - double-check a questionable first dentry ref -+ * -+ * This function is called when a dentry has caused us to increment the -+ * link count to a file from 1 to 2, and we know the object pointed to is -+ * not a directory. (Most likely, it'a a file). The second directory to -+ * reference the dinode has the correct formal inode number, but when we -+ * created the original reference in the counted links bitmap (clink1map), -+ * we had no way to check the formal inode number. (Well, we could have read -+ * in the dinode, but that would kill fsck.gfs2 performance.) -+ * So now we have to walk through the directory tree and find that original -+ * reference so make sure it's a valid reference. If the formal inode number -+ * is the same, it's a hard link (which is unlikely for gfs2). If it's not -+ * the same, that's an error, and we need to delete the damaged original -+ * dentry, since we failed to detect the problem earlier. -+ */ -+static int check_suspicious_dirref(struct gfs2_sbd *sdp, -+ struct gfs2_inum *entry) -+{ -+ struct osi_node *tmp, *next = NULL; -+ struct dir_info *dt; -+ struct gfs2_inode *ip; -+ uint64_t dirblk; -+ int error = FSCK_OK; -+ struct metawalk_fxns dirref_hunt = { -+ .private = (void *)entry, -+ .check_dentry = dirref_find, -+ }; -+ -+ log_debug("This dentry is good, but since this is a second " -+ "reference to block 0x%llx, we need to check the " -+ "original.\n", (unsigned long long)entry->no_addr); -+ for (tmp = osi_first(&dirtree); tmp; tmp = next) { -+ next = osi_next(tmp); -+ dt = (struct dir_info *)tmp; -+ dirblk = dt->dinode.no_addr; -+ if (skip_this_pass || fsck_abort) /* asked to skip the rest */ -+ break; -+ ip = fsck_load_inode(sdp, dirblk); -+ if (ip == NULL) { -+ stack; -+ return FSCK_ERROR; -+ } -+ error = check_dir(sdp, ip, &dirref_hunt); -+ fsck_inode_put(&ip); -+ /* Error just means we found the dentry and dealt with it. */ -+ if (error) -+ break; -+ } -+ log_debug("Original reference check complete. Found = %d.\n", -+ error ? 1 : 0); -+ return 0; -+} -+ - /* FIXME: should maybe refactor this a bit - but need to deal with - * FIXMEs internally first */ - static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent, -@@ -870,10 +977,13 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent, - dentry_is_valid: - /* This directory inode links to this inode via this dentry */ - error = incr_link_count(entry, ip, _("valid reference")); -- if (error > 0 && -- bad_formal_ino(ip, dent, entry, tmp_name, q, de, bh) == 1) -- goto nuke_dentry; -- -+ if (error == incr_link_check_orig) { -+ error = check_suspicious_dirref(sdp, &entry); -+ } else if (error == incr_link_ino_mismatch) { -+ log_err("incr_link_count err=%d.\n", error); -+ if (bad_formal_ino(ip, dent, entry, tmp_name, q, de, bh) == 1) -+ goto nuke_dentry; -+ } - (*count)++; - ds->entry_count++; - /* End of checks */ diff --git a/SOURCES/bz1436772-gfs2_grow_Disable_rgrp_alignment_when_dev_topology_is_unsuitable.patch b/SOURCES/bz1436772-gfs2_grow_Disable_rgrp_alignment_when_dev_topology_is_unsuitable.patch new file mode 100644 index 0000000..7c0e9f7 --- /dev/null +++ b/SOURCES/bz1436772-gfs2_grow_Disable_rgrp_alignment_when_dev_topology_is_unsuitable.patch @@ -0,0 +1,32 @@ +commit ae565c82568a8b8068c225d8534c68c9989ccbe3 +Author: Andrew Price +Date: Tue Mar 28 21:32:42 2017 +0100 + + gfs2_grow: Disable rgrp alignment when dev topology is unsuitable + + If optimal_io_size is not a multiple of minimum_io_size then the values + are not reliable swidth and sunit values, so disable rgrp stripe + alignment in that case. + + Resolves: rhbz#1436772 + + Signed-off-by: Andrew Price + +diff --git a/gfs2/mkfs/main_grow.c b/gfs2/mkfs/main_grow.c +index 173466c..66ba057 100644 +--- a/gfs2/mkfs/main_grow.c ++++ b/gfs2/mkfs/main_grow.c +@@ -169,8 +169,12 @@ static lgfs2_rgrps_t rgrps_init(struct gfs2_sbd *sdp) + unsigned long min_io_sz = blkid_topology_get_minimum_io_size(tp); + unsigned long opt_io_sz = blkid_topology_get_optimal_io_size(tp); + unsigned long phy_sector_sz = blkid_topology_get_physical_sector_size(tp); ++ /* If optimal_io_size is not a multiple of minimum_io_size then ++ the values are not reliable swidth and sunit values, so don't ++ attempt rgrp alignment */ + if ((min_io_sz > phy_sector_sz) && +- (opt_io_sz > phy_sector_sz)) { ++ (opt_io_sz > phy_sector_sz) && ++ (opt_io_sz % min_io_sz == 0)) { + al_base = opt_io_sz / sdp->bsize; + al_off = min_io_sz / sdp->bsize; + } diff --git a/SOURCES/bz1437009-mkfs_gfs2_Disable_rgrp_alignment_when_dev_topology_is_unsuitable.patch b/SOURCES/bz1437009-mkfs_gfs2_Disable_rgrp_alignment_when_dev_topology_is_unsuitable.patch deleted file mode 100644 index aedbc8b..0000000 --- a/SOURCES/bz1437009-mkfs_gfs2_Disable_rgrp_alignment_when_dev_topology_is_unsuitable.patch +++ /dev/null @@ -1,35 +0,0 @@ -commit c419b886b7520e1a92a984352baa0e122ff050e9 -Author: Andrew Price -Date: Thu Jan 26 11:05:42 2017 +0000 - - mkfs.gfs2: Disable rgrp alignment when dev topology is unsuitable - - If optimal_io_size is not a multiple of minimum_io_size then the values - are not reliable swidth and sunit values, so disable rgrp stripe - alignment in that case. - - Resolves: rhbz#1437009 - - Signed-off-by: Andrew Price - -diff --git a/gfs2/mkfs/main_mkfs.c b/gfs2/mkfs/main_mkfs.c -index 4436f93..c3497a8 100644 ---- a/gfs2/mkfs/main_mkfs.c -+++ b/gfs2/mkfs/main_mkfs.c -@@ -590,8 +590,14 @@ static lgfs2_rgrps_t rgs_init(struct mkfs_opts *opts, struct gfs2_sbd *sdp) - al_off = opts->sunit / sdp->bsize; - } - } else if (opts->align) { -- if ((opts->dev.minimum_io_size > opts->dev.physical_sector_size) && -- (opts->dev.optimal_io_size > opts->dev.physical_sector_size)) { -+ if (opts->dev.optimal_io_size <= opts->dev.physical_sector_size || -+ opts->dev.minimum_io_size <= opts->dev.physical_sector_size || -+ (opts->dev.optimal_io_size % opts->dev.minimum_io_size) != 0) { -+ /* If optimal_io_size is not a multiple of minimum_io_size then -+ the values are not reliable swidth and sunit values, so disable -+ rgrp alignment */ -+ opts->align = 0; -+ } else { - al_base = opts->dev.optimal_io_size / sdp->bsize; - al_off = opts->dev.minimum_io_size / sdp->bsize; - } diff --git a/SOURCES/bz1437125-gfs2_grow_Disable_rgrp_alignment_when_dev_topology_is_unsuitable.patch b/SOURCES/bz1437125-gfs2_grow_Disable_rgrp_alignment_when_dev_topology_is_unsuitable.patch deleted file mode 100644 index 50452da..0000000 --- a/SOURCES/bz1437125-gfs2_grow_Disable_rgrp_alignment_when_dev_topology_is_unsuitable.patch +++ /dev/null @@ -1,32 +0,0 @@ -commit 64688d3f3a4fb2134cbb9f7fd2d1d500df893aac -Author: Andrew Price -Date: Tue Mar 28 21:32:42 2017 +0100 - - gfs2_grow: Disable rgrp alignment when dev topology is unsuitable - - If optimal_io_size is not a multiple of minimum_io_size then the values - are not reliable swidth and sunit values, so disable rgrp stripe - alignment in that case. - - Resolves: rhbz#1437125 - - Signed-off-by: Andrew Price - -diff --git a/gfs2/mkfs/main_grow.c b/gfs2/mkfs/main_grow.c -index 173466c..66ba057 100644 ---- a/gfs2/mkfs/main_grow.c -+++ b/gfs2/mkfs/main_grow.c -@@ -169,8 +169,12 @@ static lgfs2_rgrps_t rgrps_init(struct gfs2_sbd *sdp) - unsigned long min_io_sz = blkid_topology_get_minimum_io_size(tp); - unsigned long opt_io_sz = blkid_topology_get_optimal_io_size(tp); - unsigned long phy_sector_sz = blkid_topology_get_physical_sector_size(tp); -+ /* If optimal_io_size is not a multiple of minimum_io_size then -+ the values are not reliable swidth and sunit values, so don't -+ attempt rgrp alignment */ - if ((min_io_sz > phy_sector_sz) && -- (opt_io_sz > phy_sector_sz)) { -+ (opt_io_sz > phy_sector_sz) && -+ (opt_io_sz % min_io_sz == 0)) { - al_base = opt_io_sz / sdp->bsize; - al_off = min_io_sz / sdp->bsize; - } diff --git a/SOURCES/bz1440269-1-mkfs_gfs2_Free_unnecessary_cached_pages_disable_readahead.patch b/SOURCES/bz1440269-1-mkfs_gfs2_Free_unnecessary_cached_pages_disable_readahead.patch new file mode 100644 index 0000000..6958f30 --- /dev/null +++ b/SOURCES/bz1440269-1-mkfs_gfs2_Free_unnecessary_cached_pages_disable_readahead.patch @@ -0,0 +1,51 @@ +commit 10deb2492c8b0dd4dceb963850c91e053bb8d6df +Author: Andrew Price +Date: Thu Apr 13 06:51:15 2017 -0400 + + mkfs.gfs2: Free unnecessary cached pages, disable readahead + + With a 300T file system, cached pages built up quite heavily and could + be left over from previous io on the device. Use POSIX_FADV_DONTNEED + where appropriate. + + Also, readahead isn't required as mkfs.gfs2 mainly writing full block + ranges at unpredictable locations so use POSIX_FADV_RANDOM to minimise + the overhead from that. + + Resolves: rhbz#1440269 + + Signed-off-by: Andrew Price + +diff --git a/gfs2/mkfs/main_mkfs.c b/gfs2/mkfs/main_mkfs.c +index 0801a4b..83065fb 100644 +--- a/gfs2/mkfs/main_mkfs.c ++++ b/gfs2/mkfs/main_mkfs.c +@@ -817,6 +817,7 @@ static int place_rgrps(struct gfs2_sbd *sdp, lgfs2_rgrps_t rgs, struct mkfs_opts + gfs2_progress_update(&progress, (sdp->rgrps)); + } + gfs2_progress_close(&progress, _("Done\n")); ++ posix_fadvise(sdp->device_fd, 0, sdp->fssize * sdp->bsize, POSIX_FADV_DONTNEED); + + return 0; + } +@@ -909,6 +910,11 @@ static void open_dev(struct mkfs_dev *dev, int withprobe) + exit(1); + } + ++ /* Freshen up the cache */ ++ posix_fadvise(dev->fd, 0, 0, POSIX_FADV_DONTNEED); ++ /* Turn off readahead, we're just writing new blocks */ ++ posix_fadvise(dev->fd, 0, 0, POSIX_FADV_RANDOM); ++ + error = fstat(dev->fd, &dev->stat); + if (error < 0) { + perror(dev->path); +@@ -1075,7 +1081,7 @@ int main(int argc, char *argv[]) + perror(opts.dev.path); + exit(EXIT_FAILURE); + } +- ++ posix_fadvise(opts.dev.fd, 0, 0, POSIX_FADV_DONTNEED); + error = close(opts.dev.fd); + if (error){ + perror(opts.dev.path); diff --git a/SOURCES/bz1440269-2-mkfs_gfs2_Fix_resource_group_alignment_issue.patch b/SOURCES/bz1440269-2-mkfs_gfs2_Fix_resource_group_alignment_issue.patch new file mode 100644 index 0000000..3dbbb61 --- /dev/null +++ b/SOURCES/bz1440269-2-mkfs_gfs2_Fix_resource_group_alignment_issue.patch @@ -0,0 +1,58 @@ +commit 896a2f709141728355c2a1e8cdf377c51573de05 +Author: Andrew Price +Date: Thu Apr 13 07:42:18 2017 -0400 + + mkfs.gfs2: Fix resource group alignment issue + + Make sure the resource groups created when allocating the journals have + an aligned length to avoid subsequent resource groups start addresses + getting misaligned. This can cause read-modify-write issues during mkfs + and likely in other situations, harming performance. + + Test case included. + + Resolves: rhbz#1440269 + + Signed-off-by: Andrew Price + +diff --git a/gfs2/mkfs/main_mkfs.c b/gfs2/mkfs/main_mkfs.c +index 83065fb..2e08bc6 100644 +--- a/gfs2/mkfs/main_mkfs.c ++++ b/gfs2/mkfs/main_mkfs.c +@@ -721,6 +721,7 @@ static int place_journals(struct gfs2_sbd *sdp, lgfs2_rgrps_t rgs, struct mkfs_o + if (mkfs_journals == NULL) + return 1; + *rgaddr = lgfs2_rgrp_align_addr(rgs, sdp->sb_addr + 1); ++ rgsize = lgfs2_rgrp_align_len(rgs, rgsize); + + for (j = 0; j < opts->journals; j++) { + int result; +diff --git a/tests/mkfs.at b/tests/mkfs.at +index c026a76..274a81d 100644 +--- a/tests/mkfs.at ++++ b/tests/mkfs.at +@@ -102,6 +102,13 @@ AT_CHECK([$GFS_MKFS -p lock_dlm -t "financial_cluster:this_time_we_test_fs_namin + GFS_FSCK_CHECK([$GFS_MKFS -p lock_dlm -t "a_really_long_named_cluster_here:concurrently_lets_check_fs_len" $GFS_TGT]) + AT_CLEANUP + ++# -o test_topology order: ++# alignment_offset, ++# logical_sector_size, ++# minimum_io_size, ++# optimal_io_size, ++# physical_sector_size ++ + AT_SETUP([Device i/o limits handling]) + AT_KEYWORDS(mkfs.gfs2 mkfs) + AT_CHECK([$GFS_MKFS -p lock_nolock -o test_topology=0:0:0:0:0 $GFS_TGT], 0, [ignore], [ignore]) +@@ -109,3 +116,10 @@ AT_CHECK([$GFS_MKFS -p lock_nolock -o test_topology=7168:512:0:33553920:512 $GFS + AT_CHECK([$GFS_MKFS -p lock_nolock -o test_topology=7168:512:8192:33553920:512 $GFS_TGT], 0, [ignore], [Warning: device is not properly aligned. This may harm performance. + ]) + AT_CLEANUP ++ ++AT_SETUP([Resource group alignment]) ++AT_KEYWORDS(mkfs.gfs2 mkfs) ++AT_CHECK([$GFS_MKFS -p lock_nolock -o test_topology=0:512:65536:393216:512 $GFS_TGT], 0, [ignore], [ignore]) ++# Check rgrp alignment to minimum_io_size: 65536 / 4096 == 16 ++AT_CHECK([gfs2_edit -p rindex $GFS_TGT | grep ri_addr | awk '{print $2, $2 % 16; if ($2 % 16 != 0) { exit 1 }}'], 0, [ignore], [ignore]) ++AT_CLEANUP diff --git a/SOURCES/bz1440269-3-libgfs2_Issue_one_write_per_rgrp_when_creating_them.patch b/SOURCES/bz1440269-3-libgfs2_Issue_one_write_per_rgrp_when_creating_them.patch new file mode 100644 index 0000000..393b84b --- /dev/null +++ b/SOURCES/bz1440269-3-libgfs2_Issue_one_write_per_rgrp_when_creating_them.patch @@ -0,0 +1,127 @@ +commit 83450dcb4c4179968343e922e75b73961c7ccefe +Author: Andrew Price +Date: Thu Apr 13 12:19:01 2017 -0400 + + libgfs2: Issue one write per rgrp when creating them + + Previously mkfs.gfs2 issued a write for each block of an rgrp. On + systems where the page size is much larger than the block size, this + incurs a read-modify-write overhead and can slow down mkfs.gfs2 + considerably. Instead, allocate a single, aligned buffer for the bitmap + blocks and write them all in one go. + + On a system with 64K pages and a 300TB block device, this speeds up + mkfs.gfs2 from over an hour to less than 3 minutes. + + Resolves: rhbz#1440269 + + Signed-off-by: Andrew Price + +diff --git a/gfs2/libgfs2/rgrp.c b/gfs2/libgfs2/rgrp.c +index 7066a5c..bb0776a 100644 +--- a/gfs2/libgfs2/rgrp.c ++++ b/gfs2/libgfs2/rgrp.c +@@ -11,6 +11,7 @@ + #include "rgrp.h" + + #define RG_SYNC_TOLERANCE 1000 ++#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) + + static void compute_bitmaps(lgfs2_rgrp_t rg, const unsigned bsize) + { +@@ -109,23 +110,30 @@ struct rgrp_tree *gfs2_blk2rgrpd(struct gfs2_sbd *sdp, uint64_t blk) + int lgfs2_rgrp_bitbuf_alloc(lgfs2_rgrp_t rg) + { + struct gfs2_sbd *sdp = rg->rgrps->sdp; ++ struct gfs2_buffer_head *bhs; ++ size_t len = rg->ri.ri_length * sdp->bsize; ++ unsigned long io_align = sdp->bsize; + unsigned i; + char *bufs; + +- bufs = calloc(rg->ri.ri_length, sizeof(struct gfs2_buffer_head) + sdp->bsize); +- if (bufs == NULL) ++ if (rg->rgrps->align > 0) { ++ len = ROUND_UP(len, rg->rgrps->align * sdp->bsize); ++ io_align = rg->rgrps->align_off * sdp->bsize; ++ } ++ bhs = calloc(rg->ri.ri_length, sizeof(struct gfs2_buffer_head)); ++ if (bhs == NULL) + return 1; + +- rg->bits[0].bi_bh = (struct gfs2_buffer_head *)bufs; +- rg->bits[0].bi_bh->iov.iov_base = (char *)(rg->bits[0].bi_bh + 1); +- rg->bits[0].bi_bh->iov.iov_len = sdp->bsize; +- rg->bits[0].bi_bh->b_blocknr = rg->ri.ri_addr; +- rg->bits[0].bi_bh->sdp = sdp; ++ if (posix_memalign((void **)&bufs, io_align, len) != 0) { ++ errno = ENOMEM; ++ free(bhs); ++ return 1; ++ } ++ memset(bufs, 0, len); + +- for (i = 1; i < rg->ri.ri_length; i++) { +- char *nextbuf = rg->bits[i - 1].bi_bh->b_data + sdp->bsize; +- rg->bits[i].bi_bh = (struct gfs2_buffer_head *)(nextbuf); +- rg->bits[i].bi_bh->iov.iov_base = (char *)(rg->bits[i].bi_bh + 1); ++ for (i = 0; i < rg->ri.ri_length; i++) { ++ rg->bits[i].bi_bh = bhs + i; ++ rg->bits[i].bi_bh->iov.iov_base = bufs + (i * sdp->bsize); + rg->bits[i].bi_bh->iov.iov_len = sdp->bsize; + rg->bits[i].bi_bh->b_blocknr = rg->ri.ri_addr + i; + rg->bits[i].bi_bh->sdp = sdp; +@@ -143,6 +151,7 @@ int lgfs2_rgrp_bitbuf_alloc(lgfs2_rgrp_t rg) + void lgfs2_rgrp_bitbuf_free(lgfs2_rgrp_t rg) + { + unsigned i; ++ free(rg->bits[0].bi_bh->iov.iov_base); + free(rg->bits[0].bi_bh); + for (i = 0; i < rg->ri.ri_length; i++) + rg->bits[i].bi_bh = NULL; +@@ -618,7 +627,7 @@ lgfs2_rgrp_t lgfs2_rgrps_append(lgfs2_rgrps_t rgs, struct gfs2_rindex *entry) + */ + int lgfs2_rgrp_write(int fd, const lgfs2_rgrp_t rg) + { +- int ret = 0; ++ struct gfs2_sbd *sdp = rg->rgrps->sdp; + unsigned int i; + const struct gfs2_meta_header bmh = { + .mh_magic = GFS2_MAGIC, +@@ -626,25 +635,29 @@ int lgfs2_rgrp_write(int fd, const lgfs2_rgrp_t rg) + .mh_format = GFS2_FORMAT_RB, + }; + int freebufs = 0; ++ ssize_t ret; ++ size_t len; + + if (rg->bits[0].bi_bh == NULL) { + freebufs = 1; + if (lgfs2_rgrp_bitbuf_alloc(rg) != 0) + return -1; + } +- + gfs2_rgrp_out(&rg->rg, rg->bits[0].bi_bh->b_data); +- ret = bwrite(rg->bits[0].bi_bh); +- +- for (i = 1; ret == 0 && i < rg->ri.ri_length; i++) { ++ for (i = 1; i < rg->ri.ri_length; i++) + gfs2_meta_header_out(&bmh, rg->bits[i].bi_bh->b_data); +- ret = bwrite(rg->bits[i].bi_bh); +- } ++ ++ len = sdp->bsize * rg->ri.ri_length; ++ if (rg->rgrps->align > 0) ++ len = ROUND_UP(len, rg->rgrps->align * sdp->bsize); ++ ++ ret = pwrite(sdp->device_fd, rg->bits[0].bi_bh->b_data, len, ++ rg->bits[0].bi_bh->b_blocknr * sdp->bsize); + + if (freebufs) + lgfs2_rgrp_bitbuf_free(rg); + +- return ret; ++ return ret == len ? 0 : -1; + } + + lgfs2_rgrp_t lgfs2_rgrp_first(lgfs2_rgrps_t rgs) diff --git a/SPECS/gfs2-utils.spec b/SPECS/gfs2-utils.spec index 07d2a2d..c01780f 100644 --- a/SPECS/gfs2-utils.spec +++ b/SPECS/gfs2-utils.spec @@ -11,8 +11,8 @@ ############################################################################### Name: gfs2-utils -Version: 3.1.9 -Release: 3%{?dist}.1 +Version: 3.1.10 +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ Group: System Environment/Kernel Summary: Utilities for managing the global file system (GFS2) @@ -27,11 +27,12 @@ BuildRequires: gettext-devel BuildRequires: bison BuildRequires: flex BuildRequires: libblkid-devel +BuildRequires: libuuid-devel BuildRequires: check-devel URL: https://fedorahosted.org/cluster/wiki/HomePage %if 0%{?rhel} > 0 -ExclusiveArch: x86_64 s390x +ExclusiveArch: x86_64 s390x ppc64le %endif # The source for this package was pulled from the upstream git tree. @@ -40,24 +41,23 @@ ExclusiveArch: x86_64 s390x # cd gfs2-utils # ./make-tarball.sh # -Source0: https://fedorahosted.org/released/gfs2-utils/gfs2-utils-%{version}.tar.gz -Patch0: bz1348703-fsck_gfs2_undo_functions_can_stop_too_early_on_duplicates.patch -Patch1: bz1350597-fsck_gfs2_link_count_checking_wrong_inode_s_formal_inode_number.patch -Patch2: bz1350600-fsck_gfs2_check_formal_inode_number_when_links_go_from_1_to_2.patch -Patch3: bz1326508-gfs2_5_Clarify_the_availability_of_the_loccookie_option.patch -Patch4: bz1437009-mkfs_gfs2_Disable_rgrp_alignment_when_dev_topology_is_unsuitable.patch -Patch5: bz1437125-gfs2_grow_Disable_rgrp_alignment_when_dev_topology_is_unsuitable.patch +Source0: https://releases.pagure.org/gfs2-utils/gfs2-utils-%{version}.tar.gz +Patch0: bz1326508-gfs2_5_Clarify_the_availability_of_the_loccookie_option.patch +Patch1: bz1436772-gfs2_grow_Disable_rgrp_alignment_when_dev_topology_is_unsuitable.patch +Patch2: bz1440269-1-mkfs_gfs2_Free_unnecessary_cached_pages_disable_readahead.patch +Patch3: bz1440269-2-mkfs_gfs2_Fix_resource_group_alignment_issue.patch +Patch4: bz1440269-3-libgfs2_Issue_one_write_per_rgrp_when_creating_them.patch + BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) %prep %setup -q -n gfs2-utils-%{version} -%patch0 -p1 -b .bz1348703-fsck_gfs2_undo_functions_can_stop_too_early_on_duplicates -%patch1 -p1 -b .bz1350597-fsck_gfs2_link_count_checking_wrong_inode_s_formal_inode_number -%patch2 -p1 -b .bz1350600-fsck_gfs2_check_formal_inode_number_when_links_go_from_1_to_2 -%patch3 -p1 -b .bz1326508-gfs2_5_Clarify_the_availability_of_the_loccookie_option -%patch4 -p1 -b .bz1437009-mkfs_gfs2_Disable_rgrp_alignment_when_dev_topology_is_unsuitable -%patch5 -p1 -b .bz1437125-gfs2_grow_Disable_rgrp_alignment_when_dev_topology_is_unsuitable +%patch0 -p1 -b .bz1326508-gfs2_5_Clarify_the_availability_of_the_loccookie_option +%patch1 -p1 -b .bz1436772-gfs2_grow_Disable_rgrp_alignment_when_dev_topology_is_unsuitable +%patch2 -p1 -b .bz1440269-1-mkfs_gfs2_Free_unnecessary_cached_pages_disable_readahead +%patch3 -p1 -b .bz1440269-2-mkfs_gfs2_Fix_resource_group_alignment_issue +%patch4 -p1 -b .bz1440269-3-libgfs2_Issue_one_write_per_rgrp_when_creating_them %build ./autogen.sh @@ -103,11 +103,28 @@ file systems. %{_prefix}/lib/udev/rules.d/82-gfs2-withdraw.rules %changelog -* Wed Mar 29 2017 Andrew Price - 3.1.9-3.1 -- mkfs.gfs2: Disable rgrp alignment when dev topology is unsuitable - Resolves: rhbz#1437009 +* Tue Apr 18 2017 Andrew Price - 3.1.10-3 +- libgfs2: Issue one write per rgrp when creating them +- mkfs.gfs2: Fix resource group alignment issue +- mkfs.gfs2: Free unnecessary cached pages, disable readahead + Resolves: rhbz#1440269 + +* Tue Mar 28 2017 Andrew Price - 3.1.10-2 - gfs2_grow: Disable rgrp alignment when dev topology is unsuitable - Resolves: rhbz#1437125 + Resolves: rhbz#1436772 + +* Tue Mar 28 2017 Andrew Price - 3.1.10-1 +- Rebase to new upstream version 3.1.10 + Resolves: rhbz#1348601 + Resolves: rhbz#1356685 + Resolves: rhbz#1382087 + Resolves: rhbz#1405163 + Resolves: rhbz#1430399 +- Make dependency on libuuid explicit + +* Wed Mar 15 2017 Andrew Price - 3.1.9-4 +- Enable ppc64le builds + Resolves: rhbz#1426651 * Wed Jul 20 2016 Andrew Price - 3.1.9-3 - gfs2(5): Clarify the availability of the loccookie option