diff --git a/include/spdk/bdev.h b/include/spdk/bdev.h index 64dcb5467..aa65c6b32 100644 --- a/include/spdk/bdev.h +++ b/include/spdk/bdev.h @@ -497,6 +497,14 @@ const char *spdk_bdev_get_name(const struct spdk_bdev *bdev); */ const char *spdk_bdev_get_product_name(const struct spdk_bdev *bdev); +/** + * Get block device creation time. + * + * \param bdev Block device to query. + * \return Creation time of bdev as a null-terminated string, or NULL if not present. + */ +const char *spdk_bdev_get_creation_time(const struct spdk_bdev *bdev); + /** * Get block device logical block size. * diff --git a/include/spdk/bdev_module.h b/include/spdk/bdev_module.h index 3d6038932..e37b1e228 100644 --- a/include/spdk/bdev_module.h +++ b/include/spdk/bdev_module.h @@ -513,6 +513,13 @@ struct spdk_bdev { */ struct spdk_uuid uuid; + /** + * Creation time for this bdev. + * + * If not provided, it will be NULL. + */ + const char *creation_time; + /** Size in bytes of a metadata for the backend */ uint32_t md_len; diff --git a/include/spdk/util.h b/include/spdk/util.h index 709363f6f..cf6965897 100644 --- a/include/spdk/util.h +++ b/include/spdk/util.h @@ -322,6 +322,25 @@ spdk_is_divisible_by(uint64_t dividend, uint64_t divisor) return (dividend & (divisor - 1)) == 0; } +/* + * Get the UTC time string in RFC3339 format. + * + * \param buf Buffer to store the UTC time string. + * \param buf_size Size of the buffer. + * + * \return void + */ +static inline void +spdk_current_utc_time_rfc3339(char *buf, size_t buf_size) { + struct tm *utc; + time_t rawtime; + + time(&rawtime); + utc = gmtime(&rawtime); + + strftime(buf, buf_size, "%Y-%m-%dT%H:%M:%SZ", utc); +} + #ifdef __cplusplus } #endif diff --git a/include/spdk_internal/lvolstore.h b/include/spdk_internal/lvolstore.h index 1de274e08..ec0e0871d 100644 --- a/include/spdk_internal/lvolstore.h +++ b/include/spdk_internal/lvolstore.h @@ -16,6 +16,9 @@ /* Default size of blobstore cluster */ #define SPDK_LVS_OPTS_CLUSTER_SZ (4 * 1024 * 1024) +/* Creation time format in RFC 3339 format */ +#define SPDK_CREATION_TIME_MAX 21 /* 20 characters + null terminator */ + /* UUID + '_' + blobid (20 characters for uint64_t). * Null terminator is already included in SPDK_UUID_STRING_LEN. */ #define SPDK_LVOL_UNIQUE_ID_MAX (SPDK_UUID_STRING_LEN + 1 + 20) @@ -106,6 +109,7 @@ struct spdk_lvol { char name[SPDK_LVOL_NAME_MAX]; struct spdk_uuid uuid; char uuid_str[SPDK_UUID_STRING_LEN]; + char creation_time[SPDK_CREATION_TIME_MAX]; struct spdk_bdev *bdev; int ref_count; bool action_in_progress; diff --git a/lib/bdev/bdev.c b/lib/bdev/bdev.c index 4e3bed7a0..03f224304 100644 --- a/lib/bdev/bdev.c +++ b/lib/bdev/bdev.c @@ -4569,6 +4569,12 @@ spdk_bdev_get_aliases(const struct spdk_bdev *bdev) return &bdev->aliases; } +const char * +spdk_bdev_get_creation_time(const struct spdk_bdev *bdev) +{ + return bdev->creation_time; +} + uint32_t spdk_bdev_get_block_size(const struct spdk_bdev *bdev) { diff --git a/lib/bdev/bdev_rpc.c b/lib/bdev/bdev_rpc.c index a6769b820..7003e1ed1 100644 --- a/lib/bdev/bdev_rpc.c +++ b/lib/bdev/bdev_rpc.c @@ -664,6 +664,7 @@ rpc_dump_bdev_info(void *ctx, struct spdk_bdev *bdev) uint64_t qos_limits[SPDK_BDEV_QOS_NUM_RATE_LIMIT_TYPES]; struct spdk_memory_domain **domains; char uuid_str[SPDK_UUID_STRING_LEN]; + const char *creation_time_str; int i, rc; spdk_json_write_object_begin(w); @@ -687,6 +688,12 @@ rpc_dump_bdev_info(void *ctx, struct spdk_bdev *bdev) spdk_uuid_fmt_lower(uuid_str, sizeof(uuid_str), &bdev->uuid); spdk_json_write_named_string(w, "uuid", uuid_str); + creation_time_str = spdk_bdev_get_creation_time(bdev); + if (creation_time_str == NULL) { + creation_time_str = ""; + } + spdk_json_write_named_string(w, "creation_time", creation_time_str); + if (spdk_bdev_get_md_size(bdev) != 0) { spdk_json_write_named_uint32(w, "md_size", spdk_bdev_get_md_size(bdev)); spdk_json_write_named_bool(w, "md_interleave", spdk_bdev_is_md_interleaved(bdev)); diff --git a/lib/lvol/lvol.c b/lib/lvol/lvol.c index ee83a6c18..4cea92c4d 100644 --- a/lib/lvol/lvol.c +++ b/lib/lvol/lvol.c @@ -16,6 +16,7 @@ #define SPDK_LVOL_BLOB_OPTS_CHANNEL_OPS 512 #define LVOL_NAME "name" +#define LVOL_CREATION_TIME "creation_time" SPDK_LOG_REGISTER_COMPONENT(lvol) @@ -114,6 +115,7 @@ lvol_alloc(struct spdk_lvol_store *lvs, const char *name, bool thin_provision, spdk_uuid_generate(&lvol->uuid); spdk_uuid_fmt_lower(lvol->uuid_str, sizeof(lvol->uuid_str), &lvol->uuid); spdk_uuid_fmt_lower(lvol->unique_id, sizeof(lvol->uuid_str), &lvol->uuid); + spdk_current_utc_time_rfc3339(lvol->creation_time, sizeof(lvol->creation_time)); TAILQ_INSERT_TAIL(&lvs->pending_lvols, lvol, link); @@ -278,6 +280,11 @@ load_next_lvol(void *cb_arg, struct spdk_blob *blob, int lvolerrno) snprintf(lvol->name, sizeof(lvol->name), "%s", attr); + rc = spdk_blob_get_xattr_value(blob, "creation_time", (const void **)&attr, &value_len); + if (rc == 0 && value_len <= SPDK_CREATION_TIME_MAX) { + snprintf(lvol->creation_time, sizeof(lvol->creation_time), "%s", attr); + } + TAILQ_INSERT_TAIL(&lvs->lvols, lvol, link); lvs->lvol_count++; @@ -1139,6 +1146,12 @@ lvol_get_xattr_value(void *xattr_ctx, const char *name, *value_len = sizeof(lvol->uuid_str); return; } + if (!strcmp(LVOL_CREATION_TIME, name)) { + *value = lvol->creation_time; + *value_len = sizeof(lvol->creation_time); + return; + } + *value = NULL; *value_len = 0; } @@ -1184,7 +1197,7 @@ spdk_lvol_create(struct spdk_lvol_store *lvs, const char *name, uint64_t sz, struct spdk_blob_store *bs; struct spdk_lvol *lvol; struct spdk_blob_opts opts; - char *xattr_names[] = {LVOL_NAME, "uuid"}; + char *xattr_names[] = {LVOL_NAME, "uuid", LVOL_CREATION_TIME}; int rc; if (lvs == NULL) { @@ -1239,7 +1252,7 @@ spdk_lvol_create_esnap_clone(const void *esnap_id, uint32_t id_len, uint64_t siz struct spdk_lvol *lvol; struct spdk_blob_opts opts; uint64_t cluster_sz; - char *xattr_names[] = {LVOL_NAME, "uuid"}; + char *xattr_names[] = {LVOL_NAME, "uuid", LVOL_CREATION_TIME}; int rc; if (lvs == NULL) { @@ -1303,7 +1316,7 @@ spdk_lvol_create_snapshot(struct spdk_lvol *origlvol, const char *snapshot_name, struct spdk_blob *origblob; struct spdk_lvol_with_handle_req *req; struct spdk_blob_xattr_opts snapshot_xattrs; - char *xattr_names[] = {LVOL_NAME, "uuid"}; + char *xattr_names[] = {LVOL_NAME, "uuid", LVOL_CREATION_TIME}; int rc; if (origlvol == NULL) { @@ -1364,7 +1377,7 @@ spdk_lvol_create_clone(struct spdk_lvol *origlvol, const char *clone_name, struct spdk_lvol_store *lvs; struct spdk_blob *origblob; struct spdk_blob_xattr_opts clone_xattrs; - char *xattr_names[] = {LVOL_NAME, "uuid"}; + char *xattr_names[] = {LVOL_NAME, "uuid", LVOL_CREATION_TIME}; int rc; if (origlvol == NULL) { diff --git a/module/bdev/lvol/vbdev_lvol.c b/module/bdev/lvol/vbdev_lvol.c index 437ef9034..2273f82d4 100644 --- a/module/bdev/lvol/vbdev_lvol.c +++ b/module/bdev/lvol/vbdev_lvol.c @@ -1144,6 +1144,7 @@ _create_lvol_disk(struct spdk_lvol *lvol, bool destroy) assert((total_size % bdev->blocklen) == 0); bdev->blockcnt = total_size / bdev->blocklen; bdev->uuid = lvol->uuid; + bdev->creation_time = lvol->creation_time; bdev->required_alignment = lvs_bdev->bdev->required_alignment; bdev->split_on_optimal_io_boundary = true; bdev->optimal_io_boundary = spdk_bs_get_cluster_size(lvol->lvol_store->blobstore) / bdev->blocklen; diff --git a/module/bdev/lvol/vbdev_lvol_rpc.c b/module/bdev/lvol/vbdev_lvol_rpc.c index fa78a7372..f26342704 100644 --- a/module/bdev/lvol/vbdev_lvol_rpc.c +++ b/module/bdev/lvol/vbdev_lvol_rpc.c @@ -1350,6 +1350,7 @@ rpc_dump_lvol(struct spdk_json_write_ctx *w, struct spdk_lvol *lvol) spdk_json_write_named_string_fmt(w, "alias", "%s/%s", lvs->name, lvol->name); spdk_json_write_named_string(w, "uuid", lvol->uuid_str); spdk_json_write_named_string(w, "name", lvol->name); + spdk_json_write_named_string(w, "creation_time", lvol->creation_time); spdk_json_write_named_bool(w, "is_thin_provisioned", spdk_blob_is_thin_provisioned(lvol->blob)); spdk_json_write_named_bool(w, "is_snapshot", spdk_blob_is_snapshot(lvol->blob)); spdk_json_write_named_bool(w, "is_clone", spdk_blob_is_clone(lvol->blob));