diff --git a/include/spdk/bdev.h b/include/spdk/bdev.h index 4778ed1ed..1225e4e6d 100644 --- a/include/spdk/bdev.h +++ b/include/spdk/bdev.h @@ -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 diff --git a/include/spdk/bdev_module.h b/include/spdk/bdev_module.h index 2645174ac..4abaac837 100644 --- a/include/spdk/bdev_module.h +++ b/include/spdk/bdev_module.h @@ -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. */ diff --git a/lib/bdev/bdev.c b/lib/bdev/bdev.c index 378068ed3..26c0fd372 100644 --- a/lib/bdev/bdev.c +++ b/lib/bdev/bdev.c @@ -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)