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:
Krzysztof Karas 2022-10-24 12:53:22 +00:00 committed by Tomasz Zawadzki
parent a1c7ae2d3f
commit b5bdbbb959
8 changed files with 144 additions and 4 deletions

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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)

View File

@ -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)