lib/bdev: media management events

Media management event was introduced.  It's sent out to notify that
some portion of the data needs to be rewritten (e.g. due to data
refresh, wear leveling, high error rate, etc.).  This type of
notification is only utilized by devices exposing raw access to the
physical medium (e.g. Open Channel SSDs).

Change-Id: Ia30faa5866d71fd597009b441f69c609de974161
Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com>
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/471460
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: Broadcom SPDK FC-NVMe CI <spdk-ci.pdl@broadcom.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
This commit is contained in:
Konrad Sztyber 2019-10-01 10:57:24 +02:00 committed by Tomasz Zawadzki
parent 64fe514efa
commit a69f90ddd8
3 changed files with 158 additions and 1 deletions

View File

@ -63,7 +63,14 @@ extern "C" {
/** Asynchronous event type */
enum spdk_bdev_event_type {
SPDK_BDEV_EVENT_REMOVE,
SPDK_BDEV_EVENT_RESIZE
SPDK_BDEV_EVENT_RESIZE,
SPDK_BDEV_EVENT_MEDIA_MANAGEMENT,
};
/** Media management event details */
struct spdk_bdev_media_event {
uint64_t offset;
uint64_t num_blocks;
};
/**
@ -1458,6 +1465,20 @@ void spdk_bdev_histogram_get(struct spdk_bdev *bdev, struct spdk_histogram_data
spdk_bdev_histogram_data_cb cb_fn,
void *cb_arg);
/**
* Retrieves media events. Can only be called from the context of
* SPDK_BDEV_EVENT_MEDIA_MANAGEMENT event callback. These events are sent by
* devices exposing raw access to the physical medium (e.g. Open Channel SSD).
*
* \param bdev_desc Block device descriptor
* \param events Array of media mangement event descriptors
* \param max_events Size of the events array
*
* \return number of events retrieved
*/
size_t spdk_bdev_get_media_events(struct spdk_bdev_desc *bdev_desc,
struct spdk_bdev_media_event *events, size_t max_events);
#ifdef __cplusplus
}
#endif

View File

