License cleanup: add SPDX GPL-2.0 license identifier to files with no license
Many source files in the tree are missing licensing information, which
makes it harder for compliance tools to determine the correct license.
By default all files without license information are under the default
license of the kernel, which is GPL version 2.
Update the files which contain no license information with the 'GPL-2.0'
SPDX license identifier. The SPDX identifier is a legally binding
shorthand, which can be used instead of the full boiler plate text.
This patch is based on work done by Thomas Gleixner and Kate Stewart and
Philippe Ombredanne.
How this work was done:
Patches were generated and checked against linux-4.14-rc6 for a subset of
the use cases:
- file had no licensing information it it.
- file was a */uapi/* one with no licensing information in it,
- file was a */uapi/* one with existing licensing information,
Further patches will be generated in subsequent months to fix up cases
where non-standard license headers were used, and references to license
had to be inferred by heuristics based on keywords.
The analysis to determine which SPDX License Identifier to be applied to
a file was done in a spreadsheet of side by side results from of the
output of two independent scanners (ScanCode & Windriver) producing SPDX
tag:value files created by Philippe Ombredanne. Philippe prepared the
base worksheet, and did an initial spot review of a few 1000 files.
The 4.13 kernel was the starting point of the analysis with 60,537 files
assessed. Kate Stewart did a file by file comparison of the scanner
results in the spreadsheet to determine which SPDX license identifier(s)
to be applied to the file. She confirmed any determination that was not
immediately clear with lawyers working with the Linux Foundation.
Criteria used to select files for SPDX license identifier tagging was:
- Files considered eligible had to be source code files.
- Make and config files were included as candidates if they contained >5
lines of source
- File already had some variant of a license header in it (even if <5
lines).
All documentation files were explicitly excluded.
The following heuristics were used to determine which SPDX license
identifiers to apply.
- when both scanners couldn't find any license traces, file was
considered to have no license information in it, and the top level
COPYING file license applied.
For non */uapi/* files that summary was:
SPDX license identifier # files
---------------------------------------------------|-------
GPL-2.0 11139
and resulted in the first patch in this series.
If that file was a */uapi/* path one, it was "GPL-2.0 WITH
Linux-syscall-note" otherwise it was "GPL-2.0". Results of that was:
SPDX license identifier # files
---------------------------------------------------|-------
GPL-2.0 WITH Linux-syscall-note 930
and resulted in the second patch in this series.
- if a file had some form of licensing information in it, and was one
of the */uapi/* ones, it was denoted with the Linux-syscall-note if
any GPL family license was found in the file or had no licensing in
it (per prior point). Results summary:
SPDX license identifier # files
---------------------------------------------------|------
GPL-2.0 WITH Linux-syscall-note 270
GPL-2.0+ WITH Linux-syscall-note 169
((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) 21
((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) 17
LGPL-2.1+ WITH Linux-syscall-note 15
GPL-1.0+ WITH Linux-syscall-note 14
((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) 5
LGPL-2.0+ WITH Linux-syscall-note 4
LGPL-2.1 WITH Linux-syscall-note 3
((GPL-2.0 WITH Linux-syscall-note) OR MIT) 3
((GPL-2.0 WITH Linux-syscall-note) AND MIT) 1
and that resulted in the third patch in this series.
- when the two scanners agreed on the detected license(s), that became
the concluded license(s).
- when there was disagreement between the two scanners (one detected a
license but the other didn't, or they both detected different
licenses) a manual inspection of the file occurred.
- In most cases a manual inspection of the information in the file
resulted in a clear resolution of the license that should apply (and
which scanner probably needed to revisit its heuristics).
- When it was not immediately clear, the license identifier was
confirmed with lawyers working with the Linux Foundation.
- If there was any question as to the appropriate license identifier,
the file was flagged for further research and to be revisited later
in time.
In total, over 70 hours of logged manual review was done on the
spreadsheet to determine the SPDX license identifiers to apply to the
source files by Kate, Philippe, Thomas and, in some cases, confirmation
by lawyers working with the Linux Foundation.
Kate also obtained a third independent scan of the 4.13 code base from
FOSSology, and compared selected files where the other two scanners
disagreed against that SPDX file, to see if there was new insights. The
Windriver scanner is based on an older version of FOSSology in part, so
they are related.
Thomas did random spot checks in about 500 files from the spreadsheets
for the uapi headers and agreed with SPDX license identifier in the
files he inspected. For the non-uapi files Thomas did random spot checks
in about 15000 files.
In initial set of patches against 4.14-rc6, 3 files were found to have
copy/paste license identifier errors, and have been fixed to reflect the
correct identifier.
Additionally Philippe spent 10 hours this week doing a detailed manual
inspection and review of the 12,461 patched files from the initial patch
version early this week with:
- a full scancode scan run, collecting the matched texts, detected
license ids and scores
- reviewing anything where there was a license detected (about 500+
files) to ensure that the applied SPDX license was correct
- reviewing anything where there was no detection but the patch license
was not GPL-2.0 WITH Linux-syscall-note to ensure that the applied
SPDX license was correct
This produced a worksheet with 20 files needing minor correction. This
worksheet was then exported into 3 different .csv files for the
different types of files to be modified.
These .csv files were then reviewed by Greg. Thomas wrote a script to
parse the csv files and add the proper SPDX tag to the file, in the
format that the file expected. This script was further refined by Greg
based on the output to detect more types of files automatically and to
distinguish between header and source .c files (which need different
comment types.) Finally Greg ran the script using the .csv files to
generate the patches.
Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org>
Reviewed-by: Philippe Ombredanne <pombredanne@nexb.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-01 14:07:57 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2010-04-06 22:14:15 +00:00
|
|
|
#include <linux/ceph/ceph_debug.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 08:04:11 +00:00
|
|
|
|
2010-04-06 22:14:15 +00:00
|
|
|
#include <linux/module.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 08:04:11 +00:00
|
|
|
#include <linux/slab.h>
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2010-04-06 22:14:15 +00:00
|
|
|
#include <linux/ceph/libceph.h>
|
|
|
|
#include <linux/ceph/osdmap.h>
|
|
|
|
#include <linux/ceph/decode.h>
|
|
|
|
#include <linux/crush/hash.h>
|
|
|
|
#include <linux/crush/mapper.h>
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2017-06-22 17:44:06 +00:00
|
|
|
char *ceph_osdmap_state_str(char *str, int len, u32 state)
|
2009-10-06 18:31:10 +00:00
|
|
|
{
|
|
|
|
if (!len)
|
2013-01-25 23:48:59 +00:00
|
|
|
return str;
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2013-01-25 23:48:59 +00:00
|
|
|
if ((state & CEPH_OSD_EXISTS) && (state & CEPH_OSD_UP))
|
|
|
|
snprintf(str, len, "exists, up");
|
|
|
|
else if (state & CEPH_OSD_EXISTS)
|
|
|
|
snprintf(str, len, "exists");
|
|
|
|
else if (state & CEPH_OSD_UP)
|
|
|
|
snprintf(str, len, "up");
|
|
|
|
else
|
2009-10-06 18:31:10 +00:00
|
|
|
snprintf(str, len, "doesn't exist");
|
2013-01-25 23:48:59 +00:00
|
|
|
|
2009-10-06 18:31:10 +00:00
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* maps */
|
|
|
|
|
2012-04-15 05:58:06 +00:00
|
|
|
static int calc_bits_of(unsigned int t)
|
2009-10-06 18:31:10 +00:00
|
|
|
{
|
|
|
|
int b = 0;
|
|
|
|
while (t) {
|
|
|
|
t = t >> 1;
|
|
|
|
b++;
|
|
|
|
}
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* the foo_mask is the smallest value 2^n-1 that is >= foo.
|
|
|
|
*/
|
|
|
|
static void calc_pg_masks(struct ceph_pg_pool_info *pi)
|
|
|
|
{
|
2013-02-23 18:41:09 +00:00
|
|
|
pi->pg_num_mask = (1 << calc_bits_of(pi->pg_num-1)) - 1;
|
|
|
|
pi->pgp_num_mask = (1 << calc_bits_of(pi->pgp_num-1)) - 1;
|
2009-10-06 18:31:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* decode crush map
|
|
|
|
*/
|
|
|
|
static int crush_decode_uniform_bucket(void **p, void *end,
|
|
|
|
struct crush_bucket_uniform *b)
|
|
|
|
{
|
|
|
|
dout("crush_decode_uniform_bucket %p to %p\n", *p, end);
|
|
|
|
ceph_decode_need(p, end, (1+b->h.size) * sizeof(u32), bad);
|
2009-10-14 16:59:09 +00:00
|
|
|
b->item_weight = ceph_decode_32(p);
|
2009-10-06 18:31:10 +00:00
|
|
|
return 0;
|
|
|
|
bad:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int crush_decode_list_bucket(void **p, void *end,
|
|
|
|
struct crush_bucket_list *b)
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
dout("crush_decode_list_bucket %p to %p\n", *p, end);
|
|
|
|
b->item_weights = kcalloc(b->h.size, sizeof(u32), GFP_NOFS);
|
|
|
|
if (b->item_weights == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
b->sum_weights = kcalloc(b->h.size, sizeof(u32), GFP_NOFS);
|
|
|
|
if (b->sum_weights == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
ceph_decode_need(p, end, 2 * b->h.size * sizeof(u32), bad);
|
|
|
|
for (j = 0; j < b->h.size; j++) {
|
2009-10-14 16:59:09 +00:00
|
|
|
b->item_weights[j] = ceph_decode_32(p);
|
|
|
|
b->sum_weights[j] = ceph_decode_32(p);
|
2009-10-06 18:31:10 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int crush_decode_tree_bucket(void **p, void *end,
|
|
|
|
struct crush_bucket_tree *b)
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
dout("crush_decode_tree_bucket %p to %p\n", *p, end);
|
2015-06-29 16:30:23 +00:00
|
|
|
ceph_decode_8_safe(p, end, b->num_nodes, bad);
|
2009-10-06 18:31:10 +00:00
|
|
|
b->node_weights = kcalloc(b->num_nodes, sizeof(u32), GFP_NOFS);
|
|
|
|
if (b->node_weights == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
ceph_decode_need(p, end, b->num_nodes * sizeof(u32), bad);
|
|
|
|
for (j = 0; j < b->num_nodes; j++)
|
2009-10-14 16:59:09 +00:00
|
|
|
b->node_weights[j] = ceph_decode_32(p);
|
2009-10-06 18:31:10 +00:00
|
|
|
return 0;
|
|
|
|
bad:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int crush_decode_straw_bucket(void **p, void *end,
|
|
|
|
struct crush_bucket_straw *b)
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
dout("crush_decode_straw_bucket %p to %p\n", *p, end);
|
|
|
|
b->item_weights = kcalloc(b->h.size, sizeof(u32), GFP_NOFS);
|
|
|
|
if (b->item_weights == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
b->straws = kcalloc(b->h.size, sizeof(u32), GFP_NOFS);
|
|
|
|
if (b->straws == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
ceph_decode_need(p, end, 2 * b->h.size * sizeof(u32), bad);
|
|
|
|
for (j = 0; j < b->h.size; j++) {
|
2009-10-14 16:59:09 +00:00
|
|
|
b->item_weights[j] = ceph_decode_32(p);
|
|
|
|
b->straws[j] = ceph_decode_32(p);
|
2009-10-06 18:31:10 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2015-04-14 13:54:52 +00:00
|
|
|
static int crush_decode_straw2_bucket(void **p, void *end,
|
|
|
|
struct crush_bucket_straw2 *b)
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
dout("crush_decode_straw2_bucket %p to %p\n", *p, end);
|
|
|
|
b->item_weights = kcalloc(b->h.size, sizeof(u32), GFP_NOFS);
|
|
|
|
if (b->item_weights == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
ceph_decode_need(p, end, b->h.size * sizeof(u32), bad);
|
|
|
|
for (j = 0; j < b->h.size; j++)
|
|
|
|
b->item_weights[j] = ceph_decode_32(p);
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-05-19 15:09:52 +00:00
|
|
|
struct crush_name_node {
|
|
|
|
struct rb_node cn_node;
|
|
|
|
int cn_id;
|
|
|
|
char cn_name[];
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct crush_name_node *alloc_crush_name(size_t name_len)
|
|
|
|
{
|
|
|
|
struct crush_name_node *cn;
|
|
|
|
|
|
|
|
cn = kmalloc(sizeof(*cn) + name_len + 1, GFP_NOIO);
|
|
|
|
if (!cn)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
RB_CLEAR_NODE(&cn->cn_node);
|
|
|
|
return cn;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_crush_name(struct crush_name_node *cn)
|
|
|
|
{
|
|
|
|
WARN_ON(!RB_EMPTY_NODE(&cn->cn_node));
|
|
|
|
|
|
|
|
kfree(cn);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_RB_FUNCS(crush_name, struct crush_name_node, cn_id, cn_node)
|
|
|
|
|
|
|
|
static int decode_crush_names(void **p, void *end, struct rb_root *root)
|
|
|
|
{
|
|
|
|
u32 n;
|
|
|
|
|
|
|
|
ceph_decode_32_safe(p, end, n, e_inval);
|
|
|
|
while (n--) {
|
|
|
|
struct crush_name_node *cn;
|
|
|
|
int id;
|
|
|
|
u32 name_len;
|
|
|
|
|
|
|
|
ceph_decode_32_safe(p, end, id, e_inval);
|
|
|
|
ceph_decode_32_safe(p, end, name_len, e_inval);
|
|
|
|
ceph_decode_need(p, end, name_len, e_inval);
|
|
|
|
|
|
|
|
cn = alloc_crush_name(name_len);
|
|
|
|
if (!cn)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
cn->cn_id = id;
|
|
|
|
memcpy(cn->cn_name, *p, name_len);
|
|
|
|
cn->cn_name[name_len] = '\0';
|
|
|
|
*p += name_len;
|
|
|
|
|
|
|
|
if (!__insert_crush_name(root, cn)) {
|
|
|
|
free_crush_name(cn);
|
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
e_inval:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void clear_crush_names(struct rb_root *root)
|
|
|
|
{
|
|
|
|
while (!RB_EMPTY_ROOT(root)) {
|
|
|
|
struct crush_name_node *cn =
|
|
|
|
rb_entry(rb_first(root), struct crush_name_node, cn_node);
|
|
|
|
|
|
|
|
erase_crush_name(root, cn);
|
|
|
|
free_crush_name(cn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-22 17:44:05 +00:00
|
|
|
static struct crush_choose_arg_map *alloc_choose_arg_map(void)
|
|
|
|
{
|
|
|
|
struct crush_choose_arg_map *arg_map;
|
|
|
|
|
|
|
|
arg_map = kzalloc(sizeof(*arg_map), GFP_NOIO);
|
|
|
|
if (!arg_map)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
RB_CLEAR_NODE(&arg_map->node);
|
|
|
|
return arg_map;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_choose_arg_map(struct crush_choose_arg_map *arg_map)
|
|
|
|
{
|
|
|
|
if (arg_map) {
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
WARN_ON(!RB_EMPTY_NODE(&arg_map->node));
|
|
|
|
|
|
|
|
for (i = 0; i < arg_map->size; i++) {
|
|
|
|
struct crush_choose_arg *arg = &arg_map->args[i];
|
|
|
|
|
|
|
|
for (j = 0; j < arg->weight_set_size; j++)
|
|
|
|
kfree(arg->weight_set[j].weights);
|
|
|
|
kfree(arg->weight_set);
|
|
|
|
kfree(arg->ids);
|
|
|
|
}
|
|
|
|
kfree(arg_map->args);
|
|
|
|
kfree(arg_map);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_RB_FUNCS(choose_arg_map, struct crush_choose_arg_map, choose_args_index,
|
|
|
|
node);
|
|
|
|
|
|
|
|
void clear_choose_args(struct crush_map *c)
|
|
|
|
{
|
|
|
|
while (!RB_EMPTY_ROOT(&c->choose_args)) {
|
|
|
|
struct crush_choose_arg_map *arg_map =
|
|
|
|
rb_entry(rb_first(&c->choose_args),
|
|
|
|
struct crush_choose_arg_map, node);
|
|
|
|
|
|
|
|
erase_choose_arg_map(&c->choose_args, arg_map);
|
|
|
|
free_choose_arg_map(arg_map);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 *decode_array_32_alloc(void **p, void *end, u32 *plen)
|
|
|
|
{
|
|
|
|
u32 *a = NULL;
|
|
|
|
u32 len;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ceph_decode_32_safe(p, end, len, e_inval);
|
|
|
|
if (len) {
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
a = kmalloc_array(len, sizeof(u32), GFP_NOIO);
|
|
|
|
if (!a) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ceph_decode_need(p, end, len * sizeof(u32), e_inval);
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
a[i] = ceph_decode_32(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
*plen = len;
|
|
|
|
return a;
|
|
|
|
|
|
|
|
e_inval:
|
|
|
|
ret = -EINVAL;
|
|
|
|
fail:
|
|
|
|
kfree(a);
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Assumes @arg is zero-initialized.
|
|
|
|
*/
|
|
|
|
static int decode_choose_arg(void **p, void *end, struct crush_choose_arg *arg)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ceph_decode_32_safe(p, end, arg->weight_set_size, e_inval);
|
|
|
|
if (arg->weight_set_size) {
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
arg->weight_set = kmalloc_array(arg->weight_set_size,
|
|
|
|
sizeof(*arg->weight_set),
|
|
|
|
GFP_NOIO);
|
|
|
|
if (!arg->weight_set)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
for (i = 0; i < arg->weight_set_size; i++) {
|
|
|
|
struct crush_weight_set *w = &arg->weight_set[i];
|
|
|
|
|
|
|
|
w->weights = decode_array_32_alloc(p, end, &w->size);
|
|
|
|
if (IS_ERR(w->weights)) {
|
|
|
|
ret = PTR_ERR(w->weights);
|
|
|
|
w->weights = NULL;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
arg->ids = decode_array_32_alloc(p, end, &arg->ids_size);
|
|
|
|
if (IS_ERR(arg->ids)) {
|
|
|
|
ret = PTR_ERR(arg->ids);
|
|
|
|
arg->ids = NULL;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
e_inval:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int decode_choose_args(void **p, void *end, struct crush_map *c)
|
|
|
|
{
|
|
|
|
struct crush_choose_arg_map *arg_map = NULL;
|
|
|
|
u32 num_choose_arg_maps, num_buckets;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ceph_decode_32_safe(p, end, num_choose_arg_maps, e_inval);
|
|
|
|
while (num_choose_arg_maps--) {
|
|
|
|
arg_map = alloc_choose_arg_map();
|
|
|
|
if (!arg_map) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ceph_decode_64_safe(p, end, arg_map->choose_args_index,
|
|
|
|
e_inval);
|
|
|
|
arg_map->size = c->max_buckets;
|
|
|
|
arg_map->args = kcalloc(arg_map->size, sizeof(*arg_map->args),
|
|
|
|
GFP_NOIO);
|
|
|
|
if (!arg_map->args) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ceph_decode_32_safe(p, end, num_buckets, e_inval);
|
|
|
|
while (num_buckets--) {
|
|
|
|
struct crush_choose_arg *arg;
|
|
|
|
u32 bucket_index;
|
|
|
|
|
|
|
|
ceph_decode_32_safe(p, end, bucket_index, e_inval);
|
|
|
|
if (bucket_index >= arg_map->size)
|
|
|
|
goto e_inval;
|
|
|
|
|
|
|
|
arg = &arg_map->args[bucket_index];
|
|
|
|
ret = decode_choose_arg(p, end, arg);
|
|
|
|
if (ret)
|
|
|
|
goto fail;
|
2017-07-24 13:49:52 +00:00
|
|
|
|
|
|
|
if (arg->ids_size &&
|
|
|
|
arg->ids_size != c->buckets[bucket_index]->size)
|
|
|
|
goto e_inval;
|
2017-06-22 17:44:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
insert_choose_arg_map(&c->choose_args, arg_map);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
e_inval:
|
|
|
|
ret = -EINVAL;
|
|
|
|
fail:
|
|
|
|
free_choose_arg_map(arg_map);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-31 14:55:06 +00:00
|
|
|
static void crush_finalize(struct crush_map *c)
|
|
|
|
{
|
|
|
|
__s32 b;
|
|
|
|
|
|
|
|
/* Space for the array of pointers to per-bucket workspace */
|
|
|
|
c->working_size = sizeof(struct crush_work) +
|
|
|
|
c->max_buckets * sizeof(struct crush_work_bucket *);
|
|
|
|
|
|
|
|
for (b = 0; b < c->max_buckets; b++) {
|
|
|
|
if (!c->buckets[b])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (c->buckets[b]->alg) {
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* The base case, permutation variables and
|
|
|
|
* the pointer to the permutation array.
|
|
|
|
*/
|
|
|
|
c->working_size += sizeof(struct crush_work_bucket);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Every bucket has a permutation array. */
|
|
|
|
c->working_size += c->buckets[b]->size * sizeof(__u32);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-06 18:31:10 +00:00
|
|
|
static struct crush_map *crush_decode(void *pbyval, void *end)
|
|
|
|
{
|
|
|
|
struct crush_map *c;
|
2017-07-13 13:57:26 +00:00
|
|
|
int err;
|
2009-10-06 18:31:10 +00:00
|
|
|
int i, j;
|
|
|
|
void **p = &pbyval;
|
|
|
|
void *start = pbyval;
|
|
|
|
u32 magic;
|
|
|
|
|
|
|
|
dout("crush_decode %p to %p len %d\n", *p, end, (int)(end - *p));
|
|
|
|
|
|
|
|
c = kzalloc(sizeof(*c), GFP_NOFS);
|
|
|
|
if (c == NULL)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
2020-05-19 15:09:52 +00:00
|
|
|
c->type_names = RB_ROOT;
|
|
|
|
c->names = RB_ROOT;
|
2017-06-22 17:44:05 +00:00
|
|
|
c->choose_args = RB_ROOT;
|
|
|
|
|
2012-07-31 01:15:23 +00:00
|
|
|
/* set tunables to default values */
|
|
|
|
c->choose_local_tries = 2;
|
|
|
|
c->choose_local_fallback_tries = 5;
|
|
|
|
c->choose_total_tries = 19;
|
libceph: for chooseleaf rules, retry CRUSH map descent from root if leaf is failed
Add libceph support for a new CRUSH tunable recently added to Ceph servers.
Consider the CRUSH rule
step chooseleaf firstn 0 type <node_type>
This rule means that <n> replicas will be chosen in a manner such that
each chosen leaf's branch will contain a unique instance of <node_type>.
When an object is re-replicated after a leaf failure, if the CRUSH map uses
a chooseleaf rule the remapped replica ends up under the <node_type> bucket
that held the failed leaf. This causes uneven data distribution across the
storage cluster, to the point that when all the leaves but one fail under a
particular <node_type> bucket, that remaining leaf holds all the data from
its failed peers.
This behavior also limits the number of peers that can participate in the
re-replication of the data held by the failed leaf, which increases the
time required to re-replicate after a failure.
For a chooseleaf CRUSH rule, the tree descent has two steps: call them the
inner and outer descents.
If the tree descent down to <node_type> is the outer descent, and the descent
from <node_type> down to a leaf is the inner descent, the issue is that a
down leaf is detected on the inner descent, so only the inner descent is
retried.
In order to disperse re-replicated data as widely as possible across a
storage cluster after a failure, we want to retry the outer descent. So,
fix up crush_choose() to allow the inner descent to return immediately on
choosing a failed leaf. Wire this up as a new CRUSH tunable.
Note that after this change, for a chooseleaf rule, if the primary OSD
in a placement group has failed, choosing a replacement may result in
one of the other OSDs in the PG colliding with the new primary. This
requires that OSD's data for that PG to need moving as well. This
seems unavoidable but should be relatively rare.
This corresponds to ceph.git commit 88f218181a9e6d2292e2697fc93797d0f6d6e5dc.
Signed-off-by: Jim Schutt <jaschut@sandia.gov>
Reviewed-by: Sage Weil <sage@inktank.com>
2012-11-30 16:15:25 +00:00
|
|
|
c->chooseleaf_descend_once = 0;
|
2012-07-31 01:15:23 +00:00
|
|
|
|
2009-10-06 18:31:10 +00:00
|
|
|
ceph_decode_need(p, end, 4*sizeof(u32), bad);
|
2009-10-14 16:59:09 +00:00
|
|
|
magic = ceph_decode_32(p);
|
2009-10-06 18:31:10 +00:00
|
|
|
if (magic != CRUSH_MAGIC) {
|
|
|
|
pr_err("crush_decode magic %x != current %x\n",
|
2012-04-15 05:58:06 +00:00
|
|
|
(unsigned int)magic, (unsigned int)CRUSH_MAGIC);
|
2009-10-06 18:31:10 +00:00
|
|
|
goto bad;
|
|
|
|
}
|
2009-10-14 16:59:09 +00:00
|
|
|
c->max_buckets = ceph_decode_32(p);
|
|
|
|
c->max_rules = ceph_decode_32(p);
|
|
|
|
c->max_devices = ceph_decode_32(p);
|
2009-10-06 18:31:10 +00:00
|
|
|
|
|
|
|
c->buckets = kcalloc(c->max_buckets, sizeof(*c->buckets), GFP_NOFS);
|
|
|
|
if (c->buckets == NULL)
|
|
|
|
goto badmem;
|
|
|
|
c->rules = kcalloc(c->max_rules, sizeof(*c->rules), GFP_NOFS);
|
|
|
|
if (c->rules == NULL)
|
|
|
|
goto badmem;
|
|
|
|
|
|
|
|
/* buckets */
|
|
|
|
for (i = 0; i < c->max_buckets; i++) {
|
|
|
|
int size = 0;
|
|
|
|
u32 alg;
|
|
|
|
struct crush_bucket *b;
|
|
|
|
|
|
|
|
ceph_decode_32_safe(p, end, alg, bad);
|
|
|
|
if (alg == 0) {
|
|
|
|
c->buckets[i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
dout("crush_decode bucket %d off %x %p to %p\n",
|
|
|
|
i, (int)(*p-start), *p, end);
|
|
|
|
|
|
|
|
switch (alg) {
|
|
|
|
case CRUSH_BUCKET_UNIFORM:
|
|
|
|
size = sizeof(struct crush_bucket_uniform);
|
|
|
|
break;
|
|
|
|
case CRUSH_BUCKET_LIST:
|
|
|
|
size = sizeof(struct crush_bucket_list);
|
|
|
|
break;
|
|
|
|
case CRUSH_BUCKET_TREE:
|
|
|
|
size = sizeof(struct crush_bucket_tree);
|
|
|
|
break;
|
|
|
|
case CRUSH_BUCKET_STRAW:
|
|
|
|
size = sizeof(struct crush_bucket_straw);
|
|
|
|
break;
|
2015-04-14 13:54:52 +00:00
|
|
|
case CRUSH_BUCKET_STRAW2:
|
|
|
|
size = sizeof(struct crush_bucket_straw2);
|
|
|
|
break;
|
2009-10-06 18:31:10 +00:00
|
|
|
default:
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
BUG_ON(size == 0);
|
|
|
|
b = c->buckets[i] = kzalloc(size, GFP_NOFS);
|
|
|
|
if (b == NULL)
|
|
|
|
goto badmem;
|
|
|
|
|
|
|
|
ceph_decode_need(p, end, 4*sizeof(u32), bad);
|
2009-10-14 16:59:09 +00:00
|
|
|
b->id = ceph_decode_32(p);
|
|
|
|
b->type = ceph_decode_16(p);
|
2009-11-08 04:18:22 +00:00
|
|
|
b->alg = ceph_decode_8(p);
|
|
|
|
b->hash = ceph_decode_8(p);
|
2009-10-14 16:59:09 +00:00
|
|
|
b->weight = ceph_decode_32(p);
|
|
|
|
b->size = ceph_decode_32(p);
|
2009-10-06 18:31:10 +00:00
|
|
|
|
|
|
|
dout("crush_decode bucket size %d off %x %p to %p\n",
|
|
|
|
b->size, (int)(*p-start), *p, end);
|
|
|
|
|
|
|
|
b->items = kcalloc(b->size, sizeof(__s32), GFP_NOFS);
|
|
|
|
if (b->items == NULL)
|
|
|
|
goto badmem;
|
|
|
|
|
|
|
|
ceph_decode_need(p, end, b->size*sizeof(u32), bad);
|
|
|
|
for (j = 0; j < b->size; j++)
|
2009-10-14 16:59:09 +00:00
|
|
|
b->items[j] = ceph_decode_32(p);
|
2009-10-06 18:31:10 +00:00
|
|
|
|
|
|
|
switch (b->alg) {
|
|
|
|
case CRUSH_BUCKET_UNIFORM:
|
|
|
|
err = crush_decode_uniform_bucket(p, end,
|
|
|
|
(struct crush_bucket_uniform *)b);
|
|
|
|
if (err < 0)
|
2017-07-13 13:57:26 +00:00
|
|
|
goto fail;
|
2009-10-06 18:31:10 +00:00
|
|
|
break;
|
|
|
|
case CRUSH_BUCKET_LIST:
|
|
|
|
err = crush_decode_list_bucket(p, end,
|
|
|
|
(struct crush_bucket_list *)b);
|
|
|
|
if (err < 0)
|
2017-07-13 13:57:26 +00:00
|
|
|
goto fail;
|
2009-10-06 18:31:10 +00:00
|
|
|
break;
|
|
|
|
case CRUSH_BUCKET_TREE:
|
|
|
|
err = crush_decode_tree_bucket(p, end,
|
|
|
|
(struct crush_bucket_tree *)b);
|
|
|
|
if (err < 0)
|
2017-07-13 13:57:26 +00:00
|
|
|
goto fail;
|
2009-10-06 18:31:10 +00:00
|
|
|
break;
|
|
|
|
case CRUSH_BUCKET_STRAW:
|
|
|
|
err = crush_decode_straw_bucket(p, end,
|
|
|
|
(struct crush_bucket_straw *)b);
|
|
|
|
if (err < 0)
|
2017-07-13 13:57:26 +00:00
|
|
|
goto fail;
|
2009-10-06 18:31:10 +00:00
|
|
|
break;
|
2015-04-14 13:54:52 +00:00
|
|
|
case CRUSH_BUCKET_STRAW2:
|
|
|
|
err = crush_decode_straw2_bucket(p, end,
|
|
|
|
(struct crush_bucket_straw2 *)b);
|
|
|
|
if (err < 0)
|
2017-07-13 13:57:26 +00:00
|
|
|
goto fail;
|
2015-04-14 13:54:52 +00:00
|
|
|
break;
|
2009-10-06 18:31:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* rules */
|
|
|
|
dout("rule vec is %p\n", c->rules);
|
|
|
|
for (i = 0; i < c->max_rules; i++) {
|
|
|
|
u32 yes;
|
|
|
|
struct crush_rule *r;
|
|
|
|
|
|
|
|
ceph_decode_32_safe(p, end, yes, bad);
|
|
|
|
if (!yes) {
|
|
|
|
dout("crush_decode NO rule %d off %x %p to %p\n",
|
|
|
|
i, (int)(*p-start), *p, end);
|
|
|
|
c->rules[i] = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
dout("crush_decode rule %d off %x %p to %p\n",
|
|
|
|
i, (int)(*p-start), *p, end);
|
|
|
|
|
|
|
|
/* len */
|
|
|
|
ceph_decode_32_safe(p, end, yes, bad);
|
|
|
|
#if BITS_PER_LONG == 32
|
2012-02-16 16:55:48 +00:00
|
|
|
if (yes > (ULONG_MAX - sizeof(*r))
|
|
|
|
/ sizeof(struct crush_rule_step))
|
2009-10-06 18:31:10 +00:00
|
|
|
goto bad;
|
|
|
|
#endif
|
2019-01-15 19:41:53 +00:00
|
|
|
r = kmalloc(struct_size(r, steps, yes), GFP_NOFS);
|
|
|
|
c->rules[i] = r;
|
2009-10-06 18:31:10 +00:00
|
|
|
if (r == NULL)
|
|
|
|
goto badmem;
|
|
|
|
dout(" rule %d is at %p\n", i, r);
|
|
|
|
r->len = yes;
|
|
|
|
ceph_decode_copy_safe(p, end, &r->mask, 4, bad); /* 4 u8's */
|
|
|
|
ceph_decode_need(p, end, r->len*3*sizeof(u32), bad);
|
|
|
|
for (j = 0; j < r->len; j++) {
|
2009-10-14 16:59:09 +00:00
|
|
|
r->steps[j].op = ceph_decode_32(p);
|
|
|
|
r->steps[j].arg1 = ceph_decode_32(p);
|
|
|
|
r->steps[j].arg2 = ceph_decode_32(p);
|
2009-10-06 18:31:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-19 15:09:52 +00:00
|
|
|
err = decode_crush_names(p, end, &c->type_names);
|
|
|
|
if (err)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
err = decode_crush_names(p, end, &c->names);
|
|
|
|
if (err)
|
|
|
|
goto fail;
|
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
ceph_decode_skip_map(p, end, 32, string, bad); /* rule_name_map */
|
2012-07-31 01:15:23 +00:00
|
|
|
|
|
|
|
/* tunables */
|
|
|
|
ceph_decode_need(p, end, 3*sizeof(u32), done);
|
|
|
|
c->choose_local_tries = ceph_decode_32(p);
|
|
|
|
c->choose_local_fallback_tries = ceph_decode_32(p);
|
|
|
|
c->choose_total_tries = ceph_decode_32(p);
|
2016-02-01 15:57:16 +00:00
|
|
|
dout("crush decode tunable choose_local_tries = %d\n",
|
2012-07-31 01:15:23 +00:00
|
|
|
c->choose_local_tries);
|
2016-02-01 15:57:16 +00:00
|
|
|
dout("crush decode tunable choose_local_fallback_tries = %d\n",
|
2012-07-31 01:15:23 +00:00
|
|
|
c->choose_local_fallback_tries);
|
2016-02-01 15:57:16 +00:00
|
|
|
dout("crush decode tunable choose_total_tries = %d\n",
|
2012-07-31 01:15:23 +00:00
|
|
|
c->choose_total_tries);
|
2009-10-06 18:31:10 +00:00
|
|
|
|
libceph: for chooseleaf rules, retry CRUSH map descent from root if leaf is failed
Add libceph support for a new CRUSH tunable recently added to Ceph servers.
Consider the CRUSH rule
step chooseleaf firstn 0 type <node_type>
This rule means that <n> replicas will be chosen in a manner such that
each chosen leaf's branch will contain a unique instance of <node_type>.
When an object is re-replicated after a leaf failure, if the CRUSH map uses
a chooseleaf rule the remapped replica ends up under the <node_type> bucket
that held the failed leaf. This causes uneven data distribution across the
storage cluster, to the point that when all the leaves but one fail under a
particular <node_type> bucket, that remaining leaf holds all the data from
its failed peers.
This behavior also limits the number of peers that can participate in the
re-replication of the data held by the failed leaf, which increases the
time required to re-replicate after a failure.
For a chooseleaf CRUSH rule, the tree descent has two steps: call them the
inner and outer descents.
If the tree descent down to <node_type> is the outer descent, and the descent
from <node_type> down to a leaf is the inner descent, the issue is that a
down leaf is detected on the inner descent, so only the inner descent is
retried.
In order to disperse re-replicated data as widely as possible across a
storage cluster after a failure, we want to retry the outer descent. So,
fix up crush_choose() to allow the inner descent to return immediately on
choosing a failed leaf. Wire this up as a new CRUSH tunable.
Note that after this change, for a chooseleaf rule, if the primary OSD
in a placement group has failed, choosing a replacement may result in
one of the other OSDs in the PG colliding with the new primary. This
requires that OSD's data for that PG to need moving as well. This
seems unavoidable but should be relatively rare.
This corresponds to ceph.git commit 88f218181a9e6d2292e2697fc93797d0f6d6e5dc.
Signed-off-by: Jim Schutt <jaschut@sandia.gov>
Reviewed-by: Sage Weil <sage@inktank.com>
2012-11-30 16:15:25 +00:00
|
|
|
ceph_decode_need(p, end, sizeof(u32), done);
|
|
|
|
c->chooseleaf_descend_once = ceph_decode_32(p);
|
2016-02-01 15:57:16 +00:00
|
|
|
dout("crush decode tunable chooseleaf_descend_once = %d\n",
|
libceph: for chooseleaf rules, retry CRUSH map descent from root if leaf is failed
Add libceph support for a new CRUSH tunable recently added to Ceph servers.
Consider the CRUSH rule
step chooseleaf firstn 0 type <node_type>
This rule means that <n> replicas will be chosen in a manner such that
each chosen leaf's branch will contain a unique instance of <node_type>.
When an object is re-replicated after a leaf failure, if the CRUSH map uses
a chooseleaf rule the remapped replica ends up under the <node_type> bucket
that held the failed leaf. This causes uneven data distribution across the
storage cluster, to the point that when all the leaves but one fail under a
particular <node_type> bucket, that remaining leaf holds all the data from
its failed peers.
This behavior also limits the number of peers that can participate in the
re-replication of the data held by the failed leaf, which increases the
time required to re-replicate after a failure.
For a chooseleaf CRUSH rule, the tree descent has two steps: call them the
inner and outer descents.
If the tree descent down to <node_type> is the outer descent, and the descent
from <node_type> down to a leaf is the inner descent, the issue is that a
down leaf is detected on the inner descent, so only the inner descent is
retried.
In order to disperse re-replicated data as widely as possible across a
storage cluster after a failure, we want to retry the outer descent. So,
fix up crush_choose() to allow the inner descent to return immediately on
choosing a failed leaf. Wire this up as a new CRUSH tunable.
Note that after this change, for a chooseleaf rule, if the primary OSD
in a placement group has failed, choosing a replacement may result in
one of the other OSDs in the PG colliding with the new primary. This
requires that OSD's data for that PG to need moving as well. This
seems unavoidable but should be relatively rare.
This corresponds to ceph.git commit 88f218181a9e6d2292e2697fc93797d0f6d6e5dc.
Signed-off-by: Jim Schutt <jaschut@sandia.gov>
Reviewed-by: Sage Weil <sage@inktank.com>
2012-11-30 16:15:25 +00:00
|
|
|
c->chooseleaf_descend_once);
|
|
|
|
|
2014-05-09 14:27:34 +00:00
|
|
|
ceph_decode_need(p, end, sizeof(u8), done);
|
|
|
|
c->chooseleaf_vary_r = ceph_decode_8(p);
|
2016-02-01 15:57:16 +00:00
|
|
|
dout("crush decode tunable chooseleaf_vary_r = %d\n",
|
2014-05-09 14:27:34 +00:00
|
|
|
c->chooseleaf_vary_r);
|
|
|
|
|
2016-02-01 15:57:16 +00:00
|
|
|
/* skip straw_calc_version, allowed_bucket_algs */
|
|
|
|
ceph_decode_need(p, end, sizeof(u8) + sizeof(u32), done);
|
|
|
|
*p += sizeof(u8) + sizeof(u32);
|
|
|
|
|
|
|
|
ceph_decode_need(p, end, sizeof(u8), done);
|
|
|
|
c->chooseleaf_stable = ceph_decode_8(p);
|
|
|
|
dout("crush decode tunable chooseleaf_stable = %d\n",
|
|
|
|
c->chooseleaf_stable);
|
|
|
|
|
2017-06-22 17:44:05 +00:00
|
|
|
if (*p != end) {
|
|
|
|
/* class_map */
|
|
|
|
ceph_decode_skip_map(p, end, 32, 32, bad);
|
|
|
|
/* class_name */
|
|
|
|
ceph_decode_skip_map(p, end, 32, string, bad);
|
|
|
|
/* class_bucket */
|
|
|
|
ceph_decode_skip_map_of_map(p, end, 32, 32, 32, bad);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*p != end) {
|
|
|
|
err = decode_choose_args(p, end, c);
|
|
|
|
if (err)
|
2017-07-13 13:57:26 +00:00
|
|
|
goto fail;
|
2017-06-22 17:44:05 +00:00
|
|
|
}
|
|
|
|
|
2012-07-31 01:15:23 +00:00
|
|
|
done:
|
2017-02-28 17:53:53 +00:00
|
|
|
crush_finalize(c);
|
2009-10-06 18:31:10 +00:00
|
|
|
dout("crush_decode success\n");
|
|
|
|
return c;
|
|
|
|
|
|
|
|
badmem:
|
|
|
|
err = -ENOMEM;
|
2017-07-13 13:57:26 +00:00
|
|
|
fail:
|
2009-10-06 18:31:10 +00:00
|
|
|
dout("crush_decode fail %d\n", err);
|
|
|
|
crush_destroy(c);
|
|
|
|
return ERR_PTR(err);
|
2017-07-13 13:57:26 +00:00
|
|
|
|
|
|
|
bad:
|
|
|
|
err = -EINVAL;
|
|
|
|
goto fail;
|
2009-10-06 18:31:10 +00:00
|
|
|
}
|
|
|
|
|
2016-04-28 14:07:23 +00:00
|
|
|
int ceph_pg_compare(const struct ceph_pg *lhs, const struct ceph_pg *rhs)
|
2009-11-04 19:39:12 +00:00
|
|
|
{
|
2016-04-28 14:07:23 +00:00
|
|
|
if (lhs->pool < rhs->pool)
|
2013-02-23 18:38:16 +00:00
|
|
|
return -1;
|
2016-04-28 14:07:23 +00:00
|
|
|
if (lhs->pool > rhs->pool)
|
2013-02-23 18:38:16 +00:00
|
|
|
return 1;
|
2016-04-28 14:07:23 +00:00
|
|
|
if (lhs->seed < rhs->seed)
|
2009-11-04 19:39:12 +00:00
|
|
|
return -1;
|
2016-04-28 14:07:23 +00:00
|
|
|
if (lhs->seed > rhs->seed)
|
2009-11-04 19:39:12 +00:00
|
|
|
return 1;
|
2016-04-28 14:07:23 +00:00
|
|
|
|
2009-11-04 19:39:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-19 10:18:05 +00:00
|
|
|
int ceph_spg_compare(const struct ceph_spg *lhs, const struct ceph_spg *rhs)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ceph_pg_compare(&lhs->pgid, &rhs->pgid);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (lhs->shard < rhs->shard)
|
|
|
|
return -1;
|
|
|
|
if (lhs->shard > rhs->shard)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
static struct ceph_pg_mapping *alloc_pg_mapping(size_t payload_len)
|
|
|
|
{
|
|
|
|
struct ceph_pg_mapping *pg;
|
|
|
|
|
|
|
|
pg = kmalloc(sizeof(*pg) + payload_len, GFP_NOIO);
|
|
|
|
if (!pg)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
RB_CLEAR_NODE(&pg->node);
|
|
|
|
return pg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_pg_mapping(struct ceph_pg_mapping *pg)
|
|
|
|
{
|
|
|
|
WARN_ON(!RB_EMPTY_NODE(&pg->node));
|
|
|
|
|
|
|
|
kfree(pg);
|
|
|
|
}
|
|
|
|
|
2016-04-28 14:07:23 +00:00
|
|
|
/*
|
|
|
|
* rbtree of pg_mapping for handling pg_temp (explicit mapping of pgid
|
|
|
|
* to a set of osds) and primary_temp (explicit primary setting)
|
|
|
|
*/
|
2017-06-21 15:27:17 +00:00
|
|
|
DEFINE_RB_FUNCS2(pg_mapping, struct ceph_pg_mapping, pgid, ceph_pg_compare,
|
|
|
|
RB_BYPTR, const struct ceph_pg *, node)
|
2011-09-28 17:11:04 +00:00
|
|
|
|
2010-02-16 23:55:03 +00:00
|
|
|
/*
|
|
|
|
* rbtree of pg pool info
|
|
|
|
*/
|
2020-05-19 14:46:47 +00:00
|
|
|
DEFINE_RB_FUNCS(pg_pool, struct ceph_pg_pool_info, id, node)
|
2010-02-16 23:55:03 +00:00
|
|
|
|
2014-01-27 15:40:19 +00:00
|
|
|
struct ceph_pg_pool_info *ceph_pg_pool_by_id(struct ceph_osdmap *map, u64 id)
|
|
|
|
{
|
2020-05-19 14:46:47 +00:00
|
|
|
return lookup_pg_pool(&map->pg_pools, id);
|
2014-01-27 15:40:19 +00:00
|
|
|
}
|
|
|
|
|
2012-10-31 00:40:33 +00:00
|
|
|
const char *ceph_pg_pool_name_by_id(struct ceph_osdmap *map, u64 id)
|
|
|
|
{
|
|
|
|
struct ceph_pg_pool_info *pi;
|
|
|
|
|
|
|
|
if (id == CEPH_NOPOOL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (WARN_ON_ONCE(id > (u64) INT_MAX))
|
|
|
|
return NULL;
|
|
|
|
|
2020-05-19 14:46:47 +00:00
|
|
|
pi = lookup_pg_pool(&map->pg_pools, id);
|
2012-10-31 00:40:33 +00:00
|
|
|
return pi ? pi->name : NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ceph_pg_pool_name_by_id);
|
|
|
|
|
2010-05-17 19:31:35 +00:00
|
|
|
int ceph_pg_poolid_by_name(struct ceph_osdmap *map, const char *name)
|
|
|
|
{
|
|
|
|
struct rb_node *rbp;
|
|
|
|
|
|
|
|
for (rbp = rb_first(&map->pg_pools); rbp; rbp = rb_next(rbp)) {
|
|
|
|
struct ceph_pg_pool_info *pi =
|
|
|
|
rb_entry(rbp, struct ceph_pg_pool_info, node);
|
|
|
|
if (pi->name && strcmp(pi->name, name) == 0)
|
|
|
|
return pi->id;
|
|
|
|
}
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2010-04-06 22:14:15 +00:00
|
|
|
EXPORT_SYMBOL(ceph_pg_poolid_by_name);
|
2020-03-09 11:03:14 +00:00
|
|
|
|
|
|
|
u64 ceph_pg_pool_flags(struct ceph_osdmap *map, u64 id)
|
|
|
|
{
|
|
|
|
struct ceph_pg_pool_info *pi;
|
|
|
|
|
2020-05-19 14:46:47 +00:00
|
|
|
pi = lookup_pg_pool(&map->pg_pools, id);
|
2020-03-09 11:03:14 +00:00
|
|
|
return pi ? pi->flags : 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ceph_pg_pool_flags);
|
2010-05-17 19:31:35 +00:00
|
|
|
|
2010-04-09 22:46:42 +00:00
|
|
|
static void __remove_pg_pool(struct rb_root *root, struct ceph_pg_pool_info *pi)
|
|
|
|
{
|
2020-05-19 14:46:47 +00:00
|
|
|
erase_pg_pool(root, pi);
|
2010-04-09 22:46:42 +00:00
|
|
|
kfree(pi->name);
|
|
|
|
kfree(pi);
|
|
|
|
}
|
|
|
|
|
2014-03-21 17:05:27 +00:00
|
|
|
static int decode_pool(void **p, void *end, struct ceph_pg_pool_info *pi)
|
2010-03-17 17:05:28 +00:00
|
|
|
{
|
2013-02-23 18:41:09 +00:00
|
|
|
u8 ev, cv;
|
|
|
|
unsigned len, num;
|
|
|
|
void *pool_end;
|
|
|
|
|
|
|
|
ceph_decode_need(p, end, 2 + 4, bad);
|
|
|
|
ev = ceph_decode_8(p); /* encoding version */
|
|
|
|
cv = ceph_decode_8(p); /* compat version */
|
|
|
|
if (ev < 5) {
|
2014-09-10 04:17:29 +00:00
|
|
|
pr_warn("got v %d < 5 cv %d of ceph_pg_pool\n", ev, cv);
|
2013-02-23 18:41:09 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2014-01-27 15:40:19 +00:00
|
|
|
if (cv > 9) {
|
2014-09-10 04:17:29 +00:00
|
|
|
pr_warn("got v %d cv %d > 9 of ceph_pg_pool\n", ev, cv);
|
2013-02-23 18:41:09 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
len = ceph_decode_32(p);
|
|
|
|
ceph_decode_need(p, end, len, bad);
|
|
|
|
pool_end = *p + len;
|
2010-08-02 18:00:55 +00:00
|
|
|
|
2013-02-23 18:41:09 +00:00
|
|
|
pi->type = ceph_decode_8(p);
|
|
|
|
pi->size = ceph_decode_8(p);
|
|
|
|
pi->crush_ruleset = ceph_decode_8(p);
|
|
|
|
pi->object_hash = ceph_decode_8(p);
|
2010-08-02 18:00:55 +00:00
|
|
|
|
2013-02-23 18:41:09 +00:00
|
|
|
pi->pg_num = ceph_decode_32(p);
|
|
|
|
pi->pgp_num = ceph_decode_32(p);
|
|
|
|
|
|
|
|
*p += 4 + 4; /* skip lpg* */
|
|
|
|
*p += 4; /* skip last_change */
|
|
|
|
*p += 8 + 4; /* skip snap_seq, snap_epoch */
|
|
|
|
|
|
|
|
/* skip snaps */
|
|
|
|
num = ceph_decode_32(p);
|
|
|
|
while (num--) {
|
|
|
|
*p += 8; /* snapid key */
|
|
|
|
*p += 1 + 1; /* versions */
|
|
|
|
len = ceph_decode_32(p);
|
|
|
|
*p += len;
|
2010-08-02 18:00:55 +00:00
|
|
|
}
|
|
|
|
|
2014-01-27 15:40:19 +00:00
|
|
|
/* skip removed_snaps */
|
2013-02-23 18:41:09 +00:00
|
|
|
num = ceph_decode_32(p);
|
|
|
|
*p += num * (8 + 8);
|
|
|
|
|
|
|
|
*p += 8; /* skip auid */
|
|
|
|
pi->flags = ceph_decode_64(p);
|
2014-01-27 15:40:19 +00:00
|
|
|
*p += 4; /* skip crash_replay_interval */
|
|
|
|
|
|
|
|
if (ev >= 7)
|
2016-04-28 14:07:23 +00:00
|
|
|
pi->min_size = ceph_decode_8(p);
|
|
|
|
else
|
|
|
|
pi->min_size = pi->size - pi->size / 2;
|
2014-01-27 15:40:19 +00:00
|
|
|
|
|
|
|
if (ev >= 8)
|
|
|
|
*p += 8 + 8; /* skip quota_max_* */
|
|
|
|
|
|
|
|
if (ev >= 9) {
|
|
|
|
/* skip tiers */
|
|
|
|
num = ceph_decode_32(p);
|
|
|
|
*p += num * 8;
|
|
|
|
|
|
|
|
*p += 8; /* skip tier_of */
|
|
|
|
*p += 1; /* skip cache_mode */
|
|
|
|
|
|
|
|
pi->read_tier = ceph_decode_64(p);
|
|
|
|
pi->write_tier = ceph_decode_64(p);
|
|
|
|
} else {
|
|
|
|
pi->read_tier = -1;
|
|
|
|
pi->write_tier = -1;
|
|
|
|
}
|
2013-02-23 18:41:09 +00:00
|
|
|
|
2016-04-28 14:07:23 +00:00
|
|
|
if (ev >= 10) {
|
|
|
|
/* skip properties */
|
|
|
|
num = ceph_decode_32(p);
|
|
|
|
while (num--) {
|
|
|
|
len = ceph_decode_32(p);
|
|
|
|
*p += len; /* key */
|
|
|
|
len = ceph_decode_32(p);
|
|
|
|
*p += len; /* val */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ev >= 11) {
|
|
|
|
/* skip hit_set_params */
|
|
|
|
*p += 1 + 1; /* versions */
|
|
|
|
len = ceph_decode_32(p);
|
|
|
|
*p += len;
|
|
|
|
|
|
|
|
*p += 4; /* skip hit_set_period */
|
|
|
|
*p += 4; /* skip hit_set_count */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ev >= 12)
|
|
|
|
*p += 4; /* skip stripe_width */
|
|
|
|
|
|
|
|
if (ev >= 13) {
|
|
|
|
*p += 8; /* skip target_max_bytes */
|
|
|
|
*p += 8; /* skip target_max_objects */
|
|
|
|
*p += 4; /* skip cache_target_dirty_ratio_micro */
|
|
|
|
*p += 4; /* skip cache_target_full_ratio_micro */
|
|
|
|
*p += 4; /* skip cache_min_flush_age */
|
|
|
|
*p += 4; /* skip cache_min_evict_age */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ev >= 14) {
|
|
|
|
/* skip erasure_code_profile */
|
|
|
|
len = ceph_decode_32(p);
|
|
|
|
*p += len;
|
|
|
|
}
|
|
|
|
|
2017-06-05 12:45:00 +00:00
|
|
|
/*
|
|
|
|
* last_force_op_resend_preluminous, will be overridden if the
|
|
|
|
* map was encoded with RESEND_ON_SPLIT
|
|
|
|
*/
|
2016-04-28 14:07:23 +00:00
|
|
|
if (ev >= 15)
|
|
|
|
pi->last_force_request_resend = ceph_decode_32(p);
|
|
|
|
else
|
|
|
|
pi->last_force_request_resend = 0;
|
|
|
|
|
2017-06-05 12:45:00 +00:00
|
|
|
if (ev >= 16)
|
|
|
|
*p += 4; /* skip min_read_recency_for_promote */
|
|
|
|
|
|
|
|
if (ev >= 17)
|
|
|
|
*p += 8; /* skip expected_num_objects */
|
|
|
|
|
|
|
|
if (ev >= 19)
|
|
|
|
*p += 4; /* skip cache_target_dirty_high_ratio_micro */
|
|
|
|
|
|
|
|
if (ev >= 20)
|
|
|
|
*p += 4; /* skip min_write_recency_for_promote */
|
|
|
|
|
|
|
|
if (ev >= 21)
|
|
|
|
*p += 1; /* skip use_gmt_hitset */
|
|
|
|
|
|
|
|
if (ev >= 22)
|
|
|
|
*p += 1; /* skip fast_read */
|
|
|
|
|
|
|
|
if (ev >= 23) {
|
|
|
|
*p += 4; /* skip hit_set_grade_decay_rate */
|
|
|
|
*p += 4; /* skip hit_set_search_last_n */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ev >= 24) {
|
|
|
|
/* skip opts */
|
|
|
|
*p += 1 + 1; /* versions */
|
|
|
|
len = ceph_decode_32(p);
|
|
|
|
*p += len;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ev >= 25)
|
|
|
|
pi->last_force_request_resend = ceph_decode_32(p);
|
|
|
|
|
2013-02-23 18:41:09 +00:00
|
|
|
/* ignore the rest */
|
|
|
|
|
|
|
|
*p = pool_end;
|
|
|
|
calc_pg_masks(pi);
|
2010-08-02 18:00:55 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
bad:
|
|
|
|
return -EINVAL;
|
2010-03-17 17:05:28 +00:00
|
|
|
}
|
|
|
|
|
2014-03-21 17:05:27 +00:00
|
|
|
static int decode_pool_names(void **p, void *end, struct ceph_osdmap *map)
|
2010-04-09 22:46:42 +00:00
|
|
|
{
|
|
|
|
struct ceph_pg_pool_info *pi;
|
2013-02-23 18:41:09 +00:00
|
|
|
u32 num, len;
|
|
|
|
u64 pool;
|
2010-04-09 22:46:42 +00:00
|
|
|
|
|
|
|
ceph_decode_32_safe(p, end, num, bad);
|
|
|
|
dout(" %d pool names\n", num);
|
|
|
|
while (num--) {
|
2013-02-23 18:41:09 +00:00
|
|
|
ceph_decode_64_safe(p, end, pool, bad);
|
2010-04-09 22:46:42 +00:00
|
|
|
ceph_decode_32_safe(p, end, len, bad);
|
2013-02-23 18:41:09 +00:00
|
|
|
dout(" pool %llu len %d\n", pool, len);
|
2012-06-07 00:35:55 +00:00
|
|
|
ceph_decode_need(p, end, len, bad);
|
2020-05-19 14:46:47 +00:00
|
|
|
pi = lookup_pg_pool(&map->pg_pools, pool);
|
2010-04-09 22:46:42 +00:00
|
|
|
if (pi) {
|
2012-06-07 00:35:55 +00:00
|
|
|
char *name = kstrndup(*p, len, GFP_NOFS);
|
|
|
|
|
|
|
|
if (!name)
|
|
|
|
return -ENOMEM;
|
2010-04-09 22:46:42 +00:00
|
|
|
kfree(pi->name);
|
2012-06-07 00:35:55 +00:00
|
|
|
pi->name = name;
|
|
|
|
dout(" name is %s\n", pi->name);
|
2010-04-09 22:46:42 +00:00
|
|
|
}
|
|
|
|
*p += len;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
bad:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-08-17 11:45:04 +00:00
|
|
|
/*
|
|
|
|
* CRUSH workspaces
|
|
|
|
*
|
|
|
|
* workspace_manager framework borrowed from fs/btrfs/compression.c.
|
|
|
|
* Two simplifications: there is only one type of workspace and there
|
|
|
|
* is always at least one workspace.
|
|
|
|
*/
|
|
|
|
static struct crush_work *alloc_workspace(const struct crush_map *c)
|
|
|
|
{
|
|
|
|
struct crush_work *work;
|
|
|
|
size_t work_size;
|
|
|
|
|
|
|
|
WARN_ON(!c->working_size);
|
|
|
|
work_size = crush_work_size(c, CEPH_PG_MAX_SIZE);
|
|
|
|
dout("%s work_size %zu bytes\n", __func__, work_size);
|
|
|
|
|
|
|
|
work = ceph_kvmalloc(work_size, GFP_NOIO);
|
|
|
|
if (!work)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&work->item);
|
|
|
|
crush_init_workspace(c, work);
|
|
|
|
return work;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_workspace(struct crush_work *work)
|
|
|
|
{
|
|
|
|
WARN_ON(!list_empty(&work->item));
|
|
|
|
kvfree(work);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void init_workspace_manager(struct workspace_manager *wsm)
|
|
|
|
{
|
|
|
|
INIT_LIST_HEAD(&wsm->idle_ws);
|
|
|
|
spin_lock_init(&wsm->ws_lock);
|
|
|
|
atomic_set(&wsm->total_ws, 0);
|
|
|
|
wsm->free_ws = 0;
|
|
|
|
init_waitqueue_head(&wsm->ws_wait);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_initial_workspace(struct workspace_manager *wsm,
|
|
|
|
struct crush_work *work)
|
|
|
|
{
|
|
|
|
WARN_ON(!list_empty(&wsm->idle_ws));
|
|
|
|
|
|
|
|
list_add(&work->item, &wsm->idle_ws);
|
|
|
|
atomic_set(&wsm->total_ws, 1);
|
|
|
|
wsm->free_ws = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cleanup_workspace_manager(struct workspace_manager *wsm)
|
|
|
|
{
|
|
|
|
struct crush_work *work;
|
|
|
|
|
|
|
|
while (!list_empty(&wsm->idle_ws)) {
|
|
|
|
work = list_first_entry(&wsm->idle_ws, struct crush_work,
|
|
|
|
item);
|
|
|
|
list_del_init(&work->item);
|
|
|
|
free_workspace(work);
|
|
|
|
}
|
|
|
|
atomic_set(&wsm->total_ws, 0);
|
|
|
|
wsm->free_ws = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Finds an available workspace or allocates a new one. If it's not
|
|
|
|
* possible to allocate a new one, waits until there is one.
|
|
|
|
*/
|
|
|
|
static struct crush_work *get_workspace(struct workspace_manager *wsm,
|
|
|
|
const struct crush_map *c)
|
|
|
|
{
|
|
|
|
struct crush_work *work;
|
|
|
|
int cpus = num_online_cpus();
|
|
|
|
|
|
|
|
again:
|
|
|
|
spin_lock(&wsm->ws_lock);
|
|
|
|
if (!list_empty(&wsm->idle_ws)) {
|
|
|
|
work = list_first_entry(&wsm->idle_ws, struct crush_work,
|
|
|
|
item);
|
|
|
|
list_del_init(&work->item);
|
|
|
|
wsm->free_ws--;
|
|
|
|
spin_unlock(&wsm->ws_lock);
|
|
|
|
return work;
|
|
|
|
|
|
|
|
}
|
|
|
|
if (atomic_read(&wsm->total_ws) > cpus) {
|
|
|
|
DEFINE_WAIT(wait);
|
|
|
|
|
|
|
|
spin_unlock(&wsm->ws_lock);
|
|
|
|
prepare_to_wait(&wsm->ws_wait, &wait, TASK_UNINTERRUPTIBLE);
|
|
|
|
if (atomic_read(&wsm->total_ws) > cpus && !wsm->free_ws)
|
|
|
|
schedule();
|
|
|
|
finish_wait(&wsm->ws_wait, &wait);
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
atomic_inc(&wsm->total_ws);
|
|
|
|
spin_unlock(&wsm->ws_lock);
|
|
|
|
|
|
|
|
work = alloc_workspace(c);
|
|
|
|
if (!work) {
|
|
|
|
atomic_dec(&wsm->total_ws);
|
|
|
|
wake_up(&wsm->ws_wait);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do not return the error but go back to waiting. We
|
2021-03-25 06:38:21 +00:00
|
|
|
* have the initial workspace and the CRUSH computation
|
2020-08-17 11:45:04 +00:00
|
|
|
* time is bounded so we will get it eventually.
|
|
|
|
*/
|
|
|
|
WARN_ON(atomic_read(&wsm->total_ws) < 1);
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
return work;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Puts a workspace back on the list or frees it if we have enough
|
|
|
|
* idle ones sitting around.
|
|
|
|
*/
|
|
|
|
static void put_workspace(struct workspace_manager *wsm,
|
|
|
|
struct crush_work *work)
|
|
|
|
{
|
|
|
|
spin_lock(&wsm->ws_lock);
|
|
|
|
if (wsm->free_ws <= num_online_cpus()) {
|
|
|
|
list_add(&work->item, &wsm->idle_ws);
|
|
|
|
wsm->free_ws++;
|
|
|
|
spin_unlock(&wsm->ws_lock);
|
|
|
|
goto wake;
|
|
|
|
}
|
|
|
|
spin_unlock(&wsm->ws_lock);
|
|
|
|
|
|
|
|
free_workspace(work);
|
|
|
|
atomic_dec(&wsm->total_ws);
|
|
|
|
wake:
|
|
|
|
if (wq_has_sleeper(&wsm->ws_wait))
|
|
|
|
wake_up(&wsm->ws_wait);
|
|
|
|
}
|
|
|
|
|
2010-04-09 22:46:42 +00:00
|
|
|
/*
|
|
|
|
* osd map
|
|
|
|
*/
|
2016-04-28 14:07:25 +00:00
|
|
|
struct ceph_osdmap *ceph_osdmap_alloc(void)
|
|
|
|
{
|
|
|
|
struct ceph_osdmap *map;
|
|
|
|
|
|
|
|
map = kzalloc(sizeof(*map), GFP_NOIO);
|
|
|
|
if (!map)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
map->pg_pools = RB_ROOT;
|
|
|
|
map->pool_max = -1;
|
|
|
|
map->pg_temp = RB_ROOT;
|
|
|
|
map->primary_temp = RB_ROOT;
|
2017-06-21 15:27:18 +00:00
|
|
|
map->pg_upmap = RB_ROOT;
|
|
|
|
map->pg_upmap_items = RB_ROOT;
|
2020-08-17 11:45:04 +00:00
|
|
|
|
|
|
|
init_workspace_manager(&map->crush_wsm);
|
2016-04-28 14:07:25 +00:00
|
|
|
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
2010-04-09 22:46:42 +00:00
|
|
|
void ceph_osdmap_destroy(struct ceph_osdmap *map)
|
|
|
|
{
|
|
|
|
dout("osdmap_destroy %p\n", map);
|
2020-08-17 11:45:04 +00:00
|
|
|
|
2010-04-09 22:46:42 +00:00
|
|
|
if (map->crush)
|
|
|
|
crush_destroy(map->crush);
|
2020-08-17 11:45:04 +00:00
|
|
|
cleanup_workspace_manager(&map->crush_wsm);
|
|
|
|
|
2010-04-09 22:46:42 +00:00
|
|
|
while (!RB_EMPTY_ROOT(&map->pg_temp)) {
|
|
|
|
struct ceph_pg_mapping *pg =
|
|
|
|
rb_entry(rb_first(&map->pg_temp),
|
|
|
|
struct ceph_pg_mapping, node);
|
2017-06-21 15:27:17 +00:00
|
|
|
erase_pg_mapping(&map->pg_temp, pg);
|
|
|
|
free_pg_mapping(pg);
|
2010-04-09 22:46:42 +00:00
|
|
|
}
|
2014-03-21 17:05:29 +00:00
|
|
|
while (!RB_EMPTY_ROOT(&map->primary_temp)) {
|
|
|
|
struct ceph_pg_mapping *pg =
|
|
|
|
rb_entry(rb_first(&map->primary_temp),
|
|
|
|
struct ceph_pg_mapping, node);
|
2017-06-21 15:27:17 +00:00
|
|
|
erase_pg_mapping(&map->primary_temp, pg);
|
|
|
|
free_pg_mapping(pg);
|
2014-03-21 17:05:29 +00:00
|
|
|
}
|
2017-06-21 15:27:18 +00:00
|
|
|
while (!RB_EMPTY_ROOT(&map->pg_upmap)) {
|
|
|
|
struct ceph_pg_mapping *pg =
|
|
|
|
rb_entry(rb_first(&map->pg_upmap),
|
|
|
|
struct ceph_pg_mapping, node);
|
|
|
|
rb_erase(&pg->node, &map->pg_upmap);
|
|
|
|
kfree(pg);
|
|
|
|
}
|
|
|
|
while (!RB_EMPTY_ROOT(&map->pg_upmap_items)) {
|
|
|
|
struct ceph_pg_mapping *pg =
|
|
|
|
rb_entry(rb_first(&map->pg_upmap_items),
|
|
|
|
struct ceph_pg_mapping, node);
|
|
|
|
rb_erase(&pg->node, &map->pg_upmap_items);
|
|
|
|
kfree(pg);
|
|
|
|
}
|
2010-04-09 22:46:42 +00:00
|
|
|
while (!RB_EMPTY_ROOT(&map->pg_pools)) {
|
|
|
|
struct ceph_pg_pool_info *pi =
|
|
|
|
rb_entry(rb_first(&map->pg_pools),
|
|
|
|
struct ceph_pg_pool_info, node);
|
|
|
|
__remove_pg_pool(&map->pg_pools, pi);
|
|
|
|
}
|
2019-09-04 13:40:03 +00:00
|
|
|
kvfree(map->osd_state);
|
|
|
|
kvfree(map->osd_weight);
|
|
|
|
kvfree(map->osd_addr);
|
|
|
|
kvfree(map->osd_primary_affinity);
|
2010-04-09 22:46:42 +00:00
|
|
|
kfree(map);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-03-21 17:05:28 +00:00
|
|
|
* Adjust max_osd value, (re)allocate arrays.
|
|
|
|
*
|
|
|
|
* The new elements are properly initialized.
|
2010-04-09 22:46:42 +00:00
|
|
|
*/
|
2019-09-04 13:40:03 +00:00
|
|
|
static int osdmap_set_max_osd(struct ceph_osdmap *map, u32 max)
|
2010-04-09 22:46:42 +00:00
|
|
|
{
|
2017-06-22 17:44:06 +00:00
|
|
|
u32 *state;
|
2010-04-09 22:46:42 +00:00
|
|
|
u32 *weight;
|
2014-03-21 17:05:28 +00:00
|
|
|
struct ceph_entity_addr *addr;
|
2019-09-04 13:40:03 +00:00
|
|
|
u32 to_copy;
|
2014-03-21 17:05:28 +00:00
|
|
|
int i;
|
2010-04-09 22:46:42 +00:00
|
|
|
|
2019-09-04 13:40:03 +00:00
|
|
|
dout("%s old %u new %u\n", __func__, map->max_osd, max);
|
|
|
|
if (max == map->max_osd)
|
|
|
|
return 0;
|
2014-09-07 10:10:51 +00:00
|
|
|
|
2019-09-04 13:40:03 +00:00
|
|
|
state = ceph_kvmalloc(array_size(max, sizeof(*state)), GFP_NOFS);
|
|
|
|
weight = ceph_kvmalloc(array_size(max, sizeof(*weight)), GFP_NOFS);
|
|
|
|
addr = ceph_kvmalloc(array_size(max, sizeof(*addr)), GFP_NOFS);
|
|
|
|
if (!state || !weight || !addr) {
|
|
|
|
kvfree(state);
|
|
|
|
kvfree(weight);
|
|
|
|
kvfree(addr);
|
2014-09-07 10:10:51 +00:00
|
|
|
return -ENOMEM;
|
2019-09-04 13:40:03 +00:00
|
|
|
}
|
2014-03-21 17:05:28 +00:00
|
|
|
|
2019-09-04 13:40:03 +00:00
|
|
|
to_copy = min(map->max_osd, max);
|
|
|
|
if (map->osd_state) {
|
|
|
|
memcpy(state, map->osd_state, to_copy * sizeof(*state));
|
|
|
|
memcpy(weight, map->osd_weight, to_copy * sizeof(*weight));
|
|
|
|
memcpy(addr, map->osd_addr, to_copy * sizeof(*addr));
|
|
|
|
kvfree(map->osd_state);
|
|
|
|
kvfree(map->osd_weight);
|
|
|
|
kvfree(map->osd_addr);
|
|
|
|
}
|
2010-04-09 22:46:42 +00:00
|
|
|
|
2019-09-04 13:40:03 +00:00
|
|
|
map->osd_state = state;
|
|
|
|
map->osd_weight = weight;
|
|
|
|
map->osd_addr = addr;
|
2014-03-21 17:05:28 +00:00
|
|
|
for (i = map->max_osd; i < max; i++) {
|
2014-09-07 10:10:51 +00:00
|
|
|
map->osd_state[i] = 0;
|
|
|
|
map->osd_weight[i] = CEPH_OSD_OUT;
|
|
|
|
memset(map->osd_addr + i, 0, sizeof(*map->osd_addr));
|
2010-04-09 22:46:42 +00:00
|
|
|
}
|
|
|
|
|
2014-03-21 17:05:30 +00:00
|
|
|
if (map->osd_primary_affinity) {
|
|
|
|
u32 *affinity;
|
|
|
|
|
2019-09-04 13:40:03 +00:00
|
|
|
affinity = ceph_kvmalloc(array_size(max, sizeof(*affinity)),
|
|
|
|
GFP_NOFS);
|
2014-03-21 17:05:30 +00:00
|
|
|
if (!affinity)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2019-09-04 13:40:03 +00:00
|
|
|
memcpy(affinity, map->osd_primary_affinity,
|
|
|
|
to_copy * sizeof(*affinity));
|
|
|
|
kvfree(map->osd_primary_affinity);
|
|
|
|
|
|
|
|
map->osd_primary_affinity = affinity;
|
2014-03-21 17:05:30 +00:00
|
|
|
for (i = map->max_osd; i < max; i++)
|
2014-09-07 10:10:51 +00:00
|
|
|
map->osd_primary_affinity[i] =
|
|
|
|
CEPH_OSD_DEFAULT_PRIMARY_AFFINITY;
|
2014-03-21 17:05:30 +00:00
|
|
|
}
|
|
|
|
|
2010-04-09 22:46:42 +00:00
|
|
|
map->max_osd = max;
|
2014-03-21 17:05:28 +00:00
|
|
|
|
2010-04-09 22:46:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-01-31 14:55:06 +00:00
|
|
|
static int osdmap_set_crush(struct ceph_osdmap *map, struct crush_map *crush)
|
|
|
|
{
|
2020-08-17 11:45:04 +00:00
|
|
|
struct crush_work *work;
|
2017-01-31 14:55:06 +00:00
|
|
|
|
2017-01-31 14:55:06 +00:00
|
|
|
if (IS_ERR(crush))
|
|
|
|
return PTR_ERR(crush);
|
|
|
|
|
2020-08-17 11:45:04 +00:00
|
|
|
work = alloc_workspace(crush);
|
|
|
|
if (!work) {
|
2017-01-31 14:55:06 +00:00
|
|
|
crush_destroy(crush);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2017-01-31 14:55:06 +00:00
|
|
|
if (map->crush)
|
|
|
|
crush_destroy(map->crush);
|
2020-08-17 11:45:04 +00:00
|
|
|
cleanup_workspace_manager(&map->crush_wsm);
|
2017-01-31 14:55:06 +00:00
|
|
|
map->crush = crush;
|
2020-08-17 11:45:04 +00:00
|
|
|
add_initial_workspace(&map->crush_wsm, work);
|
2017-01-31 14:55:06 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-21 17:05:29 +00:00
|
|
|
#define OSDMAP_WRAPPER_COMPAT_VER 7
|
|
|
|
#define OSDMAP_CLIENT_DATA_COMPAT_VER 1
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return 0 or error. On success, *v is set to 0 for old (v6) osdmaps,
|
|
|
|
* to struct_v of the client_data section for new (v7 and above)
|
|
|
|
* osdmaps.
|
|
|
|
*/
|
|
|
|
static int get_osdmap_client_data_v(void **p, void *end,
|
|
|
|
const char *prefix, u8 *v)
|
|
|
|
{
|
|
|
|
u8 struct_v;
|
|
|
|
|
|
|
|
ceph_decode_8_safe(p, end, struct_v, e_inval);
|
|
|
|
if (struct_v >= 7) {
|
|
|
|
u8 struct_compat;
|
|
|
|
|
|
|
|
ceph_decode_8_safe(p, end, struct_compat, e_inval);
|
|
|
|
if (struct_compat > OSDMAP_WRAPPER_COMPAT_VER) {
|
2014-09-10 04:17:29 +00:00
|
|
|
pr_warn("got v %d cv %d > %d of %s ceph_osdmap\n",
|
|
|
|
struct_v, struct_compat,
|
|
|
|
OSDMAP_WRAPPER_COMPAT_VER, prefix);
|
2014-03-21 17:05:29 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
*p += 4; /* ignore wrapper struct_len */
|
|
|
|
|
|
|
|
ceph_decode_8_safe(p, end, struct_v, e_inval);
|
|
|
|
ceph_decode_8_safe(p, end, struct_compat, e_inval);
|
|
|
|
if (struct_compat > OSDMAP_CLIENT_DATA_COMPAT_VER) {
|
2014-09-10 04:17:29 +00:00
|
|
|
pr_warn("got v %d cv %d > %d of %s ceph_osdmap client data\n",
|
|
|
|
struct_v, struct_compat,
|
|
|
|
OSDMAP_CLIENT_DATA_COMPAT_VER, prefix);
|
2014-03-21 17:05:29 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
*p += 4; /* ignore client data struct_len */
|
|
|
|
} else {
|
|
|
|
u16 version;
|
|
|
|
|
|
|
|
*p -= 1;
|
|
|
|
ceph_decode_16_safe(p, end, version, e_inval);
|
|
|
|
if (version < 6) {
|
2014-09-10 04:17:29 +00:00
|
|
|
pr_warn("got v %d < 6 of %s ceph_osdmap\n",
|
|
|
|
version, prefix);
|
2014-03-21 17:05:29 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2021-06-02 06:56:35 +00:00
|
|
|
/* old osdmap encoding */
|
2014-03-21 17:05:29 +00:00
|
|
|
struct_v = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
*v = struct_v;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
e_inval:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2014-03-21 17:05:27 +00:00
|
|
|
static int __decode_pools(void **p, void *end, struct ceph_osdmap *map,
|
|
|
|
bool incremental)
|
|
|
|
{
|
|
|
|
u32 n;
|
|
|
|
|
|
|
|
ceph_decode_32_safe(p, end, n, e_inval);
|
|
|
|
while (n--) {
|
|
|
|
struct ceph_pg_pool_info *pi;
|
|
|
|
u64 pool;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ceph_decode_64_safe(p, end, pool, e_inval);
|
|
|
|
|
2020-05-19 14:46:47 +00:00
|
|
|
pi = lookup_pg_pool(&map->pg_pools, pool);
|
2014-03-21 17:05:27 +00:00
|
|
|
if (!incremental || !pi) {
|
|
|
|
pi = kzalloc(sizeof(*pi), GFP_NOFS);
|
|
|
|
if (!pi)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2020-05-19 14:46:47 +00:00
|
|
|
RB_CLEAR_NODE(&pi->node);
|
2014-03-21 17:05:27 +00:00
|
|
|
pi->id = pool;
|
|
|
|
|
2020-05-19 14:46:47 +00:00
|
|
|
if (!__insert_pg_pool(&map->pg_pools, pi)) {
|
2014-03-21 17:05:27 +00:00
|
|
|
kfree(pi);
|
2020-05-19 14:46:47 +00:00
|
|
|
return -EEXIST;
|
2014-03-21 17:05:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = decode_pool(p, end, pi);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
e_inval:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int decode_pools(void **p, void *end, struct ceph_osdmap *map)
|
|
|
|
{
|
|
|
|
return __decode_pools(p, end, map, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int decode_new_pools(void **p, void *end, struct ceph_osdmap *map)
|
|
|
|
{
|
|
|
|
return __decode_pools(p, end, map, true);
|
|
|
|
}
|
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
typedef struct ceph_pg_mapping *(*decode_mapping_fn_t)(void **, void *, bool);
|
|
|
|
|
|
|
|
static int decode_pg_mapping(void **p, void *end, struct rb_root *mapping_root,
|
|
|
|
decode_mapping_fn_t fn, bool incremental)
|
2014-03-21 17:05:28 +00:00
|
|
|
{
|
|
|
|
u32 n;
|
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
WARN_ON(!incremental && !fn);
|
|
|
|
|
2014-03-21 17:05:28 +00:00
|
|
|
ceph_decode_32_safe(p, end, n, e_inval);
|
|
|
|
while (n--) {
|
2017-06-21 15:27:17 +00:00
|
|
|
struct ceph_pg_mapping *pg;
|
2014-03-21 17:05:28 +00:00
|
|
|
struct ceph_pg pgid;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ceph_decode_pgid(p, end, &pgid);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
pg = lookup_pg_mapping(mapping_root, &pgid);
|
|
|
|
if (pg) {
|
|
|
|
WARN_ON(!incremental);
|
|
|
|
erase_pg_mapping(mapping_root, pg);
|
|
|
|
free_pg_mapping(pg);
|
|
|
|
}
|
2014-03-21 17:05:28 +00:00
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
if (fn) {
|
|
|
|
pg = fn(p, end, incremental);
|
|
|
|
if (IS_ERR(pg))
|
|
|
|
return PTR_ERR(pg);
|
2014-03-21 17:05:28 +00:00
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
if (pg) {
|
|
|
|
pg->pgid = pgid; /* struct */
|
2017-06-21 15:27:17 +00:00
|
|
|
insert_pg_mapping(mapping_root, pg);
|
2014-03-21 17:05:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
e_inval:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
static struct ceph_pg_mapping *__decode_pg_temp(void **p, void *end,
|
|
|
|
bool incremental)
|
2014-03-21 17:05:28 +00:00
|
|
|
{
|
2017-06-21 15:27:17 +00:00
|
|
|
struct ceph_pg_mapping *pg;
|
|
|
|
u32 len, i;
|
2014-03-21 17:05:28 +00:00
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
ceph_decode_32_safe(p, end, len, e_inval);
|
|
|
|
if (len == 0 && incremental)
|
|
|
|
return NULL; /* new_pg_temp: [] to remove */
|
|
|
|
if (len > (SIZE_MAX - sizeof(*pg)) / sizeof(u32))
|
|
|
|
return ERR_PTR(-EINVAL);
|
2014-03-21 17:05:28 +00:00
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
ceph_decode_need(p, end, len * sizeof(u32), e_inval);
|
|
|
|
pg = alloc_pg_mapping(len * sizeof(u32));
|
|
|
|
if (!pg)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
2014-03-21 17:05:30 +00:00
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
pg->pg_temp.len = len;
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
pg->pg_temp.osds[i] = ceph_decode_32(p);
|
2014-03-21 17:05:30 +00:00
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
return pg;
|
2014-03-21 17:05:30 +00:00
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
e_inval:
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
}
|
2014-03-21 17:05:30 +00:00
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
static int decode_pg_temp(void **p, void *end, struct ceph_osdmap *map)
|
|
|
|
{
|
|
|
|
return decode_pg_mapping(p, end, &map->pg_temp, __decode_pg_temp,
|
|
|
|
false);
|
|
|
|
}
|
2014-03-21 17:05:30 +00:00
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
static int decode_new_pg_temp(void **p, void *end, struct ceph_osdmap *map)
|
|
|
|
{
|
|
|
|
return decode_pg_mapping(p, end, &map->pg_temp, __decode_pg_temp,
|
|
|
|
true);
|
|
|
|
}
|
2014-03-21 17:05:30 +00:00
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
static struct ceph_pg_mapping *__decode_primary_temp(void **p, void *end,
|
|
|
|
bool incremental)
|
|
|
|
{
|
|
|
|
struct ceph_pg_mapping *pg;
|
|
|
|
u32 osd;
|
2014-03-21 17:05:30 +00:00
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
ceph_decode_32_safe(p, end, osd, e_inval);
|
|
|
|
if (osd == (u32)-1 && incremental)
|
|
|
|
return NULL; /* new_primary_temp: -1 to remove */
|
2014-03-21 17:05:30 +00:00
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
pg = alloc_pg_mapping(0);
|
|
|
|
if (!pg)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
2014-03-21 17:05:30 +00:00
|
|
|
|
2017-06-21 15:27:17 +00:00
|
|
|
pg->primary_temp.osd = osd;
|
|
|
|
return pg;
|
2014-03-21 17:05:30 +00:00
|
|
|
|
|
|
|
e_inval:
|
2017-06-21 15:27:17 +00:00
|
|
|
return ERR_PTR(-EINVAL);
|
2014-03-21 17:05:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int decode_primary_temp(void **p, void *end, struct ceph_osdmap *map)
|
|
|
|
{
|
2017-06-21 15:27:17 +00:00
|
|
|
return decode_pg_mapping(p, end, &map->primary_temp,
|
|
|
|
__decode_primary_temp, false);
|
2014-03-21 17:05:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int decode_new_primary_temp(void **p, void *end,
|
|
|
|
struct ceph_osdmap *map)
|
|
|
|
{
|
2017-06-21 15:27:17 +00:00
|
|
|
return decode_pg_mapping(p, end, &map->primary_temp,
|
|
|
|
__decode_primary_temp, true);
|
2014-03-21 17:05:30 +00:00
|
|
|
}
|
|
|
|
|
2014-03-21 17:05:30 +00:00
|
|
|
u32 ceph_get_primary_affinity(struct ceph_osdmap *map, int osd)
|
|
|
|
{
|
|
|
|
BUG_ON(osd >= map->max_osd);
|
|
|
|
|
|
|
|
if (!map->osd_primary_affinity)
|
|
|
|
return CEPH_OSD_DEFAULT_PRIMARY_AFFINITY;
|
|
|
|
|
|
|
|
return map->osd_primary_affinity[osd];
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_primary_affinity(struct ceph_osdmap *map, int osd, u32 aff)
|
|
|
|
{
|
|
|
|
BUG_ON(osd >= map->max_osd);
|
|
|
|
|
|
|
|
if (!map->osd_primary_affinity) {
|
|
|
|
int i;
|
|
|
|
|
2019-09-04 13:40:03 +00:00
|
|
|
map->osd_primary_affinity = ceph_kvmalloc(
|
|
|
|
array_size(map->max_osd, sizeof(*map->osd_primary_affinity)),
|
|
|
|
GFP_NOFS);
|
2014-03-21 17:05:30 +00:00
|
|
|
if (!map->osd_primary_affinity)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
for (i = 0; i < map->max_osd; i++)
|
|
|
|
map->osd_primary_affinity[i] =
|
|
|
|
CEPH_OSD_DEFAULT_PRIMARY_AFFINITY;
|
|
|
|
}
|
|
|
|
|
|
|
|
map->osd_primary_affinity[osd] = aff;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-21 17:05:30 +00:00
|
|
|
static int decode_primary_affinity(void **p, void *end,
|
|
|
|
struct ceph_osdmap *map)
|
|
|
|
{
|
|
|
|
u32 len, i;
|
|
|
|
|
|
|
|
ceph_decode_32_safe(p, end, len, e_inval);
|
|
|
|
if (len == 0) {
|
2019-09-04 13:40:03 +00:00
|
|
|
kvfree(map->osd_primary_affinity);
|
2014-03-21 17:05:30 +00:00
|
|
|
map->osd_primary_affinity = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (len != map->max_osd)
|
|
|
|
goto e_inval;
|
|
|
|
|
|
|
|
ceph_decode_need(p, end, map->max_osd*sizeof(u32), e_inval);
|
|
|
|
|
|
|
|
for (i = 0; i < map->max_osd; i++) {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = set_primary_affinity(map, i, ceph_decode_32(p));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
e_inval:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int decode_new_primary_affinity(void **p, void *end,
|
|
|
|
struct ceph_osdmap *map)
|
|
|
|
{
|
|
|
|
u32 n;
|
|
|
|
|
|
|
|
ceph_decode_32_safe(p, end, n, e_inval);
|
|
|
|
while (n--) {
|
|
|
|
u32 osd, aff;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ceph_decode_32_safe(p, end, osd, e_inval);
|
|
|
|
ceph_decode_32_safe(p, end, aff, e_inval);
|
|
|
|
|
|
|
|
ret = set_primary_affinity(map, osd, aff);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2014-04-02 16:34:04 +00:00
|
|
|
|
|
|
|
pr_info("osd%d primary-affinity 0x%x\n", osd, aff);
|
2014-03-21 17:05:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
e_inval:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-06-21 15:27:18 +00:00
|
|
|
static struct ceph_pg_mapping *__decode_pg_upmap(void **p, void *end,
|
|
|
|
bool __unused)
|
|
|
|
{
|
|
|
|
return __decode_pg_temp(p, end, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int decode_pg_upmap(void **p, void *end, struct ceph_osdmap *map)
|
|
|
|
{
|
|
|
|
return decode_pg_mapping(p, end, &map->pg_upmap, __decode_pg_upmap,
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int decode_new_pg_upmap(void **p, void *end, struct ceph_osdmap *map)
|
|
|
|
{
|
|
|
|
return decode_pg_mapping(p, end, &map->pg_upmap, __decode_pg_upmap,
|
|
|
|
true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int decode_old_pg_upmap(void **p, void *end, struct ceph_osdmap *map)
|
|
|
|
{
|
|
|
|
return decode_pg_mapping(p, end, &map->pg_upmap, NULL, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ceph_pg_mapping *__decode_pg_upmap_items(void **p, void *end,
|
|
|
|
bool __unused)
|
|
|
|
{
|
|
|
|
struct ceph_pg_mapping *pg;
|
|
|
|
u32 len, i;
|
|
|
|
|
|
|
|
ceph_decode_32_safe(p, end, len, e_inval);
|
|
|
|
if (len > (SIZE_MAX - sizeof(*pg)) / (2 * sizeof(u32)))
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
|
|
ceph_decode_need(p, end, 2 * len * sizeof(u32), e_inval);
|
2017-07-07 14:14:45 +00:00
|
|
|
pg = alloc_pg_mapping(2 * len * sizeof(u32));
|
2017-06-21 15:27:18 +00:00
|
|
|
if (!pg)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
pg->pg_upmap_items.len = len;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
pg->pg_upmap_items.from_to[i][0] = ceph_decode_32(p);
|
|
|
|
pg->pg_upmap_items.from_to[i][1] = ceph_decode_32(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
return pg;
|
|
|
|
|
|
|
|
e_inval:
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int decode_pg_upmap_items(void **p, void *end, struct ceph_osdmap *map)
|
|
|
|
{
|
|
|
|
return decode_pg_mapping(p, end, &map->pg_upmap_items,
|
|
|
|
__decode_pg_upmap_items, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int decode_new_pg_upmap_items(void **p, void *end,
|
|
|
|
struct ceph_osdmap *map)
|
|
|
|
{
|
|
|
|
return decode_pg_mapping(p, end, &map->pg_upmap_items,
|
|
|
|
__decode_pg_upmap_items, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int decode_old_pg_upmap_items(void **p, void *end,
|
|
|
|
struct ceph_osdmap *map)
|
|
|
|
{
|
|
|
|
return decode_pg_mapping(p, end, &map->pg_upmap_items, NULL, true);
|
|
|
|
}
|
|
|
|
|
2009-10-06 18:31:10 +00:00
|
|
|
/*
|
|
|
|
* decode a full map.
|
|
|
|
*/
|
2020-10-30 12:30:51 +00:00
|
|
|
static int osdmap_decode(void **p, void *end, bool msgr2,
|
|
|
|
struct ceph_osdmap *map)
|
2009-10-06 18:31:10 +00:00
|
|
|
{
|
2014-03-21 17:05:29 +00:00
|
|
|
u8 struct_v;
|
2014-03-13 14:36:13 +00:00
|
|
|
u32 epoch = 0;
|
2009-10-06 18:31:10 +00:00
|
|
|
void *start = *p;
|
2014-03-13 14:36:14 +00:00
|
|
|
u32 max;
|
|
|
|
u32 len, i;
|
2014-03-13 14:36:14 +00:00
|
|
|
int err;
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2014-03-13 14:36:13 +00:00
|
|
|
dout("%s %p to %p len %d\n", __func__, *p, end, (int)(end - *p));
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2014-03-21 17:05:29 +00:00
|
|
|
err = get_osdmap_client_data_v(p, end, "full", &struct_v);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2014-03-13 14:36:16 +00:00
|
|
|
/* fsid, epoch, created, modified */
|
|
|
|
ceph_decode_need(p, end, sizeof(map->fsid) + sizeof(u32) +
|
|
|
|
sizeof(map->created) + sizeof(map->modified), e_inval);
|
2009-10-06 18:31:10 +00:00
|
|
|
ceph_decode_copy(p, &map->fsid, sizeof(map->fsid));
|
2014-03-13 14:36:13 +00:00
|
|
|
epoch = map->epoch = ceph_decode_32(p);
|
2009-10-06 18:31:10 +00:00
|
|
|
ceph_decode_copy(p, &map->created, sizeof(map->created));
|
|
|
|
ceph_decode_copy(p, &map->modified, sizeof(map->modified));
|
|
|
|
|
2014-03-21 17:05:27 +00:00
|
|
|
/* pools */
|
|
|
|
err = decode_pools(p, end, map);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
2010-04-09 22:46:42 +00:00
|
|
|
|
2014-03-21 17:05:27 +00:00
|
|
|
/* pool_name */
|
|
|
|
err = decode_pool_names(p, end, map);
|
2014-03-13 14:36:14 +00:00
|
|
|
if (err)
|
2013-02-23 18:41:09 +00:00
|
|
|
goto bad;
|
2010-04-09 22:46:42 +00:00
|
|
|
|
2014-03-13 14:36:14 +00:00
|
|
|
ceph_decode_32_safe(p, end, map->pool_max, e_inval);
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2014-03-13 14:36:14 +00:00
|
|
|
ceph_decode_32_safe(p, end, map->flags, e_inval);
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2014-03-13 14:36:14 +00:00
|
|
|
/* max_osd */
|
|
|
|
ceph_decode_32_safe(p, end, max, e_inval);
|
2009-10-06 18:31:10 +00:00
|
|
|
|
|
|
|
/* (re)alloc osd arrays */
|
|
|
|
err = osdmap_set_max_osd(map, max);
|
2014-03-13 14:36:14 +00:00
|
|
|
if (err)
|
2009-10-06 18:31:10 +00:00
|
|
|
goto bad;
|
|
|
|
|
2014-03-13 14:36:14 +00:00
|
|
|
/* osd_state, osd_weight, osd_addrs->client_addr */
|
2009-10-06 18:31:10 +00:00
|
|
|
ceph_decode_need(p, end, 3*sizeof(u32) +
|
2019-06-03 19:08:13 +00:00
|
|
|
map->max_osd*(struct_v >= 5 ? sizeof(u32) :
|
|
|
|
sizeof(u8)) +
|
|
|
|
sizeof(*map->osd_weight), e_inval);
|
2014-03-13 14:36:14 +00:00
|
|
|
if (ceph_decode_32(p) != map->max_osd)
|
|
|
|
goto e_inval;
|
|
|
|
|
2017-06-22 17:44:06 +00:00
|
|
|
if (struct_v >= 5) {
|
|
|
|
for (i = 0; i < map->max_osd; i++)
|
|
|
|
map->osd_state[i] = ceph_decode_32(p);
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < map->max_osd; i++)
|
|
|
|
map->osd_state[i] = ceph_decode_8(p);
|
|
|
|
}
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2014-03-13 14:36:14 +00:00
|
|
|
if (ceph_decode_32(p) != map->max_osd)
|
|
|
|
goto e_inval;
|
|
|
|
|
2009-10-06 18:31:10 +00:00
|
|
|
for (i = 0; i < map->max_osd; i++)
|
2009-10-14 16:59:09 +00:00
|
|
|
map->osd_weight[i] = ceph_decode_32(p);
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2014-03-13 14:36:14 +00:00
|
|
|
if (ceph_decode_32(p) != map->max_osd)
|
|
|
|
goto e_inval;
|
|
|
|
|
2019-06-03 19:08:13 +00:00
|
|
|
for (i = 0; i < map->max_osd; i++) {
|
2020-10-30 12:30:51 +00:00
|
|
|
struct ceph_entity_addr *addr = &map->osd_addr[i];
|
|
|
|
|
|
|
|
if (struct_v >= 8)
|
|
|
|
err = ceph_decode_entity_addrvec(p, end, msgr2, addr);
|
|
|
|
else
|
|
|
|
err = ceph_decode_entity_addr(p, end, addr);
|
2019-06-03 19:08:13 +00:00
|
|
|
if (err)
|
|
|
|
goto bad;
|
2020-10-30 12:30:51 +00:00
|
|
|
|
|
|
|
dout("%s osd%d addr %s\n", __func__, i, ceph_pr_addr(addr));
|
2019-06-03 19:08:13 +00:00
|
|
|
}
|
2009-10-06 18:31:10 +00:00
|
|
|
|
|
|
|
/* pg_temp */
|
2014-03-21 17:05:28 +00:00
|
|
|
err = decode_pg_temp(p, end, map);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2014-03-21 17:05:30 +00:00
|
|
|
/* primary_temp */
|
|
|
|
if (struct_v >= 1) {
|
|
|
|
err = decode_primary_temp(p, end, map);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2014-03-21 17:05:30 +00:00
|
|
|
/* primary_affinity */
|
|
|
|
if (struct_v >= 2) {
|
|
|
|
err = decode_primary_affinity(p, end, map);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
|
|
|
} else {
|
2017-06-21 15:27:18 +00:00
|
|
|
WARN_ON(map->osd_primary_affinity);
|
2014-03-21 17:05:30 +00:00
|
|
|
}
|
|
|
|
|
2009-10-06 18:31:10 +00:00
|
|
|
/* crush */
|
2014-03-13 14:36:14 +00:00
|
|
|
ceph_decode_32_safe(p, end, len, e_inval);
|
2017-01-31 14:55:06 +00:00
|
|
|
err = osdmap_set_crush(map, crush_decode(*p, min(*p + len, end)));
|
|
|
|
if (err)
|
2009-10-06 18:31:10 +00:00
|
|
|
goto bad;
|
|
|
|
|
2017-06-21 15:27:18 +00:00
|
|
|
*p += len;
|
|
|
|
if (struct_v >= 3) {
|
|
|
|
/* erasure_code_profiles */
|
|
|
|
ceph_decode_skip_map_of_map(p, end, string, string, string,
|
2017-07-13 07:45:17 +00:00
|
|
|
e_inval);
|
2017-06-21 15:27:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (struct_v >= 4) {
|
|
|
|
err = decode_pg_upmap(p, end, map);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
err = decode_pg_upmap_items(p, end, map);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
|
|
|
} else {
|
|
|
|
WARN_ON(!RB_EMPTY_ROOT(&map->pg_upmap));
|
|
|
|
WARN_ON(!RB_EMPTY_ROOT(&map->pg_upmap_items));
|
|
|
|
}
|
|
|
|
|
2014-03-13 14:36:13 +00:00
|
|
|
/* ignore the rest */
|
2009-10-06 18:31:10 +00:00
|
|
|
*p = end;
|
|
|
|
|
2014-03-13 14:36:13 +00:00
|
|
|
dout("full osdmap epoch %d max_osd %d\n", map->epoch, map->max_osd);
|
2014-03-13 14:36:13 +00:00
|
|
|
return 0;
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2014-03-13 14:36:14 +00:00
|
|
|
e_inval:
|
|
|
|
err = -EINVAL;
|
2009-10-06 18:31:10 +00:00
|
|
|
bad:
|
2014-03-13 14:36:13 +00:00
|
|
|
pr_err("corrupt full osdmap (%d) epoch %d off %d (%p of %p-%p)\n",
|
|
|
|
err, epoch, (int)(*p - start), *p, start, end);
|
|
|
|
print_hex_dump(KERN_DEBUG, "osdmap: ",
|
|
|
|
DUMP_PREFIX_OFFSET, 16, 1,
|
|
|
|
start, end - start, true);
|
2014-03-13 14:36:13 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate and decode a full map.
|
|
|
|
*/
|
2020-10-30 12:30:51 +00:00
|
|
|
struct ceph_osdmap *ceph_osdmap_decode(void **p, void *end, bool msgr2)
|
2014-03-13 14:36:13 +00:00
|
|
|
{
|
|
|
|
struct ceph_osdmap *map;
|
|
|
|
int ret;
|
|
|
|
|
2016-04-28 14:07:25 +00:00
|
|
|
map = ceph_osdmap_alloc();
|
2014-03-13 14:36:13 +00:00
|
|
|
if (!map)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
2020-10-30 12:30:51 +00:00
|
|
|
ret = osdmap_decode(p, end, msgr2, map);
|
2014-03-13 14:36:13 +00:00
|
|
|
if (ret) {
|
|
|
|
ceph_osdmap_destroy(map);
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
return map;
|
2009-10-06 18:31:10 +00:00
|
|
|
}
|
|
|
|
|
libceph: apply new_state before new_up_client on incrementals
Currently, osd_weight and osd_state fields are updated in the encoding
order. This is wrong, because an incremental map may look like e.g.
new_up_client: { osd=6, addr=... } # set osd_state and addr
new_state: { osd=6, xorstate=EXISTS } # clear osd_state
Suppose osd6's current osd_state is EXISTS (i.e. osd6 is down). After
applying new_up_client, osd_state is changed to EXISTS | UP. Carrying
on with the new_state update, we flip EXISTS and leave osd6 in a weird
"!EXISTS but UP" state. A non-existent OSD is considered down by the
mapping code
2087 for (i = 0; i < pg->pg_temp.len; i++) {
2088 if (ceph_osd_is_down(osdmap, pg->pg_temp.osds[i])) {
2089 if (ceph_can_shift_osds(pi))
2090 continue;
2091
2092 temp->osds[temp->size++] = CRUSH_ITEM_NONE;
and so requests get directed to the second OSD in the set instead of
the first, resulting in OSD-side errors like:
[WRN] : client.4239 192.168.122.21:0/2444980242 misdirected client.4239.1:2827 pg 2.5df899f2 to osd.4 not [1,4,6] in e680/680
and hung rbds on the client:
[ 493.566367] rbd: rbd0: write 400000 at 11cc00000 (0)
[ 493.566805] rbd: rbd0: result -6 xferred 400000
[ 493.567011] blk_update_request: I/O error, dev rbd0, sector 9330688
The fix is to decouple application from the decoding and:
- apply new_weight first
- apply new_state before new_up_client
- twiddle osd_state flags if marking in
- clear out some of the state if osd is destroyed
Fixes: http://tracker.ceph.com/issues/14901
Cc: stable@vger.kernel.org # 3.15+: 6dd74e44dc1d: libceph: set 'exists' flag for newly up osd
Cc: stable@vger.kernel.org # 3.15+
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Josh Durgin <jdurgin@redhat.com>
2016-07-19 01:50:28 +00:00
|
|
|
/*
|
|
|
|
* Encoding order is (new_up_client, new_state, new_weight). Need to
|
|
|
|
* apply in the (new_weight, new_state, new_up_client) order, because
|
|
|
|
* an incremental map may look like e.g.
|
|
|
|
*
|
|
|
|
* new_up_client: { osd=6, addr=... } # set osd_state and addr
|
|
|
|
* new_state: { osd=6, xorstate=EXISTS } # clear osd_state
|
|
|
|
*/
|
2017-06-22 17:44:06 +00:00
|
|
|
static int decode_new_up_state_weight(void **p, void *end, u8 struct_v,
|
2020-10-30 12:30:51 +00:00
|
|
|
bool msgr2, struct ceph_osdmap *map)
|
libceph: apply new_state before new_up_client on incrementals
Currently, osd_weight and osd_state fields are updated in the encoding
order. This is wrong, because an incremental map may look like e.g.
new_up_client: { osd=6, addr=... } # set osd_state and addr
new_state: { osd=6, xorstate=EXISTS } # clear osd_state
Suppose osd6's current osd_state is EXISTS (i.e. osd6 is down). After
applying new_up_client, osd_state is changed to EXISTS | UP. Carrying
on with the new_state update, we flip EXISTS and leave osd6 in a weird
"!EXISTS but UP" state. A non-existent OSD is considered down by the
mapping code
2087 for (i = 0; i < pg->pg_temp.len; i++) {
2088 if (ceph_osd_is_down(osdmap, pg->pg_temp.osds[i])) {
2089 if (ceph_can_shift_osds(pi))
2090 continue;
2091
2092 temp->osds[temp->size++] = CRUSH_ITEM_NONE;
and so requests get directed to the second OSD in the set instead of
the first, resulting in OSD-side errors like:
[WRN] : client.4239 192.168.122.21:0/2444980242 misdirected client.4239.1:2827 pg 2.5df899f2 to osd.4 not [1,4,6] in e680/680
and hung rbds on the client:
[ 493.566367] rbd: rbd0: write 400000 at 11cc00000 (0)
[ 493.566805] rbd: rbd0: result -6 xferred 400000
[ 493.567011] blk_update_request: I/O error, dev rbd0, sector 9330688
The fix is to decouple application from the decoding and:
- apply new_weight first
- apply new_state before new_up_client
- twiddle osd_state flags if marking in
- clear out some of the state if osd is destroyed
Fixes: http://tracker.ceph.com/issues/14901
Cc: stable@vger.kernel.org # 3.15+: 6dd74e44dc1d: libceph: set 'exists' flag for newly up osd
Cc: stable@vger.kernel.org # 3.15+
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Josh Durgin <jdurgin@redhat.com>
2016-07-19 01:50:28 +00:00
|
|
|
{
|
|
|
|
void *new_up_client;
|
|
|
|
void *new_state;
|
|
|
|
void *new_weight_end;
|
|
|
|
u32 len;
|
2020-10-30 12:30:51 +00:00
|
|
|
int ret;
|
2019-06-04 19:10:44 +00:00
|
|
|
int i;
|
libceph: apply new_state before new_up_client on incrementals
Currently, osd_weight and osd_state fields are updated in the encoding
order. This is wrong, because an incremental map may look like e.g.
new_up_client: { osd=6, addr=... } # set osd_state and addr
new_state: { osd=6, xorstate=EXISTS } # clear osd_state
Suppose osd6's current osd_state is EXISTS (i.e. osd6 is down). After
applying new_up_client, osd_state is changed to EXISTS | UP. Carrying
on with the new_state update, we flip EXISTS and leave osd6 in a weird
"!EXISTS but UP" state. A non-existent OSD is considered down by the
mapping code
2087 for (i = 0; i < pg->pg_temp.len; i++) {
2088 if (ceph_osd_is_down(osdmap, pg->pg_temp.osds[i])) {
2089 if (ceph_can_shift_osds(pi))
2090 continue;
2091
2092 temp->osds[temp->size++] = CRUSH_ITEM_NONE;
and so requests get directed to the second OSD in the set instead of
the first, resulting in OSD-side errors like:
[WRN] : client.4239 192.168.122.21:0/2444980242 misdirected client.4239.1:2827 pg 2.5df899f2 to osd.4 not [1,4,6] in e680/680
and hung rbds on the client:
[ 493.566367] rbd: rbd0: write 400000 at 11cc00000 (0)
[ 493.566805] rbd: rbd0: result -6 xferred 400000
[ 493.567011] blk_update_request: I/O error, dev rbd0, sector 9330688
The fix is to decouple application from the decoding and:
- apply new_weight first
- apply new_state before new_up_client
- twiddle osd_state flags if marking in
- clear out some of the state if osd is destroyed
Fixes: http://tracker.ceph.com/issues/14901
Cc: stable@vger.kernel.org # 3.15+: 6dd74e44dc1d: libceph: set 'exists' flag for newly up osd
Cc: stable@vger.kernel.org # 3.15+
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Josh Durgin <jdurgin@redhat.com>
2016-07-19 01:50:28 +00:00
|
|
|
|
|
|
|
new_up_client = *p;
|
|
|
|
ceph_decode_32_safe(p, end, len, e_inval);
|
2019-06-04 19:10:44 +00:00
|
|
|
for (i = 0; i < len; ++i) {
|
|
|
|
struct ceph_entity_addr addr;
|
|
|
|
|
|
|
|
ceph_decode_skip_32(p, end, e_inval);
|
2020-10-30 12:30:51 +00:00
|
|
|
if (struct_v >= 7)
|
|
|
|
ret = ceph_decode_entity_addrvec(p, end, msgr2, &addr);
|
|
|
|
else
|
|
|
|
ret = ceph_decode_entity_addr(p, end, &addr);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2019-06-04 19:10:44 +00:00
|
|
|
}
|
libceph: apply new_state before new_up_client on incrementals
Currently, osd_weight and osd_state fields are updated in the encoding
order. This is wrong, because an incremental map may look like e.g.
new_up_client: { osd=6, addr=... } # set osd_state and addr
new_state: { osd=6, xorstate=EXISTS } # clear osd_state
Suppose osd6's current osd_state is EXISTS (i.e. osd6 is down). After
applying new_up_client, osd_state is changed to EXISTS | UP. Carrying
on with the new_state update, we flip EXISTS and leave osd6 in a weird
"!EXISTS but UP" state. A non-existent OSD is considered down by the
mapping code
2087 for (i = 0; i < pg->pg_temp.len; i++) {
2088 if (ceph_osd_is_down(osdmap, pg->pg_temp.osds[i])) {
2089 if (ceph_can_shift_osds(pi))
2090 continue;
2091
2092 temp->osds[temp->size++] = CRUSH_ITEM_NONE;
and so requests get directed to the second OSD in the set instead of
the first, resulting in OSD-side errors like:
[WRN] : client.4239 192.168.122.21:0/2444980242 misdirected client.4239.1:2827 pg 2.5df899f2 to osd.4 not [1,4,6] in e680/680
and hung rbds on the client:
[ 493.566367] rbd: rbd0: write 400000 at 11cc00000 (0)
[ 493.566805] rbd: rbd0: result -6 xferred 400000
[ 493.567011] blk_update_request: I/O error, dev rbd0, sector 9330688
The fix is to decouple application from the decoding and:
- apply new_weight first
- apply new_state before new_up_client
- twiddle osd_state flags if marking in
- clear out some of the state if osd is destroyed
Fixes: http://tracker.ceph.com/issues/14901
Cc: stable@vger.kernel.org # 3.15+: 6dd74e44dc1d: libceph: set 'exists' flag for newly up osd
Cc: stable@vger.kernel.org # 3.15+
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Josh Durgin <jdurgin@redhat.com>
2016-07-19 01:50:28 +00:00
|
|
|
|
|
|
|
new_state = *p;
|
|
|
|
ceph_decode_32_safe(p, end, len, e_inval);
|
2017-06-22 17:44:06 +00:00
|
|
|
len *= sizeof(u32) + (struct_v >= 5 ? sizeof(u32) : sizeof(u8));
|
libceph: apply new_state before new_up_client on incrementals
Currently, osd_weight and osd_state fields are updated in the encoding
order. This is wrong, because an incremental map may look like e.g.
new_up_client: { osd=6, addr=... } # set osd_state and addr
new_state: { osd=6, xorstate=EXISTS } # clear osd_state
Suppose osd6's current osd_state is EXISTS (i.e. osd6 is down). After
applying new_up_client, osd_state is changed to EXISTS | UP. Carrying
on with the new_state update, we flip EXISTS and leave osd6 in a weird
"!EXISTS but UP" state. A non-existent OSD is considered down by the
mapping code
2087 for (i = 0; i < pg->pg_temp.len; i++) {
2088 if (ceph_osd_is_down(osdmap, pg->pg_temp.osds[i])) {
2089 if (ceph_can_shift_osds(pi))
2090 continue;
2091
2092 temp->osds[temp->size++] = CRUSH_ITEM_NONE;
and so requests get directed to the second OSD in the set instead of
the first, resulting in OSD-side errors like:
[WRN] : client.4239 192.168.122.21:0/2444980242 misdirected client.4239.1:2827 pg 2.5df899f2 to osd.4 not [1,4,6] in e680/680
and hung rbds on the client:
[ 493.566367] rbd: rbd0: write 400000 at 11cc00000 (0)
[ 493.566805] rbd: rbd0: result -6 xferred 400000
[ 493.567011] blk_update_request: I/O error, dev rbd0, sector 9330688
The fix is to decouple application from the decoding and:
- apply new_weight first
- apply new_state before new_up_client
- twiddle osd_state flags if marking in
- clear out some of the state if osd is destroyed
Fixes: http://tracker.ceph.com/issues/14901
Cc: stable@vger.kernel.org # 3.15+: 6dd74e44dc1d: libceph: set 'exists' flag for newly up osd
Cc: stable@vger.kernel.org # 3.15+
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Josh Durgin <jdurgin@redhat.com>
2016-07-19 01:50:28 +00:00
|
|
|
ceph_decode_need(p, end, len, e_inval);
|
|
|
|
*p += len;
|
|
|
|
|
|
|
|
/* new_weight */
|
|
|
|
ceph_decode_32_safe(p, end, len, e_inval);
|
|
|
|
while (len--) {
|
|
|
|
s32 osd;
|
|
|
|
u32 w;
|
|
|
|
|
|
|
|
ceph_decode_need(p, end, 2*sizeof(u32), e_inval);
|
|
|
|
osd = ceph_decode_32(p);
|
|
|
|
w = ceph_decode_32(p);
|
|
|
|
BUG_ON(osd >= map->max_osd);
|
|
|
|
pr_info("osd%d weight 0x%x %s\n", osd, w,
|
|
|
|
w == CEPH_OSD_IN ? "(in)" :
|
|
|
|
(w == CEPH_OSD_OUT ? "(out)" : ""));
|
|
|
|
map->osd_weight[osd] = w;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are marking in, set the EXISTS, and clear the
|
|
|
|
* AUTOOUT and NEW bits.
|
|
|
|
*/
|
|
|
|
if (w) {
|
|
|
|
map->osd_state[osd] |= CEPH_OSD_EXISTS;
|
|
|
|
map->osd_state[osd] &= ~(CEPH_OSD_AUTOOUT |
|
|
|
|
CEPH_OSD_NEW);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
new_weight_end = *p;
|
|
|
|
|
|
|
|
/* new_state (up/down) */
|
|
|
|
*p = new_state;
|
|
|
|
len = ceph_decode_32(p);
|
|
|
|
while (len--) {
|
|
|
|
s32 osd;
|
2017-06-22 17:44:06 +00:00
|
|
|
u32 xorstate;
|
libceph: apply new_state before new_up_client on incrementals
Currently, osd_weight and osd_state fields are updated in the encoding
order. This is wrong, because an incremental map may look like e.g.
new_up_client: { osd=6, addr=... } # set osd_state and addr
new_state: { osd=6, xorstate=EXISTS } # clear osd_state
Suppose osd6's current osd_state is EXISTS (i.e. osd6 is down). After
applying new_up_client, osd_state is changed to EXISTS | UP. Carrying
on with the new_state update, we flip EXISTS and leave osd6 in a weird
"!EXISTS but UP" state. A non-existent OSD is considered down by the
mapping code
2087 for (i = 0; i < pg->pg_temp.len; i++) {
2088 if (ceph_osd_is_down(osdmap, pg->pg_temp.osds[i])) {
2089 if (ceph_can_shift_osds(pi))
2090 continue;
2091
2092 temp->osds[temp->size++] = CRUSH_ITEM_NONE;
and so requests get directed to the second OSD in the set instead of
the first, resulting in OSD-side errors like:
[WRN] : client.4239 192.168.122.21:0/2444980242 misdirected client.4239.1:2827 pg 2.5df899f2 to osd.4 not [1,4,6] in e680/680
and hung rbds on the client:
[ 493.566367] rbd: rbd0: write 400000 at 11cc00000 (0)
[ 493.566805] rbd: rbd0: result -6 xferred 400000
[ 493.567011] blk_update_request: I/O error, dev rbd0, sector 9330688
The fix is to decouple application from the decoding and:
- apply new_weight first
- apply new_state before new_up_client
- twiddle osd_state flags if marking in
- clear out some of the state if osd is destroyed
Fixes: http://tracker.ceph.com/issues/14901
Cc: stable@vger.kernel.org # 3.15+: 6dd74e44dc1d: libceph: set 'exists' flag for newly up osd
Cc: stable@vger.kernel.org # 3.15+
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Josh Durgin <jdurgin@redhat.com>
2016-07-19 01:50:28 +00:00
|
|
|
|
|
|
|
osd = ceph_decode_32(p);
|
2017-06-22 17:44:06 +00:00
|
|
|
if (struct_v >= 5)
|
|
|
|
xorstate = ceph_decode_32(p);
|
|
|
|
else
|
|
|
|
xorstate = ceph_decode_8(p);
|
libceph: apply new_state before new_up_client on incrementals
Currently, osd_weight and osd_state fields are updated in the encoding
order. This is wrong, because an incremental map may look like e.g.
new_up_client: { osd=6, addr=... } # set osd_state and addr
new_state: { osd=6, xorstate=EXISTS } # clear osd_state
Suppose osd6's current osd_state is EXISTS (i.e. osd6 is down). After
applying new_up_client, osd_state is changed to EXISTS | UP. Carrying
on with the new_state update, we flip EXISTS and leave osd6 in a weird
"!EXISTS but UP" state. A non-existent OSD is considered down by the
mapping code
2087 for (i = 0; i < pg->pg_temp.len; i++) {
2088 if (ceph_osd_is_down(osdmap, pg->pg_temp.osds[i])) {
2089 if (ceph_can_shift_osds(pi))
2090 continue;
2091
2092 temp->osds[temp->size++] = CRUSH_ITEM_NONE;
and so requests get directed to the second OSD in the set instead of
the first, resulting in OSD-side errors like:
[WRN] : client.4239 192.168.122.21:0/2444980242 misdirected client.4239.1:2827 pg 2.5df899f2 to osd.4 not [1,4,6] in e680/680
and hung rbds on the client:
[ 493.566367] rbd: rbd0: write 400000 at 11cc00000 (0)
[ 493.566805] rbd: rbd0: result -6 xferred 400000
[ 493.567011] blk_update_request: I/O error, dev rbd0, sector 9330688
The fix is to decouple application from the decoding and:
- apply new_weight first
- apply new_state before new_up_client
- twiddle osd_state flags if marking in
- clear out some of the state if osd is destroyed
Fixes: http://tracker.ceph.com/issues/14901
Cc: stable@vger.kernel.org # 3.15+: 6dd74e44dc1d: libceph: set 'exists' flag for newly up osd
Cc: stable@vger.kernel.org # 3.15+
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Josh Durgin <jdurgin@redhat.com>
2016-07-19 01:50:28 +00:00
|
|
|
if (xorstate == 0)
|
|
|
|
xorstate = CEPH_OSD_UP;
|
|
|
|
BUG_ON(osd >= map->max_osd);
|
|
|
|
if ((map->osd_state[osd] & CEPH_OSD_UP) &&
|
|
|
|
(xorstate & CEPH_OSD_UP))
|
|
|
|
pr_info("osd%d down\n", osd);
|
|
|
|
if ((map->osd_state[osd] & CEPH_OSD_EXISTS) &&
|
|
|
|
(xorstate & CEPH_OSD_EXISTS)) {
|
|
|
|
pr_info("osd%d does not exist\n", osd);
|
|
|
|
ret = set_primary_affinity(map, osd,
|
|
|
|
CEPH_OSD_DEFAULT_PRIMARY_AFFINITY);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
memset(map->osd_addr + osd, 0, sizeof(*map->osd_addr));
|
|
|
|
map->osd_state[osd] = 0;
|
|
|
|
} else {
|
|
|
|
map->osd_state[osd] ^= xorstate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* new_up_client */
|
|
|
|
*p = new_up_client;
|
|
|
|
len = ceph_decode_32(p);
|
|
|
|
while (len--) {
|
|
|
|
s32 osd;
|
|
|
|
struct ceph_entity_addr addr;
|
|
|
|
|
|
|
|
osd = ceph_decode_32(p);
|
|
|
|
BUG_ON(osd >= map->max_osd);
|
2020-10-30 12:30:51 +00:00
|
|
|
if (struct_v >= 7)
|
|
|
|
ret = ceph_decode_entity_addrvec(p, end, msgr2, &addr);
|
|
|
|
else
|
|
|
|
ret = ceph_decode_entity_addr(p, end, &addr);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
dout("%s osd%d addr %s\n", __func__, osd, ceph_pr_addr(&addr));
|
|
|
|
|
libceph: apply new_state before new_up_client on incrementals
Currently, osd_weight and osd_state fields are updated in the encoding
order. This is wrong, because an incremental map may look like e.g.
new_up_client: { osd=6, addr=... } # set osd_state and addr
new_state: { osd=6, xorstate=EXISTS } # clear osd_state
Suppose osd6's current osd_state is EXISTS (i.e. osd6 is down). After
applying new_up_client, osd_state is changed to EXISTS | UP. Carrying
on with the new_state update, we flip EXISTS and leave osd6 in a weird
"!EXISTS but UP" state. A non-existent OSD is considered down by the
mapping code
2087 for (i = 0; i < pg->pg_temp.len; i++) {
2088 if (ceph_osd_is_down(osdmap, pg->pg_temp.osds[i])) {
2089 if (ceph_can_shift_osds(pi))
2090 continue;
2091
2092 temp->osds[temp->size++] = CRUSH_ITEM_NONE;
and so requests get directed to the second OSD in the set instead of
the first, resulting in OSD-side errors like:
[WRN] : client.4239 192.168.122.21:0/2444980242 misdirected client.4239.1:2827 pg 2.5df899f2 to osd.4 not [1,4,6] in e680/680
and hung rbds on the client:
[ 493.566367] rbd: rbd0: write 400000 at 11cc00000 (0)
[ 493.566805] rbd: rbd0: result -6 xferred 400000
[ 493.567011] blk_update_request: I/O error, dev rbd0, sector 9330688
The fix is to decouple application from the decoding and:
- apply new_weight first
- apply new_state before new_up_client
- twiddle osd_state flags if marking in
- clear out some of the state if osd is destroyed
Fixes: http://tracker.ceph.com/issues/14901
Cc: stable@vger.kernel.org # 3.15+: 6dd74e44dc1d: libceph: set 'exists' flag for newly up osd
Cc: stable@vger.kernel.org # 3.15+
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Josh Durgin <jdurgin@redhat.com>
2016-07-19 01:50:28 +00:00
|
|
|
pr_info("osd%d up\n", osd);
|
|
|
|
map->osd_state[osd] |= CEPH_OSD_EXISTS | CEPH_OSD_UP;
|
|
|
|
map->osd_addr[osd] = addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
*p = new_weight_end;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
e_inval:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2009-10-06 18:31:10 +00:00
|
|
|
/*
|
|
|
|
* decode and apply an incremental map update.
|
|
|
|
*/
|
2020-10-30 12:30:51 +00:00
|
|
|
struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, bool msgr2,
|
2016-04-28 14:07:21 +00:00
|
|
|
struct ceph_osdmap *map)
|
2009-10-06 18:31:10 +00:00
|
|
|
{
|
|
|
|
struct ceph_fsid fsid;
|
|
|
|
u32 epoch = 0;
|
|
|
|
struct ceph_timespec modified;
|
2013-02-23 18:41:09 +00:00
|
|
|
s32 len;
|
|
|
|
u64 pool;
|
|
|
|
__s64 new_pool_max;
|
|
|
|
__s32 new_flags, max;
|
2009-10-06 18:31:10 +00:00
|
|
|
void *start = *p;
|
2014-03-13 14:36:15 +00:00
|
|
|
int err;
|
2014-03-21 17:05:29 +00:00
|
|
|
u8 struct_v;
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2014-03-13 14:36:13 +00:00
|
|
|
dout("%s %p to %p len %d\n", __func__, *p, end, (int)(end - *p));
|
|
|
|
|
2014-03-21 17:05:29 +00:00
|
|
|
err = get_osdmap_client_data_v(p, end, "inc", &struct_v);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2014-03-13 14:36:16 +00:00
|
|
|
/* fsid, epoch, modified, new_pool_max, new_flags */
|
|
|
|
ceph_decode_need(p, end, sizeof(fsid) + sizeof(u32) + sizeof(modified) +
|
|
|
|
sizeof(u64) + sizeof(u32), e_inval);
|
2009-10-06 18:31:10 +00:00
|
|
|
ceph_decode_copy(p, &fsid, sizeof(fsid));
|
2009-10-14 16:59:09 +00:00
|
|
|
epoch = ceph_decode_32(p);
|
2009-10-06 18:31:10 +00:00
|
|
|
BUG_ON(epoch != map->epoch+1);
|
|
|
|
ceph_decode_copy(p, &modified, sizeof(modified));
|
2013-02-23 18:41:09 +00:00
|
|
|
new_pool_max = ceph_decode_64(p);
|
2009-10-14 16:59:09 +00:00
|
|
|
new_flags = ceph_decode_32(p);
|
2009-10-06 18:31:10 +00:00
|
|
|
|
|
|
|
/* full map? */
|
2014-03-13 14:36:15 +00:00
|
|
|
ceph_decode_32_safe(p, end, len, e_inval);
|
2009-10-06 18:31:10 +00:00
|
|
|
if (len > 0) {
|
|
|
|
dout("apply_incremental full map len %d, %p to %p\n",
|
|
|
|
len, *p, end);
|
2020-10-30 12:30:51 +00:00
|
|
|
return ceph_osdmap_decode(p, min(*p+len, end), msgr2);
|
2009-10-06 18:31:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* new crush? */
|
2014-03-13 14:36:15 +00:00
|
|
|
ceph_decode_32_safe(p, end, len, e_inval);
|
2009-10-06 18:31:10 +00:00
|
|
|
if (len > 0) {
|
2017-01-31 14:55:06 +00:00
|
|
|
err = osdmap_set_crush(map,
|
|
|
|
crush_decode(*p, min(*p + len, end)));
|
|
|
|
if (err)
|
2014-03-13 14:36:15 +00:00
|
|
|
goto bad;
|
2010-06-17 17:22:48 +00:00
|
|
|
*p += len;
|
2009-10-06 18:31:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* new flags? */
|
|
|
|
if (new_flags >= 0)
|
|
|
|
map->flags = new_flags;
|
2010-02-16 23:55:03 +00:00
|
|
|
if (new_pool_max >= 0)
|
|
|
|
map->pool_max = new_pool_max;
|
2009-10-06 18:31:10 +00:00
|
|
|
|
|
|
|
/* new max? */
|
2014-03-13 14:36:16 +00:00
|
|
|
ceph_decode_32_safe(p, end, max, e_inval);
|
2009-10-06 18:31:10 +00:00
|
|
|
if (max >= 0) {
|
|
|
|
err = osdmap_set_max_osd(map, max);
|
2014-03-13 14:36:15 +00:00
|
|
|
if (err)
|
2009-10-06 18:31:10 +00:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
map->epoch++;
|
2011-05-12 22:18:43 +00:00
|
|
|
map->modified = modified;
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2014-03-21 17:05:27 +00:00
|
|
|
/* new_pools */
|
|
|
|
err = decode_new_pools(p, end, map);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
2014-03-13 14:36:16 +00:00
|
|
|
|
2014-03-21 17:05:27 +00:00
|
|
|
/* new_pool_names */
|
|
|
|
err = decode_pool_names(p, end, map);
|
2014-03-13 14:36:16 +00:00
|
|
|
if (err)
|
|
|
|
goto bad;
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2010-02-16 23:55:03 +00:00
|
|
|
/* old_pool */
|
2014-03-13 14:36:15 +00:00
|
|
|
ceph_decode_32_safe(p, end, len, e_inval);
|
2010-02-16 23:55:03 +00:00
|
|
|
while (len--) {
|
|
|
|
struct ceph_pg_pool_info *pi;
|
|
|
|
|
2014-03-13 14:36:15 +00:00
|
|
|
ceph_decode_64_safe(p, end, pool, e_inval);
|
2020-05-19 14:46:47 +00:00
|
|
|
pi = lookup_pg_pool(&map->pg_pools, pool);
|
2010-04-09 22:46:42 +00:00
|
|
|
if (pi)
|
|
|
|
__remove_pg_pool(&map->pg_pools, pi);
|
2010-02-16 23:55:03 +00:00
|
|
|
}
|
2009-10-06 18:31:10 +00:00
|
|
|
|
libceph: apply new_state before new_up_client on incrementals
Currently, osd_weight and osd_state fields are updated in the encoding
order. This is wrong, because an incremental map may look like e.g.
new_up_client: { osd=6, addr=... } # set osd_state and addr
new_state: { osd=6, xorstate=EXISTS } # clear osd_state
Suppose osd6's current osd_state is EXISTS (i.e. osd6 is down). After
applying new_up_client, osd_state is changed to EXISTS | UP. Carrying
on with the new_state update, we flip EXISTS and leave osd6 in a weird
"!EXISTS but UP" state. A non-existent OSD is considered down by the
mapping code
2087 for (i = 0; i < pg->pg_temp.len; i++) {
2088 if (ceph_osd_is_down(osdmap, pg->pg_temp.osds[i])) {
2089 if (ceph_can_shift_osds(pi))
2090 continue;
2091
2092 temp->osds[temp->size++] = CRUSH_ITEM_NONE;
and so requests get directed to the second OSD in the set instead of
the first, resulting in OSD-side errors like:
[WRN] : client.4239 192.168.122.21:0/2444980242 misdirected client.4239.1:2827 pg 2.5df899f2 to osd.4 not [1,4,6] in e680/680
and hung rbds on the client:
[ 493.566367] rbd: rbd0: write 400000 at 11cc00000 (0)
[ 493.566805] rbd: rbd0: result -6 xferred 400000
[ 493.567011] blk_update_request: I/O error, dev rbd0, sector 9330688
The fix is to decouple application from the decoding and:
- apply new_weight first
- apply new_state before new_up_client
- twiddle osd_state flags if marking in
- clear out some of the state if osd is destroyed
Fixes: http://tracker.ceph.com/issues/14901
Cc: stable@vger.kernel.org # 3.15+: 6dd74e44dc1d: libceph: set 'exists' flag for newly up osd
Cc: stable@vger.kernel.org # 3.15+
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Josh Durgin <jdurgin@redhat.com>
2016-07-19 01:50:28 +00:00
|
|
|
/* new_up_client, new_state, new_weight */
|
2020-10-30 12:30:51 +00:00
|
|
|
err = decode_new_up_state_weight(p, end, struct_v, msgr2, map);
|
libceph: apply new_state before new_up_client on incrementals
Currently, osd_weight and osd_state fields are updated in the encoding
order. This is wrong, because an incremental map may look like e.g.
new_up_client: { osd=6, addr=... } # set osd_state and addr
new_state: { osd=6, xorstate=EXISTS } # clear osd_state
Suppose osd6's current osd_state is EXISTS (i.e. osd6 is down). After
applying new_up_client, osd_state is changed to EXISTS | UP. Carrying
on with the new_state update, we flip EXISTS and leave osd6 in a weird
"!EXISTS but UP" state. A non-existent OSD is considered down by the
mapping code
2087 for (i = 0; i < pg->pg_temp.len; i++) {
2088 if (ceph_osd_is_down(osdmap, pg->pg_temp.osds[i])) {
2089 if (ceph_can_shift_osds(pi))
2090 continue;
2091
2092 temp->osds[temp->size++] = CRUSH_ITEM_NONE;
and so requests get directed to the second OSD in the set instead of
the first, resulting in OSD-side errors like:
[WRN] : client.4239 192.168.122.21:0/2444980242 misdirected client.4239.1:2827 pg 2.5df899f2 to osd.4 not [1,4,6] in e680/680
and hung rbds on the client:
[ 493.566367] rbd: rbd0: write 400000 at 11cc00000 (0)
[ 493.566805] rbd: rbd0: result -6 xferred 400000
[ 493.567011] blk_update_request: I/O error, dev rbd0, sector 9330688
The fix is to decouple application from the decoding and:
- apply new_weight first
- apply new_state before new_up_client
- twiddle osd_state flags if marking in
- clear out some of the state if osd is destroyed
Fixes: http://tracker.ceph.com/issues/14901
Cc: stable@vger.kernel.org # 3.15+: 6dd74e44dc1d: libceph: set 'exists' flag for newly up osd
Cc: stable@vger.kernel.org # 3.15+
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Josh Durgin <jdurgin@redhat.com>
2016-07-19 01:50:28 +00:00
|
|
|
if (err)
|
|
|
|
goto bad;
|
2009-10-06 18:31:10 +00:00
|
|
|
|
|
|
|
/* new_pg_temp */
|
2014-03-21 17:05:28 +00:00
|
|
|
err = decode_new_pg_temp(p, end, map);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2014-03-21 17:05:30 +00:00
|
|
|
/* new_primary_temp */
|
|
|
|
if (struct_v >= 1) {
|
|
|
|
err = decode_new_primary_temp(p, end, map);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2014-03-21 17:05:30 +00:00
|
|
|
/* new_primary_affinity */
|
|
|
|
if (struct_v >= 2) {
|
|
|
|
err = decode_new_primary_affinity(p, end, map);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2017-06-21 15:27:18 +00:00
|
|
|
if (struct_v >= 3) {
|
|
|
|
/* new_erasure_code_profiles */
|
|
|
|
ceph_decode_skip_map_of_map(p, end, string, string, string,
|
2017-07-13 07:45:17 +00:00
|
|
|
e_inval);
|
2017-06-21 15:27:18 +00:00
|
|
|
/* old_erasure_code_profiles */
|
2017-07-13 07:45:17 +00:00
|
|
|
ceph_decode_skip_set(p, end, string, e_inval);
|
2017-06-21 15:27:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (struct_v >= 4) {
|
|
|
|
err = decode_new_pg_upmap(p, end, map);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
err = decode_old_pg_upmap(p, end, map);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
err = decode_new_pg_upmap_items(p, end, map);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
err = decode_old_pg_upmap_items(p, end, map);
|
|
|
|
if (err)
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2009-10-06 18:31:10 +00:00
|
|
|
/* ignore the rest */
|
|
|
|
*p = end;
|
2014-03-13 14:36:13 +00:00
|
|
|
|
|
|
|
dout("inc osdmap epoch %d max_osd %d\n", map->epoch, map->max_osd);
|
2009-10-06 18:31:10 +00:00
|
|
|
return map;
|
|
|
|
|
2014-03-13 14:36:15 +00:00
|
|
|
e_inval:
|
|
|
|
err = -EINVAL;
|
2009-10-06 18:31:10 +00:00
|
|
|
bad:
|
2014-03-13 14:36:13 +00:00
|
|
|
pr_err("corrupt inc osdmap (%d) epoch %d off %d (%p of %p-%p)\n",
|
|
|
|
err, epoch, (int)(*p - start), *p, start, end);
|
2009-12-14 23:13:47 +00:00
|
|
|
print_hex_dump(KERN_DEBUG, "osdmap: ",
|
|
|
|
DUMP_PREFIX_OFFSET, 16, 1,
|
|
|
|
start, end - start, true);
|
2009-10-06 18:31:10 +00:00
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
|
|
|
|
2016-02-14 03:24:31 +00:00
|
|
|
void ceph_oloc_copy(struct ceph_object_locator *dest,
|
|
|
|
const struct ceph_object_locator *src)
|
|
|
|
{
|
2017-06-05 12:44:59 +00:00
|
|
|
ceph_oloc_destroy(dest);
|
2016-02-14 03:24:31 +00:00
|
|
|
|
|
|
|
dest->pool = src->pool;
|
|
|
|
if (src->pool_ns)
|
|
|
|
dest->pool_ns = ceph_get_string(src->pool_ns);
|
2017-06-05 12:44:59 +00:00
|
|
|
else
|
|
|
|
dest->pool_ns = NULL;
|
2016-02-14 03:24:31 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ceph_oloc_copy);
|
|
|
|
|
|
|
|
void ceph_oloc_destroy(struct ceph_object_locator *oloc)
|
|
|
|
{
|
|
|
|
ceph_put_string(oloc->pool_ns);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ceph_oloc_destroy);
|
|
|
|
|
2016-04-29 17:54:20 +00:00
|
|
|
void ceph_oid_copy(struct ceph_object_id *dest,
|
|
|
|
const struct ceph_object_id *src)
|
|
|
|
{
|
2017-06-05 12:44:59 +00:00
|
|
|
ceph_oid_destroy(dest);
|
2016-04-29 17:54:20 +00:00
|
|
|
|
|
|
|
if (src->name != src->inline_name) {
|
|
|
|
/* very rare, see ceph_object_id definition */
|
|
|
|
dest->name = kmalloc(src->name_len + 1,
|
|
|
|
GFP_NOIO | __GFP_NOFAIL);
|
2017-06-05 12:44:59 +00:00
|
|
|
} else {
|
|
|
|
dest->name = dest->inline_name;
|
2016-04-29 17:54:20 +00:00
|
|
|
}
|
|
|
|
memcpy(dest->name, src->name, src->name_len + 1);
|
|
|
|
dest->name_len = src->name_len;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ceph_oid_copy);
|
|
|
|
|
|
|
|
static __printf(2, 0)
|
|
|
|
int oid_printf_vargs(struct ceph_object_id *oid, const char *fmt, va_list ap)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
|
|
|
|
WARN_ON(!ceph_oid_empty(oid));
|
|
|
|
|
|
|
|
len = vsnprintf(oid->inline_name, sizeof(oid->inline_name), fmt, ap);
|
|
|
|
if (len >= sizeof(oid->inline_name))
|
|
|
|
return len;
|
|
|
|
|
|
|
|
oid->name_len = len;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If oid doesn't fit into inline buffer, BUG.
|
|
|
|
*/
|
|
|
|
void ceph_oid_printf(struct ceph_object_id *oid, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
BUG_ON(oid_printf_vargs(oid, fmt, ap));
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ceph_oid_printf);
|
|
|
|
|
|
|
|
static __printf(3, 0)
|
|
|
|
int oid_aprintf_vargs(struct ceph_object_id *oid, gfp_t gfp,
|
|
|
|
const char *fmt, va_list ap)
|
|
|
|
{
|
|
|
|
va_list aq;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
va_copy(aq, ap);
|
|
|
|
len = oid_printf_vargs(oid, fmt, aq);
|
|
|
|
va_end(aq);
|
|
|
|
|
|
|
|
if (len) {
|
|
|
|
char *external_name;
|
|
|
|
|
|
|
|
external_name = kmalloc(len + 1, gfp);
|
|
|
|
if (!external_name)
|
|
|
|
return -ENOMEM;
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2016-04-29 17:54:20 +00:00
|
|
|
oid->name = external_name;
|
|
|
|
WARN_ON(vsnprintf(oid->name, len + 1, fmt, ap) != len);
|
|
|
|
oid->name_len = len;
|
|
|
|
}
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2016-04-29 17:54:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If oid doesn't fit into inline buffer, allocate.
|
|
|
|
*/
|
|
|
|
int ceph_oid_aprintf(struct ceph_object_id *oid, gfp_t gfp,
|
|
|
|
const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
ret = oid_aprintf_vargs(oid, gfp, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ceph_oid_aprintf);
|
|
|
|
|
|
|
|
void ceph_oid_destroy(struct ceph_object_id *oid)
|
|
|
|
{
|
|
|
|
if (oid->name != oid->inline_name)
|
|
|
|
kfree(oid->name);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ceph_oid_destroy);
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2016-04-28 14:07:23 +00:00
|
|
|
/*
|
|
|
|
* osds only
|
|
|
|
*/
|
|
|
|
static bool __osds_equal(const struct ceph_osds *lhs,
|
|
|
|
const struct ceph_osds *rhs)
|
|
|
|
{
|
|
|
|
if (lhs->size == rhs->size &&
|
|
|
|
!memcmp(lhs->osds, rhs->osds, rhs->size * sizeof(rhs->osds[0])))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* osds + primary
|
|
|
|
*/
|
|
|
|
static bool osds_equal(const struct ceph_osds *lhs,
|
|
|
|
const struct ceph_osds *rhs)
|
|
|
|
{
|
|
|
|
if (__osds_equal(lhs, rhs) &&
|
|
|
|
lhs->primary == rhs->primary)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-04-28 14:07:22 +00:00
|
|
|
static bool osds_valid(const struct ceph_osds *set)
|
|
|
|
{
|
|
|
|
/* non-empty set */
|
|
|
|
if (set->size > 0 && set->primary >= 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* empty can_shift_osds set */
|
|
|
|
if (!set->size && set->primary == -1)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* empty !can_shift_osds set - all NONE */
|
|
|
|
if (set->size > 0 && set->primary == -1) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < set->size; i++) {
|
|
|
|
if (set->osds[i] != CRUSH_ITEM_NONE)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == set->size)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ceph_osds_copy(struct ceph_osds *dest, const struct ceph_osds *src)
|
|
|
|
{
|
|
|
|
memcpy(dest->osds, src->osds, src->size * sizeof(src->osds[0]));
|
|
|
|
dest->size = src->size;
|
|
|
|
dest->primary = src->primary;
|
|
|
|
}
|
|
|
|
|
2017-06-15 14:30:54 +00:00
|
|
|
bool ceph_pg_is_split(const struct ceph_pg *pgid, u32 old_pg_num,
|
|
|
|
u32 new_pg_num)
|
2016-04-28 14:07:23 +00:00
|
|
|
{
|
|
|
|
int old_bits = calc_bits_of(old_pg_num);
|
|
|
|
int old_mask = (1 << old_bits) - 1;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
WARN_ON(pgid->seed >= old_pg_num);
|
|
|
|
if (new_pg_num <= old_pg_num)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (n = 1; ; n++) {
|
|
|
|
int next_bit = n << (old_bits - 1);
|
|
|
|
u32 s = next_bit | pgid->seed;
|
|
|
|
|
|
|
|
if (s < old_pg_num || s == pgid->seed)
|
|
|
|
continue;
|
|
|
|
if (s >= new_pg_num)
|
|
|
|
break;
|
|
|
|
|
|
|
|
s = ceph_stable_mod(s, old_pg_num, old_mask);
|
|
|
|
if (s == pgid->seed)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ceph_is_new_interval(const struct ceph_osds *old_acting,
|
|
|
|
const struct ceph_osds *new_acting,
|
|
|
|
const struct ceph_osds *old_up,
|
|
|
|
const struct ceph_osds *new_up,
|
|
|
|
int old_size,
|
|
|
|
int new_size,
|
|
|
|
int old_min_size,
|
|
|
|
int new_min_size,
|
|
|
|
u32 old_pg_num,
|
|
|
|
u32 new_pg_num,
|
|
|
|
bool old_sort_bitwise,
|
|
|
|
bool new_sort_bitwise,
|
2017-07-27 15:59:14 +00:00
|
|
|
bool old_recovery_deletes,
|
|
|
|
bool new_recovery_deletes,
|
2016-04-28 14:07:23 +00:00
|
|
|
const struct ceph_pg *pgid)
|
|
|
|
{
|
|
|
|
return !osds_equal(old_acting, new_acting) ||
|
|
|
|
!osds_equal(old_up, new_up) ||
|
|
|
|
old_size != new_size ||
|
|
|
|
old_min_size != new_min_size ||
|
2017-06-15 14:30:54 +00:00
|
|
|
ceph_pg_is_split(pgid, old_pg_num, new_pg_num) ||
|
2017-07-27 15:59:14 +00:00
|
|
|
old_sort_bitwise != new_sort_bitwise ||
|
|
|
|
old_recovery_deletes != new_recovery_deletes;
|
2016-04-28 14:07:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int calc_pg_rank(int osd, const struct ceph_osds *acting)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < acting->size; i++) {
|
|
|
|
if (acting->osds[i] == osd)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool primary_changed(const struct ceph_osds *old_acting,
|
|
|
|
const struct ceph_osds *new_acting)
|
|
|
|
{
|
|
|
|
if (!old_acting->size && !new_acting->size)
|
|
|
|
return false; /* both still empty */
|
|
|
|
|
|
|
|
if (!old_acting->size ^ !new_acting->size)
|
|
|
|
return true; /* was empty, now not, or vice versa */
|
|
|
|
|
|
|
|
if (old_acting->primary != new_acting->primary)
|
|
|
|
return true; /* primary changed */
|
|
|
|
|
|
|
|
if (calc_pg_rank(old_acting->primary, old_acting) !=
|
|
|
|
calc_pg_rank(new_acting->primary, new_acting))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false; /* same primary (tho replicas may have changed) */
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ceph_osds_changed(const struct ceph_osds *old_acting,
|
|
|
|
const struct ceph_osds *new_acting,
|
|
|
|
bool any_change)
|
|
|
|
{
|
|
|
|
if (primary_changed(old_acting, new_acting))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (any_change && !__osds_equal(old_acting, new_acting))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-10-06 18:31:10 +00:00
|
|
|
/*
|
2016-04-28 14:07:22 +00:00
|
|
|
* Map an object into a PG.
|
|
|
|
*
|
|
|
|
* Should only be called with target_oid and target_oloc (as opposed to
|
|
|
|
* base_oid and base_oloc), since tiering isn't taken into account.
|
2009-10-06 18:31:10 +00:00
|
|
|
*/
|
2018-05-23 12:46:53 +00:00
|
|
|
void __ceph_object_locator_to_pg(struct ceph_pg_pool_info *pi,
|
|
|
|
const struct ceph_object_id *oid,
|
|
|
|
const struct ceph_object_locator *oloc,
|
|
|
|
struct ceph_pg *raw_pgid)
|
2009-10-06 18:31:10 +00:00
|
|
|
{
|
2017-06-15 14:30:56 +00:00
|
|
|
WARN_ON(pi->id != oloc->pool);
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2016-02-14 03:24:31 +00:00
|
|
|
if (!oloc->pool_ns) {
|
|
|
|
raw_pgid->pool = oloc->pool;
|
|
|
|
raw_pgid->seed = ceph_str_hash(pi->object_hash, oid->name,
|
|
|
|
oid->name_len);
|
|
|
|
dout("%s %s -> raw_pgid %llu.%x\n", __func__, oid->name,
|
|
|
|
raw_pgid->pool, raw_pgid->seed);
|
|
|
|
} else {
|
|
|
|
char stack_buf[256];
|
|
|
|
char *buf = stack_buf;
|
|
|
|
int nsl = oloc->pool_ns->len;
|
|
|
|
size_t total = nsl + 1 + oid->name_len;
|
|
|
|
|
2018-05-23 12:46:53 +00:00
|
|
|
if (total > sizeof(stack_buf))
|
|
|
|
buf = kmalloc(total, GFP_NOIO | __GFP_NOFAIL);
|
2016-02-14 03:24:31 +00:00
|
|
|
memcpy(buf, oloc->pool_ns->str, nsl);
|
|
|
|
buf[nsl] = '\037';
|
|
|
|
memcpy(buf + nsl + 1, oid->name, oid->name_len);
|
|
|
|
raw_pgid->pool = oloc->pool;
|
|
|
|
raw_pgid->seed = ceph_str_hash(pi->object_hash, buf, total);
|
|
|
|
if (buf != stack_buf)
|
|
|
|
kfree(buf);
|
|
|
|
dout("%s %s ns %.*s -> raw_pgid %llu.%x\n", __func__,
|
|
|
|
oid->name, nsl, oloc->pool_ns->str,
|
|
|
|
raw_pgid->pool, raw_pgid->seed);
|
|
|
|
}
|
2009-10-06 18:31:10 +00:00
|
|
|
}
|
2017-06-15 14:30:56 +00:00
|
|
|
|
|
|
|
int ceph_object_locator_to_pg(struct ceph_osdmap *osdmap,
|
|
|
|
const struct ceph_object_id *oid,
|
|
|
|
const struct ceph_object_locator *oloc,
|
|
|
|
struct ceph_pg *raw_pgid)
|
|
|
|
{
|
|
|
|
struct ceph_pg_pool_info *pi;
|
|
|
|
|
|
|
|
pi = ceph_pg_pool_by_id(osdmap, oloc->pool);
|
|
|
|
if (!pi)
|
|
|
|
return -ENOENT;
|
|
|
|
|
2018-05-23 12:46:53 +00:00
|
|
|
__ceph_object_locator_to_pg(pi, oid, oloc, raw_pgid);
|
|
|
|
return 0;
|
2017-06-15 14:30:56 +00:00
|
|
|
}
|
2016-04-28 14:07:22 +00:00
|
|
|
EXPORT_SYMBOL(ceph_object_locator_to_pg);
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2016-04-28 14:07:22 +00:00
|
|
|
/*
|
|
|
|
* Map a raw PG (full precision ps) into an actual PG.
|
|
|
|
*/
|
|
|
|
static void raw_pg_to_pg(struct ceph_pg_pool_info *pi,
|
|
|
|
const struct ceph_pg *raw_pgid,
|
|
|
|
struct ceph_pg *pgid)
|
|
|
|
{
|
|
|
|
pgid->pool = raw_pgid->pool;
|
|
|
|
pgid->seed = ceph_stable_mod(raw_pgid->seed, pi->pg_num,
|
|
|
|
pi->pg_num_mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Map a raw PG (full precision ps) into a placement ps (placement
|
|
|
|
* seed). Include pool id in that value so that different pools don't
|
|
|
|
* use the same seeds.
|
|
|
|
*/
|
|
|
|
static u32 raw_pg_to_pps(struct ceph_pg_pool_info *pi,
|
|
|
|
const struct ceph_pg *raw_pgid)
|
|
|
|
{
|
|
|
|
if (pi->flags & CEPH_POOL_FLAG_HASHPSPOOL) {
|
|
|
|
/* hash pool id and seed so that pool PGs do not overlap */
|
|
|
|
return crush_hash32_2(CRUSH_HASH_RJENKINS1,
|
|
|
|
ceph_stable_mod(raw_pgid->seed,
|
|
|
|
pi->pgp_num,
|
|
|
|
pi->pgp_num_mask),
|
|
|
|
raw_pgid->pool);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* legacy behavior: add ps and pool together. this is
|
|
|
|
* not a great approach because the PGs from each pool
|
|
|
|
* will overlap on top of each other: 0.5 == 1.4 ==
|
|
|
|
* 2.3 == ...
|
|
|
|
*/
|
|
|
|
return ceph_stable_mod(raw_pgid->seed, pi->pgp_num,
|
|
|
|
pi->pgp_num_mask) +
|
|
|
|
(unsigned)raw_pgid->pool;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-24 14:43:49 +00:00
|
|
|
/*
|
|
|
|
* Magic value used for a "default" fallback choose_args, used if the
|
|
|
|
* crush_choose_arg_map passed to do_crush() does not exist. If this
|
|
|
|
* also doesn't exist, fall back to canonical weights.
|
|
|
|
*/
|
|
|
|
#define CEPH_DEFAULT_CHOOSE_ARGS -1
|
|
|
|
|
2014-01-31 15:54:26 +00:00
|
|
|
static int do_crush(struct ceph_osdmap *map, int ruleno, int x,
|
|
|
|
int *result, int result_max,
|
2017-06-22 17:44:05 +00:00
|
|
|
const __u32 *weight, int weight_max,
|
2017-07-24 14:43:49 +00:00
|
|
|
s64 choose_args_index)
|
2013-12-24 19:19:24 +00:00
|
|
|
{
|
2017-06-22 17:44:05 +00:00
|
|
|
struct crush_choose_arg_map *arg_map;
|
2020-08-17 11:45:04 +00:00
|
|
|
struct crush_work *work;
|
2014-01-31 15:54:26 +00:00
|
|
|
int r;
|
|
|
|
|
|
|
|
BUG_ON(result_max > CEPH_PG_MAX_SIZE);
|
|
|
|
|
2017-06-22 17:44:05 +00:00
|
|
|
arg_map = lookup_choose_arg_map(&map->crush->choose_args,
|
|
|
|
choose_args_index);
|
2017-07-24 14:43:49 +00:00
|
|
|
if (!arg_map)
|
|
|
|
arg_map = lookup_choose_arg_map(&map->crush->choose_args,
|
|
|
|
CEPH_DEFAULT_CHOOSE_ARGS);
|
2017-06-22 17:44:05 +00:00
|
|
|
|
2020-08-17 11:45:04 +00:00
|
|
|
work = get_workspace(&map->crush_wsm, map->crush);
|
2014-01-31 15:54:26 +00:00
|
|
|
r = crush_do_rule(map->crush, ruleno, x, result, result_max,
|
2020-08-17 11:45:04 +00:00
|
|
|
weight, weight_max, work,
|
2017-06-22 17:44:05 +00:00
|
|
|
arg_map ? arg_map->args : NULL);
|
2020-08-17 11:45:04 +00:00
|
|
|
put_workspace(&map->crush_wsm, work);
|
2014-01-31 15:54:26 +00:00
|
|
|
return r;
|
2013-12-24 19:19:24 +00:00
|
|
|
}
|
|
|
|
|
2017-06-21 15:27:18 +00:00
|
|
|
static void remove_nonexistent_osds(struct ceph_osdmap *osdmap,
|
|
|
|
struct ceph_pg_pool_info *pi,
|
|
|
|
struct ceph_osds *set)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (ceph_can_shift_osds(pi)) {
|
|
|
|
int removed = 0;
|
|
|
|
|
|
|
|
/* shift left */
|
|
|
|
for (i = 0; i < set->size; i++) {
|
|
|
|
if (!ceph_osd_exists(osdmap, set->osds[i])) {
|
|
|
|
removed++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (removed)
|
|
|
|
set->osds[i - removed] = set->osds[i];
|
|
|
|
}
|
|
|
|
set->size -= removed;
|
|
|
|
} else {
|
|
|
|
/* set dne devices to NONE */
|
|
|
|
for (i = 0; i < set->size; i++) {
|
|
|
|
if (!ceph_osd_exists(osdmap, set->osds[i]))
|
|
|
|
set->osds[i] = CRUSH_ITEM_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-24 15:12:47 +00:00
|
|
|
/*
|
2017-06-21 15:27:18 +00:00
|
|
|
* Calculate raw set (CRUSH output) for given PG and filter out
|
|
|
|
* nonexistent OSDs. ->primary is undefined for a raw set.
|
2014-03-24 15:12:47 +00:00
|
|
|
*
|
2016-04-28 14:07:22 +00:00
|
|
|
* Placement seed (CRUSH input) is returned through @ppps.
|
2014-03-24 15:12:47 +00:00
|
|
|
*/
|
2016-04-28 14:07:22 +00:00
|
|
|
static void pg_to_raw_osds(struct ceph_osdmap *osdmap,
|
|
|
|
struct ceph_pg_pool_info *pi,
|
|
|
|
const struct ceph_pg *raw_pgid,
|
|
|
|
struct ceph_osds *raw,
|
|
|
|
u32 *ppps)
|
2014-03-24 15:12:47 +00:00
|
|
|
{
|
2016-04-28 14:07:22 +00:00
|
|
|
u32 pps = raw_pg_to_pps(pi, raw_pgid);
|
2014-03-24 15:12:47 +00:00
|
|
|
int ruleno;
|
|
|
|
int len;
|
|
|
|
|
2016-04-28 14:07:22 +00:00
|
|
|
ceph_osds_init(raw);
|
|
|
|
if (ppps)
|
|
|
|
*ppps = pps;
|
|
|
|
|
|
|
|
ruleno = crush_find_rule(osdmap->crush, pi->crush_ruleset, pi->type,
|
|
|
|
pi->size);
|
2014-03-24 15:12:47 +00:00
|
|
|
if (ruleno < 0) {
|
|
|
|
pr_err("no crush rule: pool %lld ruleset %d type %d size %d\n",
|
2016-04-28 14:07:22 +00:00
|
|
|
pi->id, pi->crush_ruleset, pi->type, pi->size);
|
|
|
|
return;
|
2014-03-24 15:12:47 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 17:57:48 +00:00
|
|
|
if (pi->size > ARRAY_SIZE(raw->osds)) {
|
|
|
|
pr_err_ratelimited("pool %lld ruleset %d type %d too wide: size %d > %zu\n",
|
|
|
|
pi->id, pi->crush_ruleset, pi->type, pi->size,
|
|
|
|
ARRAY_SIZE(raw->osds));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = do_crush(osdmap, ruleno, pps, raw->osds, pi->size,
|
2017-06-22 17:44:05 +00:00
|
|
|
osdmap->osd_weight, osdmap->max_osd, pi->id);
|
2014-03-24 15:12:47 +00:00
|
|
|
if (len < 0) {
|
|
|
|
pr_err("error %d from crush rule %d: pool %lld ruleset %d type %d size %d\n",
|
2016-04-28 14:07:22 +00:00
|
|
|
len, ruleno, pi->id, pi->crush_ruleset, pi->type,
|
|
|
|
pi->size);
|
|
|
|
return;
|
2014-03-24 15:12:47 +00:00
|
|
|
}
|
|
|
|
|
2016-04-28 14:07:22 +00:00
|
|
|
raw->size = len;
|
2017-06-21 15:27:18 +00:00
|
|
|
remove_nonexistent_osds(osdmap, pi, raw);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* apply pg_upmap[_items] mappings */
|
|
|
|
static void apply_upmap(struct ceph_osdmap *osdmap,
|
|
|
|
const struct ceph_pg *pgid,
|
|
|
|
struct ceph_osds *raw)
|
|
|
|
{
|
|
|
|
struct ceph_pg_mapping *pg;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
pg = lookup_pg_mapping(&osdmap->pg_upmap, pgid);
|
|
|
|
if (pg) {
|
|
|
|
/* make sure targets aren't marked out */
|
|
|
|
for (i = 0; i < pg->pg_upmap.len; i++) {
|
|
|
|
int osd = pg->pg_upmap.osds[i];
|
|
|
|
|
|
|
|
if (osd != CRUSH_ITEM_NONE &&
|
|
|
|
osd < osdmap->max_osd &&
|
|
|
|
osdmap->osd_weight[osd] == 0) {
|
|
|
|
/* reject/ignore explicit mapping */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < pg->pg_upmap.len; i++)
|
|
|
|
raw->osds[i] = pg->pg_upmap.osds[i];
|
|
|
|
raw->size = pg->pg_upmap.len;
|
2017-07-27 13:16:39 +00:00
|
|
|
/* check and apply pg_upmap_items, if any */
|
2017-06-21 15:27:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pg = lookup_pg_mapping(&osdmap->pg_upmap_items, pgid);
|
|
|
|
if (pg) {
|
libceph: don't allow bidirectional swap of pg-upmap-items
This reverts most of commit f53b7665c8ce ("libceph: upmap semantic
changes").
We need to prevent duplicates in the final result. For example, we
can currently take
[1,2,3] and apply [(1,2)] and get [2,2,3]
or
[1,2,3] and apply [(3,2)] and get [1,2,2]
The rest of the system is not prepared to handle duplicates in the
result set like this.
The reverted piece was intended to allow
[1,2,3] and [(1,2),(2,1)] to get [2,1,3]
to reorder primaries. First, this bidirectional swap is hard to
implement in a way that also prevents dups. For example, [1,2,3] and
[(1,4),(2,3),(3,4)] would give [4,3,4] but would we just drop the last
step we'd have [4,3,3] which is also invalid, etc. Simpler to just not
handle bidirectional swaps. In practice, they are not needed: if you
just want to choose a different primary then use primary_affinity, or
pg_upmap (not pg_upmap_items).
Cc: stable@vger.kernel.org # 4.13
Link: http://tracker.ceph.com/issues/21410
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Sage Weil <sage@redhat.com>
2017-09-18 10:21:37 +00:00
|
|
|
/*
|
|
|
|
* Note: this approach does not allow a bidirectional swap,
|
|
|
|
* e.g., [[1,2],[2,1]] applied to [0,1,2] -> [0,2,1].
|
|
|
|
*/
|
|
|
|
for (i = 0; i < pg->pg_upmap_items.len; i++) {
|
|
|
|
int from = pg->pg_upmap_items.from_to[i][0];
|
|
|
|
int to = pg->pg_upmap_items.from_to[i][1];
|
|
|
|
int pos = -1;
|
|
|
|
bool exists = false;
|
|
|
|
|
|
|
|
/* make sure replacement doesn't already appear */
|
|
|
|
for (j = 0; j < raw->size; j++) {
|
|
|
|
int osd = raw->osds[j];
|
|
|
|
|
|
|
|
if (osd == to) {
|
|
|
|
exists = true;
|
2017-06-21 15:27:18 +00:00
|
|
|
break;
|
|
|
|
}
|
libceph: don't allow bidirectional swap of pg-upmap-items
This reverts most of commit f53b7665c8ce ("libceph: upmap semantic
changes").
We need to prevent duplicates in the final result. For example, we
can currently take
[1,2,3] and apply [(1,2)] and get [2,2,3]
or
[1,2,3] and apply [(3,2)] and get [1,2,2]
The rest of the system is not prepared to handle duplicates in the
result set like this.
The reverted piece was intended to allow
[1,2,3] and [(1,2),(2,1)] to get [2,1,3]
to reorder primaries. First, this bidirectional swap is hard to
implement in a way that also prevents dups. For example, [1,2,3] and
[(1,4),(2,3),(3,4)] would give [4,3,4] but would we just drop the last
step we'd have [4,3,3] which is also invalid, etc. Simpler to just not
handle bidirectional swaps. In practice, they are not needed: if you
just want to choose a different primary then use primary_affinity, or
pg_upmap (not pg_upmap_items).
Cc: stable@vger.kernel.org # 4.13
Link: http://tracker.ceph.com/issues/21410
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Sage Weil <sage@redhat.com>
2017-09-18 10:21:37 +00:00
|
|
|
/* ignore mapping if target is marked out */
|
|
|
|
if (osd == from && pos < 0 &&
|
|
|
|
!(to != CRUSH_ITEM_NONE &&
|
|
|
|
to < osdmap->max_osd &&
|
|
|
|
osdmap->osd_weight[to] == 0)) {
|
|
|
|
pos = j;
|
|
|
|
}
|
2017-06-21 15:27:18 +00:00
|
|
|
}
|
libceph: don't allow bidirectional swap of pg-upmap-items
This reverts most of commit f53b7665c8ce ("libceph: upmap semantic
changes").
We need to prevent duplicates in the final result. For example, we
can currently take
[1,2,3] and apply [(1,2)] and get [2,2,3]
or
[1,2,3] and apply [(3,2)] and get [1,2,2]
The rest of the system is not prepared to handle duplicates in the
result set like this.
The reverted piece was intended to allow
[1,2,3] and [(1,2),(2,1)] to get [2,1,3]
to reorder primaries. First, this bidirectional swap is hard to
implement in a way that also prevents dups. For example, [1,2,3] and
[(1,4),(2,3),(3,4)] would give [4,3,4] but would we just drop the last
step we'd have [4,3,3] which is also invalid, etc. Simpler to just not
handle bidirectional swaps. In practice, they are not needed: if you
just want to choose a different primary then use primary_affinity, or
pg_upmap (not pg_upmap_items).
Cc: stable@vger.kernel.org # 4.13
Link: http://tracker.ceph.com/issues/21410
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Sage Weil <sage@redhat.com>
2017-09-18 10:21:37 +00:00
|
|
|
if (!exists && pos >= 0)
|
|
|
|
raw->osds[pos] = to;
|
2017-06-21 15:27:18 +00:00
|
|
|
}
|
|
|
|
}
|
2014-03-24 15:12:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-04-28 14:07:22 +00:00
|
|
|
* Given raw set, calculate up set and up primary. By definition of an
|
|
|
|
* up set, the result won't contain nonexistent or down OSDs.
|
2014-03-24 15:12:47 +00:00
|
|
|
*
|
2016-04-28 14:07:22 +00:00
|
|
|
* This is done in-place - on return @set is the up set. If it's
|
|
|
|
* empty, ->primary will remain undefined.
|
2014-03-24 15:12:47 +00:00
|
|
|
*/
|
2016-04-28 14:07:22 +00:00
|
|
|
static void raw_to_up_osds(struct ceph_osdmap *osdmap,
|
|
|
|
struct ceph_pg_pool_info *pi,
|
|
|
|
struct ceph_osds *set)
|
2014-03-24 15:12:47 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2016-04-28 14:07:22 +00:00
|
|
|
/* ->primary is undefined for a raw set */
|
|
|
|
BUG_ON(set->primary != -1);
|
|
|
|
|
|
|
|
if (ceph_can_shift_osds(pi)) {
|
2014-03-24 15:12:47 +00:00
|
|
|
int removed = 0;
|
|
|
|
|
2016-04-28 14:07:22 +00:00
|
|
|
/* shift left */
|
|
|
|
for (i = 0; i < set->size; i++) {
|
|
|
|
if (ceph_osd_is_down(osdmap, set->osds[i])) {
|
2014-03-24 15:12:47 +00:00
|
|
|
removed++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (removed)
|
2016-04-28 14:07:22 +00:00
|
|
|
set->osds[i - removed] = set->osds[i];
|
2014-03-24 15:12:47 +00:00
|
|
|
}
|
2016-04-28 14:07:22 +00:00
|
|
|
set->size -= removed;
|
|
|
|
if (set->size > 0)
|
|
|
|
set->primary = set->osds[0];
|
2014-03-24 15:12:47 +00:00
|
|
|
} else {
|
2016-04-28 14:07:22 +00:00
|
|
|
/* set down/dne devices to NONE */
|
|
|
|
for (i = set->size - 1; i >= 0; i--) {
|
|
|
|
if (ceph_osd_is_down(osdmap, set->osds[i]))
|
|
|
|
set->osds[i] = CRUSH_ITEM_NONE;
|
2014-03-24 15:12:47 +00:00
|
|
|
else
|
2016-04-28 14:07:22 +00:00
|
|
|
set->primary = set->osds[i];
|
2014-03-24 15:12:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-28 14:07:22 +00:00
|
|
|
static void apply_primary_affinity(struct ceph_osdmap *osdmap,
|
|
|
|
struct ceph_pg_pool_info *pi,
|
|
|
|
u32 pps,
|
|
|
|
struct ceph_osds *up)
|
2014-03-24 15:12:49 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int pos = -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do we have any non-default primary_affinity values for these
|
|
|
|
* osds?
|
|
|
|
*/
|
|
|
|
if (!osdmap->osd_primary_affinity)
|
|
|
|
return;
|
|
|
|
|
2016-04-28 14:07:22 +00:00
|
|
|
for (i = 0; i < up->size; i++) {
|
|
|
|
int osd = up->osds[i];
|
2014-04-10 14:09:41 +00:00
|
|
|
|
|
|
|
if (osd != CRUSH_ITEM_NONE &&
|
|
|
|
osdmap->osd_primary_affinity[osd] !=
|
2014-03-24 15:12:49 +00:00
|
|
|
CEPH_OSD_DEFAULT_PRIMARY_AFFINITY) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-04-28 14:07:22 +00:00
|
|
|
if (i == up->size)
|
2014-03-24 15:12:49 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pick the primary. Feed both the seed (for the pg) and the
|
|
|
|
* osd into the hash/rng so that a proportional fraction of an
|
|
|
|
* osd's pgs get rejected as primary.
|
|
|
|
*/
|
2016-04-28 14:07:22 +00:00
|
|
|
for (i = 0; i < up->size; i++) {
|
|
|
|
int osd = up->osds[i];
|
2014-03-24 15:12:49 +00:00
|
|
|
u32 aff;
|
|
|
|
|
|
|
|
if (osd == CRUSH_ITEM_NONE)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
aff = osdmap->osd_primary_affinity[osd];
|
|
|
|
if (aff < CEPH_OSD_MAX_PRIMARY_AFFINITY &&
|
|
|
|
(crush_hash32_2(CRUSH_HASH_RJENKINS1,
|
|
|
|
pps, osd) >> 16) >= aff) {
|
|
|
|
/*
|
|
|
|
* We chose not to use this primary. Note it
|
|
|
|
* anyway as a fallback in case we don't pick
|
|
|
|
* anyone else, but keep looking.
|
|
|
|
*/
|
|
|
|
if (pos < 0)
|
|
|
|
pos = i;
|
|
|
|
} else {
|
|
|
|
pos = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pos < 0)
|
|
|
|
return;
|
|
|
|
|
2016-04-28 14:07:22 +00:00
|
|
|
up->primary = up->osds[pos];
|
2014-03-24 15:12:49 +00:00
|
|
|
|
2016-04-28 14:07:22 +00:00
|
|
|
if (ceph_can_shift_osds(pi) && pos > 0) {
|
2014-03-24 15:12:49 +00:00
|
|
|
/* move the new primary to the front */
|
|
|
|
for (i = pos; i > 0; i--)
|
2016-04-28 14:07:22 +00:00
|
|
|
up->osds[i] = up->osds[i - 1];
|
|
|
|
up->osds[0] = up->primary;
|
2014-03-24 15:12:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-24 15:12:47 +00:00
|
|
|
/*
|
2016-04-28 14:07:22 +00:00
|
|
|
* Get pg_temp and primary_temp mappings for given PG.
|
2014-03-24 15:12:47 +00:00
|
|
|
*
|
2016-04-28 14:07:22 +00:00
|
|
|
* Note that a PG may have none, only pg_temp, only primary_temp or
|
|
|
|
* both pg_temp and primary_temp mappings. This means @temp isn't
|
|
|
|
* always a valid OSD set on return: in the "only primary_temp" case,
|
|
|
|
* @temp will have its ->primary >= 0 but ->size == 0.
|
2014-03-24 15:12:47 +00:00
|
|
|
*/
|
2016-04-28 14:07:22 +00:00
|
|
|
static void get_temp_osds(struct ceph_osdmap *osdmap,
|
|
|
|
struct ceph_pg_pool_info *pi,
|
2017-06-21 15:27:18 +00:00
|
|
|
const struct ceph_pg *pgid,
|
2016-04-28 14:07:22 +00:00
|
|
|
struct ceph_osds *temp)
|
2014-03-24 15:12:47 +00:00
|
|
|
{
|
|
|
|
struct ceph_pg_mapping *pg;
|
|
|
|
int i;
|
|
|
|
|
2016-04-28 14:07:22 +00:00
|
|
|
ceph_osds_init(temp);
|
2014-03-24 15:12:47 +00:00
|
|
|
|
|
|
|
/* pg_temp? */
|
2017-06-21 15:27:18 +00:00
|
|
|
pg = lookup_pg_mapping(&osdmap->pg_temp, pgid);
|
2014-03-24 15:12:47 +00:00
|
|
|
if (pg) {
|
|
|
|
for (i = 0; i < pg->pg_temp.len; i++) {
|
|
|
|
if (ceph_osd_is_down(osdmap, pg->pg_temp.osds[i])) {
|
2016-04-28 14:07:22 +00:00
|
|
|
if (ceph_can_shift_osds(pi))
|
2014-03-24 15:12:47 +00:00
|
|
|
continue;
|
2016-04-28 14:07:22 +00:00
|
|
|
|
|
|
|
temp->osds[temp->size++] = CRUSH_ITEM_NONE;
|
2014-03-24 15:12:47 +00:00
|
|
|
} else {
|
2016-04-28 14:07:22 +00:00
|
|
|
temp->osds[temp->size++] = pg->pg_temp.osds[i];
|
2014-03-24 15:12:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* apply pg_temp's primary */
|
2016-04-28 14:07:22 +00:00
|
|
|
for (i = 0; i < temp->size; i++) {
|
|
|
|
if (temp->osds[i] != CRUSH_ITEM_NONE) {
|
|
|
|
temp->primary = temp->osds[i];
|
2014-03-24 15:12:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-24 15:12:48 +00:00
|
|
|
/* primary_temp? */
|
2017-06-21 15:27:18 +00:00
|
|
|
pg = lookup_pg_mapping(&osdmap->primary_temp, pgid);
|
2014-03-24 15:12:48 +00:00
|
|
|
if (pg)
|
2016-04-28 14:07:22 +00:00
|
|
|
temp->primary = pg->primary_temp.osd;
|
2014-03-24 15:12:47 +00:00
|
|
|
}
|
|
|
|
|
2010-05-10 17:24:48 +00:00
|
|
|
/*
|
2016-04-28 14:07:22 +00:00
|
|
|
* Map a PG to its acting set as well as its up set.
|
2014-03-24 15:12:48 +00:00
|
|
|
*
|
2016-04-28 14:07:22 +00:00
|
|
|
* Acting set is used for data mapping purposes, while up set can be
|
|
|
|
* recorded for detecting interval changes and deciding whether to
|
|
|
|
* resend a request.
|
2010-05-10 17:24:48 +00:00
|
|
|
*/
|
2016-04-28 14:07:22 +00:00
|
|
|
void ceph_pg_to_up_acting_osds(struct ceph_osdmap *osdmap,
|
2017-06-15 14:30:56 +00:00
|
|
|
struct ceph_pg_pool_info *pi,
|
2016-04-28 14:07:22 +00:00
|
|
|
const struct ceph_pg *raw_pgid,
|
|
|
|
struct ceph_osds *up,
|
|
|
|
struct ceph_osds *acting)
|
2010-05-10 17:24:48 +00:00
|
|
|
{
|
2017-06-21 15:27:18 +00:00
|
|
|
struct ceph_pg pgid;
|
2014-03-24 15:12:48 +00:00
|
|
|
u32 pps;
|
2010-05-10 17:24:48 +00:00
|
|
|
|
2017-06-15 14:30:56 +00:00
|
|
|
WARN_ON(pi->id != raw_pgid->pool);
|
2017-06-21 15:27:18 +00:00
|
|
|
raw_pg_to_pg(pi, raw_pgid, &pgid);
|
2010-05-10 17:24:48 +00:00
|
|
|
|
2016-04-28 14:07:22 +00:00
|
|
|
pg_to_raw_osds(osdmap, pi, raw_pgid, up, &pps);
|
2017-06-21 15:27:18 +00:00
|
|
|
apply_upmap(osdmap, &pgid, up);
|
2016-04-28 14:07:22 +00:00
|
|
|
raw_to_up_osds(osdmap, pi, up);
|
|
|
|
apply_primary_affinity(osdmap, pi, pps, up);
|
2017-06-21 15:27:18 +00:00
|
|
|
get_temp_osds(osdmap, pi, &pgid, acting);
|
2016-04-28 14:07:22 +00:00
|
|
|
if (!acting->size) {
|
|
|
|
memcpy(acting->osds, up->osds, up->size * sizeof(up->osds[0]));
|
|
|
|
acting->size = up->size;
|
|
|
|
if (acting->primary == -1)
|
|
|
|
acting->primary = up->primary;
|
2014-03-24 15:12:48 +00:00
|
|
|
}
|
2016-04-28 14:07:22 +00:00
|
|
|
WARN_ON(!osds_valid(up) || !osds_valid(acting));
|
2010-05-10 17:24:48 +00:00
|
|
|
}
|
|
|
|
|
2017-06-15 14:30:53 +00:00
|
|
|
bool ceph_pg_to_primary_shard(struct ceph_osdmap *osdmap,
|
2017-06-15 14:30:56 +00:00
|
|
|
struct ceph_pg_pool_info *pi,
|
2017-06-15 14:30:53 +00:00
|
|
|
const struct ceph_pg *raw_pgid,
|
|
|
|
struct ceph_spg *spgid)
|
|
|
|
{
|
|
|
|
struct ceph_pg pgid;
|
|
|
|
struct ceph_osds up, acting;
|
|
|
|
int i;
|
|
|
|
|
2017-06-15 14:30:56 +00:00
|
|
|
WARN_ON(pi->id != raw_pgid->pool);
|
2017-06-15 14:30:53 +00:00
|
|
|
raw_pg_to_pg(pi, raw_pgid, &pgid);
|
|
|
|
|
|
|
|
if (ceph_can_shift_osds(pi)) {
|
|
|
|
spgid->pgid = pgid; /* struct */
|
|
|
|
spgid->shard = CEPH_SPG_NOSHARD;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-06-15 14:30:56 +00:00
|
|
|
ceph_pg_to_up_acting_osds(osdmap, pi, &pgid, &up, &acting);
|
2017-06-15 14:30:53 +00:00
|
|
|
for (i = 0; i < acting.size; i++) {
|
|
|
|
if (acting.osds[i] == acting.primary) {
|
|
|
|
spgid->pgid = pgid; /* struct */
|
|
|
|
spgid->shard = i;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-10-06 18:31:10 +00:00
|
|
|
/*
|
2016-04-28 14:07:23 +00:00
|
|
|
* Return acting primary for given PG, or -1 if none.
|
2009-10-06 18:31:10 +00:00
|
|
|
*/
|
2016-04-28 14:07:23 +00:00
|
|
|
int ceph_pg_to_acting_primary(struct ceph_osdmap *osdmap,
|
|
|
|
const struct ceph_pg *raw_pgid)
|
2009-10-06 18:31:10 +00:00
|
|
|
{
|
2017-06-15 14:30:56 +00:00
|
|
|
struct ceph_pg_pool_info *pi;
|
2016-04-28 14:07:22 +00:00
|
|
|
struct ceph_osds up, acting;
|
2009-10-06 18:31:10 +00:00
|
|
|
|
2017-06-15 14:30:56 +00:00
|
|
|
pi = ceph_pg_pool_by_id(osdmap, raw_pgid->pool);
|
|
|
|
if (!pi)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ceph_pg_to_up_acting_osds(osdmap, pi, raw_pgid, &up, &acting);
|
2016-04-28 14:07:22 +00:00
|
|
|
return acting.primary;
|
2009-10-06 18:31:10 +00:00
|
|
|
}
|
2016-04-28 14:07:23 +00:00
|
|
|
EXPORT_SYMBOL(ceph_pg_to_acting_primary);
|
2020-05-22 13:24:53 +00:00
|
|
|
|
|
|
|
static struct crush_loc_node *alloc_crush_loc(size_t type_name_len,
|
|
|
|
size_t name_len)
|
|
|
|
{
|
|
|
|
struct crush_loc_node *loc;
|
|
|
|
|
|
|
|
loc = kmalloc(sizeof(*loc) + type_name_len + name_len + 2, GFP_NOIO);
|
|
|
|
if (!loc)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
RB_CLEAR_NODE(&loc->cl_node);
|
|
|
|
return loc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_crush_loc(struct crush_loc_node *loc)
|
|
|
|
{
|
|
|
|
WARN_ON(!RB_EMPTY_NODE(&loc->cl_node));
|
|
|
|
|
|
|
|
kfree(loc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int crush_loc_compare(const struct crush_loc *loc1,
|
|
|
|
const struct crush_loc *loc2)
|
|
|
|
{
|
|
|
|
return strcmp(loc1->cl_type_name, loc2->cl_type_name) ?:
|
|
|
|
strcmp(loc1->cl_name, loc2->cl_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_RB_FUNCS2(crush_loc, struct crush_loc_node, cl_loc, crush_loc_compare,
|
|
|
|
RB_BYPTR, const struct crush_loc *, cl_node)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parses a set of <bucket type name>':'<bucket name> pairs separated
|
|
|
|
* by '|', e.g. "rack:foo1|rack:foo2|datacenter:bar".
|
|
|
|
*
|
|
|
|
* Note that @crush_location is modified by strsep().
|
|
|
|
*/
|
|
|
|
int ceph_parse_crush_location(char *crush_location, struct rb_root *locs)
|
|
|
|
{
|
|
|
|
struct crush_loc_node *loc;
|
|
|
|
const char *type_name, *name, *colon;
|
|
|
|
size_t type_name_len, name_len;
|
|
|
|
|
|
|
|
dout("%s '%s'\n", __func__, crush_location);
|
|
|
|
while ((type_name = strsep(&crush_location, "|"))) {
|
|
|
|
colon = strchr(type_name, ':');
|
|
|
|
if (!colon)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
type_name_len = colon - type_name;
|
|
|
|
if (type_name_len == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
name = colon + 1;
|
|
|
|
name_len = strlen(name);
|
|
|
|
if (name_len == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
loc = alloc_crush_loc(type_name_len, name_len);
|
|
|
|
if (!loc)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
loc->cl_loc.cl_type_name = loc->cl_data;
|
|
|
|
memcpy(loc->cl_loc.cl_type_name, type_name, type_name_len);
|
|
|
|
loc->cl_loc.cl_type_name[type_name_len] = '\0';
|
|
|
|
|
|
|
|
loc->cl_loc.cl_name = loc->cl_data + type_name_len + 1;
|
|
|
|
memcpy(loc->cl_loc.cl_name, name, name_len);
|
|
|
|
loc->cl_loc.cl_name[name_len] = '\0';
|
|
|
|
|
|
|
|
if (!__insert_crush_loc(locs, loc)) {
|
|
|
|
free_crush_loc(loc);
|
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
dout("%s type_name '%s' name '%s'\n", __func__,
|
|
|
|
loc->cl_loc.cl_type_name, loc->cl_loc.cl_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ceph_compare_crush_locs(struct rb_root *locs1, struct rb_root *locs2)
|
|
|
|
{
|
|
|
|
struct rb_node *n1 = rb_first(locs1);
|
|
|
|
struct rb_node *n2 = rb_first(locs2);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
for ( ; n1 && n2; n1 = rb_next(n1), n2 = rb_next(n2)) {
|
|
|
|
struct crush_loc_node *loc1 =
|
|
|
|
rb_entry(n1, struct crush_loc_node, cl_node);
|
|
|
|
struct crush_loc_node *loc2 =
|
|
|
|
rb_entry(n2, struct crush_loc_node, cl_node);
|
|
|
|
|
|
|
|
ret = crush_loc_compare(&loc1->cl_loc, &loc2->cl_loc);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!n1 && n2)
|
|
|
|
return -1;
|
|
|
|
if (n1 && !n2)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ceph_clear_crush_locs(struct rb_root *locs)
|
|
|
|
{
|
|
|
|
while (!RB_EMPTY_ROOT(locs)) {
|
|
|
|
struct crush_loc_node *loc =
|
|
|
|
rb_entry(rb_first(locs), struct crush_loc_node, cl_node);
|
|
|
|
|
|
|
|
erase_crush_loc(locs, loc);
|
|
|
|
free_crush_loc(loc);
|
|
|
|
}
|
|
|
|
}
|
2020-05-23 09:45:48 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* [a-zA-Z0-9-_.]+
|
|
|
|
*/
|
|
|
|
static bool is_valid_crush_name(const char *name)
|
|
|
|
{
|
|
|
|
do {
|
|
|
|
if (!('a' <= *name && *name <= 'z') &&
|
|
|
|
!('A' <= *name && *name <= 'Z') &&
|
|
|
|
!('0' <= *name && *name <= '9') &&
|
|
|
|
*name != '-' && *name != '_' && *name != '.')
|
|
|
|
return false;
|
|
|
|
} while (*++name != '\0');
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Gets the parent of an item. Returns its id (<0 because the
|
|
|
|
* parent is always a bucket), type id (>0 for the same reason,
|
|
|
|
* via @parent_type_id) and location (via @parent_loc). If no
|
|
|
|
* parent, returns 0.
|
|
|
|
*
|
|
|
|
* Does a linear search, as there are no parent pointers of any
|
2021-06-02 06:56:35 +00:00
|
|
|
* kind. Note that the result is ambiguous for items that occur
|
2020-05-23 09:45:48 +00:00
|
|
|
* multiple times in the map.
|
|
|
|
*/
|
|
|
|
static int get_immediate_parent(struct crush_map *c, int id,
|
|
|
|
u16 *parent_type_id,
|
|
|
|
struct crush_loc *parent_loc)
|
|
|
|
{
|
|
|
|
struct crush_bucket *b;
|
|
|
|
struct crush_name_node *type_cn, *cn;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < c->max_buckets; i++) {
|
|
|
|
b = c->buckets[i];
|
|
|
|
if (!b)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* ignore per-class shadow hierarchy */
|
|
|
|
cn = lookup_crush_name(&c->names, b->id);
|
|
|
|
if (!cn || !is_valid_crush_name(cn->cn_name))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (j = 0; j < b->size; j++) {
|
|
|
|
if (b->items[j] != id)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
*parent_type_id = b->type;
|
|
|
|
type_cn = lookup_crush_name(&c->type_names, b->type);
|
|
|
|
parent_loc->cl_type_name = type_cn->cn_name;
|
|
|
|
parent_loc->cl_name = cn->cn_name;
|
|
|
|
return b->id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0; /* no parent */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculates the locality/distance from an item to a client
|
|
|
|
* location expressed in terms of CRUSH hierarchy as a set of
|
|
|
|
* (bucket type name, bucket name) pairs. Specifically, looks
|
|
|
|
* for the lowest-valued bucket type for which the location of
|
|
|
|
* @id matches one of the locations in @locs, so for standard
|
|
|
|
* bucket types (host = 1, rack = 3, datacenter = 8, zone = 9)
|
|
|
|
* a matching host is closer than a matching rack and a matching
|
|
|
|
* data center is closer than a matching zone.
|
|
|
|
*
|
|
|
|
* Specifying multiple locations (a "multipath" location) such
|
|
|
|
* as "rack=foo1 rack=foo2 datacenter=bar" is allowed -- @locs
|
|
|
|
* is a multimap. The locality will be:
|
|
|
|
*
|
|
|
|
* - 3 for OSDs in racks foo1 and foo2
|
|
|
|
* - 8 for OSDs in data center bar
|
|
|
|
* - -1 for all other OSDs
|
|
|
|
*
|
|
|
|
* The lowest possible bucket type is 1, so the best locality
|
|
|
|
* for an OSD is 1 (i.e. a matching host). Locality 0 would be
|
|
|
|
* the OSD itself.
|
|
|
|
*/
|
|
|
|
int ceph_get_crush_locality(struct ceph_osdmap *osdmap, int id,
|
|
|
|
struct rb_root *locs)
|
|
|
|
{
|
|
|
|
struct crush_loc loc;
|
|
|
|
u16 type_id;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Instead of repeated get_immediate_parent() calls,
|
|
|
|
* the location of @id could be obtained with a single
|
|
|
|
* depth-first traversal.
|
|
|
|
*/
|
|
|
|
for (;;) {
|
|
|
|
id = get_immediate_parent(osdmap->crush, id, &type_id, &loc);
|
|
|
|
if (id >= 0)
|
|
|
|
return -1; /* not local */
|
|
|
|
|
|
|
|
if (lookup_crush_loc(locs, &loc))
|
|
|
|
return type_id;
|
|
|
|
}
|
|
|
|
}
|