bdev: enable bdevs based on physical device to generate UUID
Add an option "--generate-uuids" to bdev_nvme_set_options RPC to enable generation of UUIDs for NVMes devices that do not provide this value themselves. The identifier is based on a serial number of the device, so a bdev using this NVMe will always be assigned the same UUID. Part of enhancement from #2516. Change-Id: I86d76274e5702d14ace89d83d1e9129573f543e2 Signed-off-by: Krzysztof Karas <krzysztof.karas@intel.com> Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15151 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Community-CI: Mellanox Build Bot
This commit is contained in:
parent
a1c7ae2d3f
commit
b5bdbbb959
@ -27,6 +27,12 @@ New `spdk_bdev_copy_blocks` and `spdk_bdev_get_max_copy` APIs to support copy co
|
||||
|
||||
A new API `spdk_bdev_io_get_submit_tsc` was added to get the submit_tsc of the bdev I/O.
|
||||
|
||||
Bdevs will no longer have UUIDs generated based on timestamp and are responsible for
|
||||
setting this field themselves. Generation of UUIDs for NVMe bdevs may be enabled by
|
||||
running `bdev_nvme_set_options` RPC with `--generate-uuids` option. These identifiers
|
||||
are based on serial number and namespace ID and will always be the same for a given
|
||||
device.
|
||||
|
||||
### event
|
||||
|
||||
Added core lock file mechanism to prevent the same CPU cores from being used by multiple
|
||||
|
@ -3317,6 +3317,7 @@ ctrlr_loss_timeout_sec | Optional | number | Time to wait until ctrlr i
|
||||
reconnect_delay_sec | Optional | number | Time to delay a reconnect trial. 0 means no reconnect.
|
||||
fast_io_fail_timeout_sec | Optional | number | Time to wait until ctrlr is reconnected before failing I/O to ctrlr. 0 means no such timeout.
|
||||
disable_auto_failback | Optional | boolean | Disable automatic failback. The RPC bdev_nvme_set_preferred_path can be used to do manual failback.
|
||||
generate_uuids | Optional | boolean | Enable generation of UUIDs for NVMe bdevs that do not provide this value themselves.
|
||||
|
||||
#### Example
|
||||
|
||||
|
@ -380,8 +380,7 @@ struct spdk_bdev {
|
||||
/**
|
||||
* UUID for this bdev.
|
||||
*
|
||||
* Fill with zeroes if no uuid is available. The bdev layer
|
||||
* will automatically populate this if necessary.
|
||||
* Fill with zeroes if no uuid is available.
|
||||
*/
|
||||
struct spdk_uuid uuid;
|
||||
|
||||
|
@ -32,6 +32,8 @@
|
||||
#define SPDK_BDEV_NVME_DEFAULT_DELAY_CMD_SUBMIT true
|
||||
#define SPDK_BDEV_NVME_DEFAULT_KEEP_ALIVE_TIMEOUT_IN_MS (10000)
|
||||
|
||||
#define NSID_STR_LEN 10
|
||||
|
||||
static int bdev_nvme_config_json(struct spdk_json_write_ctx *w);
|
||||
|
||||
struct nvme_bdev_io {
|
||||
@ -120,6 +122,7 @@ static struct spdk_bdev_nvme_opts g_opts = {
|
||||
.reconnect_delay_sec = 0,
|
||||
.fast_io_fail_timeout_sec = 0,
|
||||
.disable_auto_failback = false,
|
||||
.generate_uuids = false,
|
||||
};
|
||||
|
||||
#define NVME_HOTPLUG_POLL_PERIOD_MAX 10000000ULL
|
||||
@ -2907,6 +2910,120 @@ nvme_ns_set_ana_state(const struct spdk_nvme_ana_group_descriptor *desc, void *c
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
merge_nsid_sn_strings(const char *sn, char *nsid, int8_t *out)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
int sn_len = strlen(sn), nsid_len = strlen(nsid);
|
||||
|
||||
for (i = 0; i < nsid_len; i++) {
|
||||
out[i] = nsid[i];
|
||||
}
|
||||
|
||||
/* Since last few characters are more likely to be unique,
|
||||
* even among the devices from the same manufacturer,
|
||||
* we use serial number in reverse. We also skip the
|
||||
* terminating character of serial number string. */
|
||||
for (j = sn_len - 1; j >= 0; j--) {
|
||||
if (i == SPDK_UUID_STRING_LEN - 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* There may be a lot of spaces in serial number string
|
||||
* and they will generate equally large number of the
|
||||
* same character, so just skip them. */
|
||||
if (sn[j] == ' ') {
|
||||
continue;
|
||||
}
|
||||
|
||||
out[i] = sn[j];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dictionary of characters for UUID generation. */
|
||||
static char dict[17] = "0123456789abcdef";
|
||||
|
||||
static struct spdk_uuid
|
||||
nvme_generate_uuid(const char *sn, uint32_t nsid)
|
||||
{
|
||||
struct spdk_uuid new_uuid;
|
||||
char buf[SPDK_UUID_STRING_LEN] = {'\0'}, merged_str[SPDK_UUID_STRING_LEN] = {'\0'};
|
||||
char nsid_str[NSID_STR_LEN] = {'\0'}, tmp;
|
||||
uint64_t i = 0, j = 0, rem, dict_size = strlen(dict);
|
||||
int rc;
|
||||
|
||||
assert(strlen(sn) <= SPDK_NVME_CTRLR_SN_LEN);
|
||||
|
||||
snprintf(nsid_str, NSID_STR_LEN, "%" PRIu32, nsid);
|
||||
|
||||
merge_nsid_sn_strings(sn, nsid_str, merged_str);
|
||||
|
||||
while (i < SPDK_UUID_STRING_LEN) {
|
||||
/* If 'j' is equal to indexes, where '-' should be placed,
|
||||
* insert this character and continue the loop without
|
||||
* increasing 'i'. */
|
||||
if ((j == 8 || j == 13 || j == 18 || j == 23)) {
|
||||
buf[j] = '-';
|
||||
j++;
|
||||
|
||||
/* Break, if we ran out of characters in
|
||||
* serial number and namespace ID string. */
|
||||
if (j == strlen(merged_str)) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Change character in shuffled string to lower case. */
|
||||
tmp = tolower(merged_str[i]);
|
||||
|
||||
if (isxdigit(tmp)) {
|
||||
/* If character can be represented by a hex
|
||||
* value as is, copy it to the result buffer. */
|
||||
buf[j] = tmp;
|
||||
} else {
|
||||
/* Otherwise get its code and divide it
|
||||
* by the number of elements in dictionary.
|
||||
* The remainder will be the index of dictionary
|
||||
* character to replace tmp value with. */
|
||||
rem = tmp % dict_size;
|
||||
buf[j] = dict[rem];
|
||||
}
|
||||
|
||||
i++;
|
||||
j++;
|
||||
|
||||
/* Break, if we ran out of characters in
|
||||
* serial number and namespace ID string. */
|
||||
if (j == strlen(merged_str)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If there are not enough values to fill UUID,
|
||||
* the rest is taken from dictionary characters. */
|
||||
i = 0;
|
||||
while (j < SPDK_UUID_STRING_LEN - 1) {
|
||||
if ((j == 8 || j == 13 || j == 18 || j == 23)) {
|
||||
buf[j] = '-';
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
buf[j] = dict[i % dict_size];
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
|
||||
rc = spdk_uuid_parse(&new_uuid, buf);
|
||||
if (rc != 0) {
|
||||
SPDK_ERRLOG("Unexpected spdk_uuid_parse failure on %s.\n", buf);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return new_uuid;
|
||||
}
|
||||
|
||||
static int
|
||||
nvme_disk_create(struct spdk_bdev *disk, const char *base_name,
|
||||
struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns,
|
||||
@ -2919,6 +3036,7 @@ nvme_disk_create(struct spdk_bdev *disk, const char *base_name,
|
||||
const struct spdk_nvme_ctrlr_opts *opts;
|
||||
enum spdk_nvme_csi csi;
|
||||
uint32_t atomic_bs, phys_bs, bs;
|
||||
char sn_tmp[SPDK_NVME_CTRLR_SN_LEN + 1] = {'\0'};
|
||||
|
||||
cdata = spdk_nvme_ctrlr_get_data(ctrlr);
|
||||
csi = spdk_nvme_ns_get_csi(ns);
|
||||
@ -2974,6 +3092,9 @@ nvme_disk_create(struct spdk_bdev *disk, const char *base_name,
|
||||
uuid = spdk_nvme_ns_get_uuid(ns);
|
||||
if (uuid) {
|
||||
disk->uuid = *uuid;
|
||||
} else if (g_opts.generate_uuids) {
|
||||
spdk_strcpy_pad(sn_tmp, cdata->sn, SPDK_NVME_CTRLR_SN_LEN + 1, '\0');
|
||||
disk->uuid = nvme_generate_uuid(sn_tmp, spdk_nvme_ns_get_id(ns));
|
||||
}
|
||||
} else {
|
||||
memcpy(&disk->uuid, nguid, sizeof(disk->uuid));
|
||||
@ -6579,6 +6700,7 @@ bdev_nvme_opts_config_json(struct spdk_json_write_ctx *w)
|
||||
spdk_json_write_named_int32(w, "ctrlr_loss_timeout_sec", g_opts.ctrlr_loss_timeout_sec);
|
||||
spdk_json_write_named_uint32(w, "reconnect_delay_sec", g_opts.reconnect_delay_sec);
|
||||
spdk_json_write_named_uint32(w, "fast_io_fail_timeout_sec", g_opts.fast_io_fail_timeout_sec);
|
||||
spdk_json_write_named_bool(w, "generate_uuids", g_opts.generate_uuids);
|
||||
spdk_json_write_object_end(w);
|
||||
|
||||
spdk_json_write_object_end(w);
|
||||
|
@ -250,6 +250,7 @@ struct spdk_bdev_nvme_opts {
|
||||
uint32_t reconnect_delay_sec;
|
||||
uint32_t fast_io_fail_timeout_sec;
|
||||
bool disable_auto_failback;
|
||||
bool generate_uuids;
|
||||
};
|
||||
|
||||
struct spdk_nvme_qpair *bdev_nvme_get_io_qpair(struct spdk_io_channel *ctrlr_io_ch);
|
||||
|
@ -68,6 +68,7 @@ static const struct spdk_json_object_decoder rpc_bdev_nvme_options_decoders[] =
|
||||
{"reconnect_delay_sec", offsetof(struct spdk_bdev_nvme_opts, reconnect_delay_sec), spdk_json_decode_uint32, true},
|
||||
{"fast_io_fail_timeout_sec", offsetof(struct spdk_bdev_nvme_opts, fast_io_fail_timeout_sec), spdk_json_decode_uint32, true},
|
||||
{"disable_auto_failback", offsetof(struct spdk_bdev_nvme_opts, disable_auto_failback), spdk_json_decode_bool, true},
|
||||
{"generate_uuids", offsetof(struct spdk_bdev_nvme_opts, generate_uuids), spdk_json_decode_bool, true},
|
||||
};
|
||||
|
||||
static void
|
||||
|
@ -521,7 +521,7 @@ def bdev_nvme_set_options(client, action_on_timeout=None, timeout_us=None, timeo
|
||||
nvme_adminq_poll_period_us=None, nvme_ioq_poll_period_us=None, io_queue_requests=None,
|
||||
delay_cmd_submit=None, transport_retry_count=None, bdev_retry_count=None,
|
||||
transport_ack_timeout=None, ctrlr_loss_timeout_sec=None, reconnect_delay_sec=None,
|
||||
fast_io_fail_timeout_sec=None, disable_auto_failback=None):
|
||||
fast_io_fail_timeout_sec=None, disable_auto_failback=None, generate_uuids=None):
|
||||
"""Set options for the bdev nvme. This is startup command.
|
||||
|
||||
Args:
|
||||
@ -559,6 +559,8 @@ def bdev_nvme_set_options(client, action_on_timeout=None, timeout_us=None, timeo
|
||||
This can be overridden by bdev_nvme_attach_controller. (optional)
|
||||
disable_auto_failback: Disable automatic failback. bdev_nvme_set_preferred_path can be used to do manual failback.
|
||||
By default, immediately failback to the preferred I/O path if it is restored. (optional)
|
||||
generate_uuids: Enable generation of unique identifiers for NVMe bdevs only if they do not provide UUID themselves.
|
||||
These strings are based on device serial number and namespace ID and will always be the same for that device.
|
||||
|
||||
"""
|
||||
params = {}
|
||||
@ -624,6 +626,9 @@ def bdev_nvme_set_options(client, action_on_timeout=None, timeout_us=None, timeo
|
||||
if disable_auto_failback is not None:
|
||||
params['disable_auto_failback'] = disable_auto_failback
|
||||
|
||||
if generate_uuids is not None:
|
||||
params['generate_uuids'] = generate_uuids
|
||||
|
||||
return client.call('bdev_nvme_set_options', params)
|
||||
|
||||
|
||||
|
@ -556,7 +556,8 @@ if __name__ == "__main__":
|
||||
ctrlr_loss_timeout_sec=args.ctrlr_loss_timeout_sec,
|
||||
reconnect_delay_sec=args.reconnect_delay_sec,
|
||||
fast_io_fail_timeout_sec=args.fast_io_fail_timeout_sec,
|
||||
disable_auto_failback=args.disable_auto_failback)
|
||||
disable_auto_failback=args.disable_auto_failback,
|
||||
generate_uuids=args.generate_uuids)
|
||||
|
||||
p = subparsers.add_parser('bdev_nvme_set_options',
|
||||
help='Set options for the bdev nvme type. This is startup command.')
|
||||
@ -621,6 +622,10 @@ if __name__ == "__main__":
|
||||
help="""Disable automatic failback. bdev_nvme_set_preferred_path can be used to do manual failback.
|
||||
By default, immediately failback to the preferred I/O path if it restored.""",
|
||||
action='store_true')
|
||||
p.add_argument('--generate-uuids',
|
||||
help="""Enable generation of unique identifiers for NVMe bdevs only if they do
|
||||
not provide UUID themselves. These strings are based on device serial number and
|
||||
namespace ID and will always be the same for that device.""", action='store_true')
|
||||
|
||||
p.set_defaults(func=bdev_nvme_set_options)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user