@ -355,6 +355,11 @@ struct spdk_bdev {
*/
uint32_t optimal_open_zones;
/**
* Specifies whether bdev supports media management events.
*/
bool media_events;
/**
* Pointer to the bdev module that registered this bdev.
*/
@ -1110,6 +1115,27 @@ struct spdk_bdev *spdk_bdev_part_get_base_bdev(struct spdk_bdev_part *part);
*/
uint64_t spdk_bdev_part_get_offset_blocks(struct spdk_bdev_part *part);
/**
* Push media management events. To send the notification that new events are
* available, spdk_bdev_notify_media_management needs to be called.
*
* \param bdev Block device
* \param events Array of media events
* \param num_events Size of the events array
*
* \return number of events pushed or negative errno in case of failure
*/
int spdk_bdev_push_media_events(struct spdk_bdev *bdev, const struct spdk_bdev_media_event *events,
size_t num_events);
/**
* Send SPDK_BDEV_EVENT_MEDIA_MANAGEMENT to all open descriptors that have
* pending media events.
*
* \param bdev Block device
*/
void spdk_bdev_notify_media_management(struct spdk_bdev *bdev);
/*
* Macro used to register module for later initialization.
*/

View File

@ -278,6 +278,13 @@ struct spdk_bdev_channel {
bdev_io_tailq_t queued_resets;
};
struct media_event_entry {
struct spdk_bdev_media_event event;
TAILQ_ENTRY(media_event_entry) tailq;
};
#define MEDIA_EVENT_POOL_SIZE 64
struct spdk_bdev_desc {
struct spdk_bdev *bdev;
struct spdk_thread *thread;
@ -293,6 +300,9 @@ struct spdk_bdev_desc {
bool write;
pthread_mutex_t mutex;
uint32_t refs;
TAILQ_HEAD(, media_event_entry) pending_media_events;
TAILQ_HEAD(, media_event_entry) free_media_events;
struct media_event_entry *media_events_buffer;
TAILQ_ENTRY(spdk_bdev_desc) link;
uint64_t timeout_in_sec;
@ -2136,6 +2146,7 @@ static void
bdev_desc_free(struct spdk_bdev_desc *desc)
{
pthread_mutex_destroy(&desc->mutex);
free(desc->media_events_buffer);
free(desc);
}
@ -4606,6 +4617,9 @@ spdk_bdev_open(struct spdk_bdev *bdev, bool write, spdk_bdev_remove_cb_t remove_
remove_cb = bdev_dummy_event_cb;
}
TAILQ_INIT(&desc->pending_media_events);
TAILQ_INIT(&desc->free_media_events);
desc->callback.open_with_ext = false;
desc->callback.remove_fn = remove_cb;
desc->callback.ctx = remove_ctx;
@ -4632,6 +4646,7 @@ spdk_bdev_open_ext(const char *bdev_name, bool write, spdk_bdev_event_cb_t event
{
struct spdk_bdev_desc *desc;
struct spdk_bdev *bdev;
unsigned int event_id;
int rc;
if (event_cb == NULL) {
@ -4656,11 +4671,30 @@ spdk_bdev_open_ext(const char *bdev_name, bool write, spdk_bdev_event_cb_t event
return -ENOMEM;
}
TAILQ_INIT(&desc->pending_media_events);
TAILQ_INIT(&desc->free_media_events);
desc->callback.open_with_ext = true;
desc->callback.event_fn = event_cb;
desc->callback.ctx = event_ctx;
pthread_mutex_init(&desc->mutex, NULL);
if (bdev->media_events) {
desc->media_events_buffer = calloc(MEDIA_EVENT_POOL_SIZE,
sizeof(*desc->media_events_buffer));
if (desc->media_events_buffer == NULL) {
SPDK_ERRLOG("Failed to initialize media event pool\n");
bdev_desc_free(desc);
pthread_mutex_unlock(&g_bdev_mgr.mutex);
return -ENOMEM;
}
for (event_id = 0; event_id < MEDIA_EVENT_POOL_SIZE; ++event_id) {
TAILQ_INSERT_TAIL(&desc->free_media_events,
&desc->media_events_buffer[event_id], tailq);
}
}
rc = bdev_open(bdev, write, desc);
if (rc != 0) {
bdev_desc_free(desc);
@ -5314,6 +5348,82 @@ spdk_bdev_histogram_get(struct spdk_bdev *bdev, struct spdk_histogram_data *hist
bdev_histogram_get_channel_cb);
}
size_t
spdk_bdev_get_media_events(struct spdk_bdev_desc *desc, struct spdk_bdev_media_event *events,
size_t max_events)
{
struct media_event_entry *entry;
size_t num_events = 0;
for (; num_events < max_events; ++num_events) {
entry = TAILQ_FIRST(&desc->pending_media_events);
if (entry == NULL) {
break;
}
events[num_events] = entry->event;
TAILQ_REMOVE(&desc->pending_media_events, entry, tailq);
TAILQ_INSERT_TAIL(&desc->free_media_events, entry, tailq);
}
return num_events;
}
int
spdk_bdev_push_media_events(struct spdk_bdev *bdev, const struct spdk_bdev_media_event *events,
size_t num_events)
{
struct spdk_bdev_desc *desc;
struct media_event_entry *entry;
size_t event_id;
int rc = 0;
assert(bdev->media_events);
pthread_mutex_lock(&bdev->internal.mutex);
TAILQ_FOREACH(desc, &bdev->internal.open_descs, link) {
if (desc->write) {
break;
}
}
if (desc == NULL || desc->media_events_buffer == NULL) {
rc = -ENODEV;
goto out;
}
for (event_id = 0; event_id < num_events; ++event_id) {
entry = TAILQ_FIRST(&desc->free_media_events);
if (entry == NULL) {
break;
}
TAILQ_REMOVE(&desc->free_media_events, entry, tailq);
TAILQ_INSERT_TAIL(&desc->pending_media_events, entry, tailq);
entry->event = events[event_id];
}
rc = event_id;
out:
pthread_mutex_unlock(&bdev->internal.mutex);
return rc;
}
void
spdk_bdev_notify_media_management(struct spdk_bdev *bdev)
{
struct spdk_bdev_desc *desc;
pthread_mutex_lock(&bdev->internal.mutex);
TAILQ_FOREACH(desc, &bdev->internal.open_descs, link) {
if (!TAILQ_EMPTY(&desc->pending_media_events)) {
desc->callback.event_fn(SPDK_BDEV_EVENT_MEDIA_MANAGEMENT, bdev,
desc->callback.ctx);
}
}
pthread_mutex_unlock(&bdev->internal.mutex);
}
SPDK_LOG_REGISTER_COMPONENT("bdev", SPDK_LOG_BDEV)
SPDK_TRACE_REGISTER_FN(bdev_trace, "bdev", TRACE_GROUP_BDEV)