From patchwork Fri Jul 30 13:55:28 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Monjalon X-Patchwork-Id: 96462 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 9862AA0A0C; Fri, 30 Jul 2021 15:56:11 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 859D040DFB; Fri, 30 Jul 2021 15:56:11 +0200 (CEST) Received: from out1-smtp.messagingengine.com (out1-smtp.messagingengine.com [66.111.4.25]) by mails.dpdk.org (Postfix) with ESMTP id A35DF40141 for ; Fri, 30 Jul 2021 15:56:09 +0200 (CEST) Received: from compute1.internal (compute1.nyi.internal [10.202.2.41]) by mailout.nyi.internal (Postfix) with ESMTP id 3F8CC5C00C9; Fri, 30 Jul 2021 09:56:09 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute1.internal (MEProxy); Fri, 30 Jul 2021 09:56:09 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=monjalon.net; h= from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; s=fm1; bh=5MuYWuqW+Onhk OmI7Q44MmKORIH5WFAbRVmCJmlMpOE=; b=lEzzUl2EvBkV7+ecADa4RUKKM8t9p fiV1ulbfbvowtVmV+oa1HDk1VmLSmMoxp0c16bSibJ8jAG2yeAl6QBubWsF7W9W2 oNhuuTzi6CnHHU4XDGfIHaDrVyHQelaczqDkYs3XbeS9WXM16uGH4XkUfy/jbtwm rSMMJZFcK9RPsXjjPqL4zxNuF955Ibkgg9s0Il1HYmAMDxRais4IUTnwn1KfpxTO ACYcXalB+AfNlLiD5IxqC+uEOmpE5jiM5C6W9UoGCgn9N8Ow+9KO/y+k1SuU2ARt x2F/ppi4vaHlnaRV22vhpBPAtrq8UXz7Ate7uf/pwsfd0O9ZwCIV7DILg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:date:from :in-reply-to:message-id:mime-version:references:subject:to :x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm3; bh=5MuYWuqW+OnhkOmI7Q44MmKORIH5WFAbRVmCJmlMpOE=; b=Kq3ahXVc 33fCs+mGJCU6bhStyMidSWZtwBkx1zLtfOBnos2HEcrGzcBKG7xF/qR9HoRWWAAZ soFI7+C0usMGoVdWJUgn5fAwWIDPh2J6Y/bf8UiNjojNLfYVFb8pOOl7BlGCrsVu t0ea6rgNtlu0IaZvjklUdJr2G5066PlBRW5DrrJ7t/DjqSGRnU+E3FKKQM+6I4u0 kindZRWgri00U113wQhOc/x7MEk4jgRXqYSG3EqPN/WJrZlHPjsLEPDJ885gyK1Q soIOBJuc0cS1JCX0QuIACJG0iOyzfNYxBl9BsEYMYWqrTfp75qOo3YpK1b1+JL2y khi53NZgUFTdYQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvtddrheehgdeiiecutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenuc fjughrpefhvffufffkofgjfhgggfestdekredtredttdenucfhrhhomhepvfhhohhmrghs ucfoohhnjhgrlhhonhcuoehthhhomhgrshesmhhonhhjrghlohhnrdhnvghtqeenucggtf frrghtthgvrhhnpedvhefgiedvjeegtdevheefhfetleefgfeivefgffevfeejgedtgfeu tdehtdegveenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhroh hmpehthhhomhgrshesmhhonhhjrghlohhnrdhnvght X-ME-Proxy: Received: by mail.messagingengine.com (Postfix) with ESMTPA; Fri, 30 Jul 2021 09:56:06 -0400 (EDT) From: Thomas Monjalon To: dev@dpdk.org Cc: Stephen Hemminger , David Marchand , Andrew Rybchenko , Haiyue Wang , Honnappa Nagarahalli , Jerin Jacob , Ferruh Yigit , Elena Agostini , Ray Kinsella Date: Fri, 30 Jul 2021 15:55:28 +0200 Message-Id: <20210730135533.417611-3-thomas@monjalon.net> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210730135533.417611-1-thomas@monjalon.net> References: <20210602203531.2288645-1-thomas@monjalon.net> <20210730135533.417611-1-thomas@monjalon.net> MIME-Version: 1.0 Subject: [dpdk-dev] [RFC PATCH v2 2/7] hcdev: add event notification X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Callback functions may be registered for a device event. Callback management is per-process and not thread-safe. The events RTE_HCDEV_EVENT_NEW and RTE_HCDEV_EVENT_DEL are notified respectively after creation and before removal of a device, as part of the library functions. Some future events may be emitted from drivers. Signed-off-by: Thomas Monjalon --- lib/hcdev/hcdev.c | 137 +++++++++++++++++++++++++++++++++++++++ lib/hcdev/hcdev_driver.h | 7 ++ lib/hcdev/rte_hcdev.h | 71 ++++++++++++++++++++ lib/hcdev/version.map | 3 + 4 files changed, 218 insertions(+) diff --git a/lib/hcdev/hcdev.c b/lib/hcdev/hcdev.c index ea587b3713..2a7ce1ccd8 100644 --- a/lib/hcdev/hcdev.c +++ b/lib/hcdev/hcdev.c @@ -3,6 +3,7 @@ */ #include +#include #include #include #include @@ -27,6 +28,15 @@ static int16_t hcdev_max; /* Number of currently valid devices */ static int16_t hcdev_count; +/* Event callback object */ +struct rte_hcdev_callback { + TAILQ_ENTRY(rte_hcdev_callback) next; + rte_hcdev_callback_t *function; + void *user_data; + enum rte_hcdev_event event; +}; +static void hcdev_free_callbacks(struct rte_hcdev *dev); + int rte_hcdev_init(size_t dev_max) { @@ -166,6 +176,7 @@ rte_hcdev_allocate(const char *name) dev->info.name = dev->name; dev->info.dev_id = dev_id; dev->info.numa_node = -1; + TAILQ_INIT(&dev->callbacks); hcdev_count++; HCDEV_LOG(DEBUG, "new device %s (id %d) of total %d", @@ -180,6 +191,7 @@ rte_hcdev_complete_new(struct rte_hcdev *dev) return; dev->state = RTE_HCDEV_STATE_INITIALIZED; + rte_hcdev_notify(dev, RTE_HCDEV_EVENT_NEW); } int @@ -192,6 +204,9 @@ rte_hcdev_release(struct rte_hcdev *dev) HCDEV_LOG(DEBUG, "free device %s (id %d)", dev->info.name, dev->info.dev_id); + rte_hcdev_notify(dev, RTE_HCDEV_EVENT_DEL); + + hcdev_free_callbacks(dev); dev->state = RTE_HCDEV_STATE_UNUSED; hcdev_count--; @@ -224,6 +239,128 @@ rte_hcdev_close(int16_t dev_id) return firsterr; } +int +rte_hcdev_callback_register(int16_t dev_id, enum rte_hcdev_event event, + rte_hcdev_callback_t *function, void *user_data) +{ + int16_t next_dev, last_dev; + struct rte_hcdev_callback_list *callbacks; + struct rte_hcdev_callback *callback; + + if (!rte_hcdev_is_valid(dev_id) && dev_id != RTE_HCDEV_ID_ANY) { + HCDEV_LOG(ERR, "register callback of invalid ID %d", dev_id); + rte_errno = ENODEV; + return -rte_errno; + } + if (function == NULL) { + HCDEV_LOG(ERR, "cannot register callback without function"); + rte_errno = EINVAL; + return -rte_errno; + } + + if (dev_id == RTE_HCDEV_ID_ANY) { + next_dev = 0; + last_dev = hcdev_max - 1; + } else { + next_dev = last_dev = dev_id; + } + do { + callbacks = &hcdevs[next_dev].callbacks; + + /* check if not already registered */ + TAILQ_FOREACH(callback, callbacks, next) { + if (callback->event == event && + callback->function == function && + callback->user_data == user_data) { + HCDEV_LOG(INFO, "callback already registered"); + return 0; + } + } + + callback = malloc(sizeof(*callback)); + if (callback == NULL) { + HCDEV_LOG(ERR, "cannot allocate callback"); + return -ENOMEM; + } + callback->function = function; + callback->user_data = user_data; + callback->event = event; + TAILQ_INSERT_TAIL(callbacks, callback, next); + + } while (++next_dev <= last_dev); + + return 0; +} + +int +rte_hcdev_callback_unregister(int16_t dev_id, enum rte_hcdev_event event, + rte_hcdev_callback_t *function, void *user_data) +{ + int16_t next_dev, last_dev; + struct rte_hcdev_callback_list *callbacks; + struct rte_hcdev_callback *callback, *next_callback; + + if (!rte_hcdev_is_valid(dev_id) && dev_id != RTE_HCDEV_ID_ANY) { + HCDEV_LOG(ERR, "unregister callback of invalid ID %d", dev_id); + rte_errno = ENODEV; + return -rte_errno; + } + if (function == NULL) { + HCDEV_LOG(ERR, "cannot unregister callback without function"); + rte_errno = EINVAL; + return -rte_errno; + } + + if (dev_id == RTE_HCDEV_ID_ANY) { + next_dev = 0; + last_dev = hcdev_max - 1; + } else { + next_dev = last_dev = dev_id; + } + + do { + callbacks = &hcdevs[next_dev].callbacks; + TAILQ_FOREACH_SAFE(callback, callbacks, next, next_callback) { + if (callback->event != event || + callback->function != function || + (callback->user_data != user_data && + user_data != (void *)-1)) + continue; + TAILQ_REMOVE(callbacks, callback, next); + free(callback); + } + } while (++next_dev <= last_dev); + + return 0; +} + +static void +hcdev_free_callbacks(struct rte_hcdev *dev) +{ + struct rte_hcdev_callback_list *callbacks; + struct rte_hcdev_callback *callback, *next_callback; + + callbacks = &dev->callbacks; + TAILQ_FOREACH_SAFE(callback, callbacks, next, next_callback) { + TAILQ_REMOVE(callbacks, callback, next); + free(callback); + } +} + +void +rte_hcdev_notify(struct rte_hcdev *dev, enum rte_hcdev_event event) +{ + int16_t dev_id; + struct rte_hcdev_callback *callback; + + dev_id = dev->info.dev_id; + TAILQ_FOREACH(callback, &dev->callbacks, next) { + if (callback->event != event || callback->function == NULL) + continue; + callback->function(dev_id, event, callback->user_data); + } +} + int rte_hcdev_info_get(int16_t dev_id, struct rte_hcdev_info *info) { diff --git a/lib/hcdev/hcdev_driver.h b/lib/hcdev/hcdev_driver.h index ca23cb9b9f..80d11bd612 100644 --- a/lib/hcdev/hcdev_driver.h +++ b/lib/hcdev/hcdev_driver.h @@ -12,6 +12,7 @@ #define RTE_HCDEV_DRIVER_H #include +#include #include @@ -43,6 +44,8 @@ struct rte_hcdev { struct rte_hcdev_info info; /* Driver functions. */ struct rte_hcdev_ops ops; + /* Event callback list. */ + TAILQ_HEAD(rte_hcdev_callback_list, rte_hcdev_callback) callbacks; /* Current state (used or not) in the running process. */ enum rte_hcdev_state state; /* Updated by this library. */ /* Driver-specific private data for the running process. */ @@ -64,4 +67,8 @@ void rte_hcdev_complete_new(struct rte_hcdev *dev); __rte_internal int rte_hcdev_release(struct rte_hcdev *dev); +/* Call registered callbacks. No multi-process event. */ +__rte_internal +void rte_hcdev_notify(struct rte_hcdev *dev, enum rte_hcdev_event); + #endif /* RTE_HCDEV_DRIVER_H */ diff --git a/lib/hcdev/rte_hcdev.h b/lib/hcdev/rte_hcdev.h index 83f58193c1..8131e4045a 100644 --- a/lib/hcdev/rte_hcdev.h +++ b/lib/hcdev/rte_hcdev.h @@ -17,6 +17,7 @@ * * The API is not thread-safe. * Device management must be done by a single thread. + * TODO device rwlock for callback list * * @warning * @b EXPERIMENTAL: this API may change without prior notice. @@ -31,6 +32,11 @@ extern "C" { /** Empty device ID. */ #define RTE_HCDEV_ID_NONE -1 +/** Catch-all device ID. */ +#define RTE_HCDEV_ID_ANY INT16_MIN + +/** Catch-all callback data. */ +#define RTE_HCDEV_CALLBACK_ANY_DATA ((void *)-1) /** Store device info. */ struct rte_hcdev_info { @@ -46,6 +52,18 @@ struct rte_hcdev_info { int16_t numa_node; }; +/** Flags passed in notification callback. */ +enum rte_hcdev_event { + /** Device is just initialized. */ + RTE_HCDEV_EVENT_NEW, + /** Device is going to be released. */ + RTE_HCDEV_EVENT_DEL, +}; + +/** Prototype of event callback function. */ +typedef void (rte_hcdev_callback_t)(int16_t dev_id, + enum rte_hcdev_event event, void *user_data); + /** * @warning * @b EXPERIMENTAL: this API may change without prior notice. @@ -142,6 +160,59 @@ int16_t rte_hcdev_find_next(int16_t dev_id); __rte_experimental int rte_hcdev_close(int16_t dev_id); +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Register a function as event callback. + * A function may be registered multiple times for different events. + * + * @param dev_id + * Device ID to get notified about. + * RTE_HCDEV_ID_ANY means all devices. + * @param event + * Device event to be registered for. + * @param function + * Callback function to be called on event. + * @param user_data + * Optional parameter passed in the callback. + * + * @return + * 0 on success, -rte_errno otherwise: + * - ENODEV if invalid dev_id + * - EINVAL if NULL function + * - ENOMEM if out of memory + */ +__rte_experimental +int rte_hcdev_callback_register(int16_t dev_id, enum rte_hcdev_event event, + rte_hcdev_callback_t *function, void *user_data); + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Unregister for an event. + * + * @param dev_id + * Device ID to be silenced. + * RTE_HCDEV_ID_ANY means all devices. + * @param event + * Registered event. + * @param function + * Registered function. + * @param user_data + * Optional parameter as registered. + * RTE_HCDEV_CALLBACK_ANY_DATA is a catch-all. + * + * @return + * 0 on success, -rte_errno otherwise: + * - ENODEV if invalid dev_id + * - EINVAL if NULL function + */ +__rte_experimental +int rte_hcdev_callback_unregister(int16_t dev_id, enum rte_hcdev_event event, + rte_hcdev_callback_t *function, void *user_data); + /** * @warning * @b EXPERIMENTAL: this API may change without prior notice. diff --git a/lib/hcdev/version.map b/lib/hcdev/version.map index bc6dae6de7..24a5a5a7c4 100644 --- a/lib/hcdev/version.map +++ b/lib/hcdev/version.map @@ -2,6 +2,8 @@ EXPERIMENTAL { global: # added in 21.11 + rte_hcdev_callback_register; + rte_hcdev_callback_unregister; rte_hcdev_close; rte_hcdev_count_avail; rte_hcdev_find_next; @@ -16,5 +18,6 @@ INTERNAL { rte_hcdev_allocate; rte_hcdev_complete_new; rte_hcdev_get_by_name; + rte_hcdev_notify; rte_hcdev_release; };