From patchwork Wed Mar 22 14:48:26 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christophe Milard X-Patchwork-Id: 95749 Delivered-To: patch@linaro.org Received: by 10.182.3.34 with SMTP id 2csp231592obz; Wed, 22 Mar 2017 07:07:06 -0700 (PDT) X-Received: by 10.107.47.137 with SMTP id v9mr36245112iov.112.1490191626696; Wed, 22 Mar 2017 07:07:06 -0700 (PDT) Return-Path: Received: from lists.linaro.org (lists.linaro.org. [54.225.227.206]) by mx.google.com with ESMTP id 21si1876513ioe.135.2017.03.22.07.07.06; Wed, 22 Mar 2017 07:07:06 -0700 (PDT) Received-SPF: pass (google.com: domain of lng-odp-bounces@lists.linaro.org designates 54.225.227.206 as permitted sender) client-ip=54.225.227.206; Authentication-Results: mx.google.com; spf=pass (google.com: domain of lng-odp-bounces@lists.linaro.org designates 54.225.227.206 as permitted sender) smtp.mailfrom=lng-odp-bounces@lists.linaro.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: by lists.linaro.org (Postfix, from userid 109) id 15F2E642D3; Wed, 22 Mar 2017 14:07:06 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on ip-10-142-244-252 X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, URIBL_BLOCKED autolearn=disabled version=3.4.0 Received: from [127.0.0.1] (localhost [127.0.0.1]) by lists.linaro.org (Postfix) with ESMTP id 80B53642FB; Wed, 22 Mar 2017 13:51:31 +0000 (UTC) X-Original-To: lng-odp@lists.linaro.org Delivered-To: lng-odp@lists.linaro.org Received: by lists.linaro.org (Postfix, from userid 109) id 9789A64315; Wed, 22 Mar 2017 13:51:22 +0000 (UTC) Received: from mail-wm0-f52.google.com (mail-wm0-f52.google.com [74.125.82.52]) by lists.linaro.org (Postfix) with ESMTPS id 98A56642D0 for ; Wed, 22 Mar 2017 13:49:35 +0000 (UTC) Received: by mail-wm0-f52.google.com with SMTP id t189so38384065wmt.1 for ; Wed, 22 Mar 2017 06:49:35 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=hNj+1a6Kq6fn6/ZslSPjy+0Ix5QumCiwkesqVH1Vzlo=; b=Xnakg3gmh99BU71EcECZUv3tojCzfXryX2KO3Pl0WvkMGT0pR/8N/FWTMKwN0uEJ14 0AP1Ic7yzRpJjiQv3msuuSKYtRhc/t6vestPGVwjLGF82K5d5GHCUdnB8UN5ivjyXB5s kjoNPf8VrYIdW5IIyvJHrKF7usAHT0NjXcfzoIIOWpsyyP4jUCOSELvunpuSdqwCvQbL DG5b9bwHqf+l2x01ngHWo838mTGDHF70MMbiSJ9ZF8xxqYCnd6K0OTjM030M/YvbSo3U 4ZVif9sN6w7KXysmBJUWjbox33mTIOVIVw/AJgHCMoeKZwx7giEZELHiLd8Bw6/Y21rK gSxw== X-Gm-Message-State: AFeK/H0KE9xhBPzNTBBRgbIgs6v653ud31sX4RltHFctPZ4Z7e7HaHZ0p0lB5WblVxnInUYSj2U= X-Received: by 10.25.223.86 with SMTP id q22mr11407156lfj.73.1490190574273; Wed, 22 Mar 2017 06:49:34 -0700 (PDT) Received: from erachmi-ericsson.ki.sw.ericsson.se (c-83-233-76-66.cust.bredband2.com. [83.233.76.66]) by smtp.gmail.com with ESMTPSA id l23sm522305lfk.27.2017.03.22.06.49.33 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 22 Mar 2017 06:49:33 -0700 (PDT) From: Christophe Milard To: yi.he@linaro.org, bill.fischofer@linaro.org, lng-odp@lists.linaro.org Date: Wed, 22 Mar 2017 15:48:26 +0100 Message-Id: <1490194110-40168-20-git-send-email-christophe.milard@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1490194110-40168-1-git-send-email-christophe.milard@linaro.org> References: <1490194110-40168-1-git-send-email-christophe.milard@linaro.org> Subject: [lng-odp] [API-NEXT PATCHv2 19/23] linux-gen: driver registration and probing X-BeenThere: lng-odp@lists.linaro.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: "The OpenDataPlane \(ODP\) List" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: lng-odp-bounces@lists.linaro.org Sender: "lng-odp" Driver registration and probing is implemented for linux-gen ODP. Signed-off-by: Christophe Milard --- platform/linux-generic/drv_driver.c | 349 ++++++++++++++++++++++++++++++++++-- 1 file changed, 336 insertions(+), 13 deletions(-) -- 2.7.4 diff --git a/platform/linux-generic/drv_driver.c b/platform/linux-generic/drv_driver.c index 873ec3c..ea92457 100644 --- a/platform/linux-generic/drv_driver.c +++ b/platform/linux-generic/drv_driver.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,11 @@ typedef struct _odpdrv_enumr_class_s _odpdrv_enumr_class_t; typedef struct _odpdrv_enumr_s _odpdrv_enumr_t; typedef struct _odpdrv_device_s _odpdrv_device_t; typedef struct _odpdrv_devio_s _odpdrv_devio_t; +typedef struct _odpdrv_driver_s _odpdrv_driver_t; + +static int unbind_device_driver(_odpdrv_device_t *dev, + void (*callback)(odpdrv_device_t odpdrv_dev), + int immediate); /* an enumerator class (list element) */ struct _odpdrv_enumr_class_s { @@ -62,6 +68,8 @@ static struct _odpdrv_enumr_lst_t enumr_lst; /* a device (list element) */ struct _odpdrv_device_s { odpdrv_device_param_t param; + _odpdrv_driver_t *driver; /* driver for the device (if bound), or NULL*/ + _odpdrv_devio_t *devio; /* devio used for device (if bound), or NULL*/ void (*enumr_destroy_callback)(void *enum_dev);/*dev destroy callback */ struct _odpdrv_device_s *next; } _odpdrv_device_s; @@ -87,6 +95,21 @@ typedef struct _odpdrv_devio_lst_t { } _odpdrv_devio_lst_t; static struct _odpdrv_devio_lst_t devio_lst; +/* a driver (list element) */ +struct _odpdrv_driver_s { + odpdrv_driver_param_t param; + _odp_ishm_pool_t *pool; + odp_ticketlock_t probelock; /* to avoid concurrent probe on same drv*/ + struct _odpdrv_driver_s *next; +}; + +/* the driver list: */ +typedef struct _odpdrv_driver_lst_t { + odp_rwlock_recursive_t lock; + _odpdrv_driver_t *head; +} _odpdrv_driver_lst_t; +static struct _odpdrv_driver_lst_t driver_lst; + /* some driver elements (such as enumeraor classes, drivers, devio) may * register before init_global and init_local complete. Mutex will fail * in this cases but should be used later on. @@ -188,6 +211,30 @@ static void devio_list_write_unlock(void) odp_rwlock_recursive_write_unlock(&devio_lst.lock); } +static void driver_list_read_lock(void) +{ + if (init_global_status == DONE) + odp_rwlock_recursive_read_lock(&driver_lst.lock); +} + +static void driver_list_read_unlock(void) +{ + if (init_global_status == DONE) + odp_rwlock_recursive_read_unlock(&driver_lst.lock); +} + +static void driver_list_write_lock(void) +{ + if (init_global_status == DONE) + odp_rwlock_recursive_write_lock(&driver_lst.lock); +} + +static void driver_list_write_unlock(void) +{ + if (init_global_status == DONE) + odp_rwlock_recursive_write_unlock(&driver_lst.lock); +} + /* some functions to get internal pointers from handles... */ static inline _odpdrv_enumr_class_t *get_enumr_class(odpdrv_enumr_class_t class) { @@ -346,6 +393,8 @@ odpdrv_device_t odpdrv_device_create(odpdrv_device_param_t *param) /* save and set dev init parameters and insert new device in list */ dev->param = *param; dev->enumr_destroy_callback = NULL; + dev->driver = NULL; + dev->devio = NULL; dev_list_write_lock(); dev->next = device_lst.head; device_lst.head = dev; @@ -399,19 +448,17 @@ int odpdrv_device_destroy(odpdrv_device_t dev, */ target->enumr_destroy_callback = callback; - /* TODO: if a driver is bound to the device, unbind it! - * passing the flag andf device_destroy_terminate() as a callback */ - - /* no driver is handling this device, or no callback was - * provided: continue removing the device: */ - device_destroy_terminate(dev); + /* unbind the driver from the device (if bound). + * The callback is always called. */ + unbind_device_driver(target, + device_destroy_terminate, + (flags & ODPDRV_DEV_DESTROY_IMMEDIATE)); return 0; } /* This function is called as a callback from the driver, when unbindind - * a device, or directely from odpdrv_device_destroy() if no driver - * was bound to the device. + * a device drom odpdrv_device_destroy() * just call the enumerator callback to cleanup the enumerator part * and free device memory */ static void device_destroy_terminate(odpdrv_device_t drv_device) @@ -532,10 +579,239 @@ odpdrv_devio_t odpdrv_devio_register(odpdrv_devio_param_t *param) odpdrv_driver_t odpdrv_driver_register(odpdrv_driver_param_t *param) { - ODP_ERR("NOT Supported yet! Driver %s Registration!\n.", - param->name); + _odpdrv_driver_t *driver; + + /* check for a few compulsory things: */ + if ((param->probe == NULL) || + (param->unbind == NULL)) + return ODPDRV_DRIVER_INVALID; + + /* parse the list of already registered drivers to make + * sure no driver with same name already exists: + */ + driver_list_read_lock(); + driver = driver_lst.head; + while (driver) { + if ((strncmp(param->name, driver->param.name, + ODPDRV_NAME_SIZE) == 0)) { + ODP_ERR("driver %s already registered!\n", + param->name); + driver_list_read_unlock(); + return ODPDRV_DRIVER_INVALID; + } + driver = driver->next; + } + driver_list_read_unlock(); + + /* allocate memory for the new driver: + * If init_global has not been done yet, then, we cannot allocate + * from any _ishm pool (ishm has not even been initialised at this + * stage...this happens when statically linked drivers + * register: their __constructor__ function is run before main() + * is called). But any malloc performed here(before init_global) + * will be inherited by any odpthreads (process or pthreads) as we + * are still running in the ODP instantiation processes and all + * other processes are guaranteed to be descendent of this one... + * If init_global has been done, then we allocate from the _ishm pool + * to guarantee visibility from any ODP thread. + */ + + if (init_global_status == UNDONE) { + driver = malloc(sizeof(_odpdrv_driver_t)); + if (!driver) + return ODPDRV_DRIVER_INVALID; + driver->pool = NULL; + } else { + driver = _odp_ishm_pool_alloc(list_elt_pool, + sizeof(_odpdrv_driver_t)); + if (!driver) { + ODP_ERR("_odp_ishm_pool_alloc failed!\n"); + return ODPDRV_DRIVER_INVALID; + } + driver->pool = list_elt_pool; + } + + /* save init parameters and insert driver in list */ + driver->param = *param; + odp_ticketlock_init(&driver->probelock); + driver_list_write_lock(); + driver->next = driver_lst.head; + driver_lst.head = driver; + driver_list_write_unlock(); + + return (odpdrv_driver_t)driver; +} + +/* Probe, if possible, the given driver with the given device: + * The driver is probed if: + * There exist a devio D such as + * -The name and version of the API provided by D matches one of the requested + * devio {name,version} requested by the driver + * -The enumerator's API (name and version) requested by D is provided + * by the enumerator which enumerated the device. + * This function will return zero if the above condition where met by some + * devio D and the driver probe function returns 0 (success). + * The function will return -1 if some devio D were found, but the driver + * returned a non-zero value when probed (for all of them). + * The function will return -2 if no devio matching the above requirement was + * found. + * The function will return -3 if the device was already bound to a driver */ +static int probe_device_driver(_odpdrv_device_t *dev, _odpdrv_driver_t *drv) +{ + int i; + int ret = -2; + _odpdrv_devio_t *devio; + _odpdrv_enumr_t *enumr; + _odpdrv_enumr_class_t *enumr_c; + + /* the device already has a driver?: end of story... */ + if (dev->driver) + return -3; + + /* look at the different devio this driver can work with: */ + for (i = 0; i < ODPDRV_MAX_DEVIOS; i++) { + /* look at each registered devios: */ + devio_list_read_lock(); + for (devio = devio_lst.head; devio; devio = devio->next) { + /* if devio is no good for this driver, keep searching*/ + if ((strncmp(drv->param.devios[i].api_name, + devio->param.api_name, + ODPDRV_NAME_SIZE) != 0) || + (drv->param.devios[i].api_version != + devio->param.api_version)) + continue; + + /* give a chance to the devio to reject the device + * if it feels it should do so: */ + if (devio->param.probe && + devio->param.probe((odpdrv_device_t)dev)) + continue; + + /* grab the device enumerator and its class: */ + enumr = get_enumr(dev->param.enumerator); + enumr_c = get_enumr_class(enumr->param.enumr_class); + + /* if devio is no good for this dev, keep searching */ + if ((strncmp(devio->param.enumr_api_name, + enumr->param.api_name, + ODPDRV_NAME_SIZE) != 0) || + (devio->param.enumr_api_version != + enumr->param.api_version)) + continue; + + /* seems we are good to probe the driver: */ + odp_ticketlock_lock(&drv->probelock); + if (drv->param.probe((odpdrv_device_t)dev, + (odpdrv_devio_t)devio, i) == 0) { + /* the driver accepts this device */ + odp_ticketlock_unlock(&drv->probelock); + devio_list_read_unlock(); + ODP_DBG("driver %s will handle device %s(%s)\n", + drv->param.name, + dev->param.address, + enumr_c->param.name); + dev->driver = drv; + dev->devio = devio; + return 0; + } + odp_ticketlock_unlock(&drv->probelock); + + /* driver did not accept the device: keep searching */ + ret = -1; + } + devio_list_read_unlock(); + } + return ret; +} + +/* an empty callback is given to the driver on unprobe, if no real callback is + * needed */ +static void empty_unbind_callback(odpdrv_device_t odpdrv_dev ODP_UNUSED) +{ +} + +/* unbind the device driver from the device (i.e. "unprobe") + * if the immediate flag is set, the unbind is requested to be immediate, + * i.e. the driver is due to call the callback within its unbind function. + * (if the flag is not set, the callback can be called later on from + * another context. Immediate unbinding may be less graceful then + * non immediate binding) + * The callback function is called in all cases (even if the device was not + * bound) + */ +static int unbind_device_driver(_odpdrv_device_t *dev, + void (*callback)(odpdrv_device_t odpdrv_dev), + int immediate) +{ + _odpdrv_driver_t *drv; + odpdrv_device_t odpdrv_dev = (odpdrv_device_t)dev; + int flg = immediate ? ODPDRV_DRV_UNBIND_IMMEDIATE : 0; + + if (!callback) + callback = empty_unbind_callback; + + drv = dev->driver; + if (!drv) { /* nothing to do */ + callback(odpdrv_dev); + return 0; + } + + /* note that we assure that a given driver will not be bound/unbound + * concurrentely - but this does not cover the callback */ + odp_ticketlock_lock(&drv->probelock); + if (drv->param.unbind(odpdrv_dev, callback, flg)) { + ODP_DBG("driver %s could not release device %s\n", + drv->param.name, + dev->param.address); + odp_ticketlock_unlock(&drv->probelock); + return -1; + } + + /* unbind succeeded */ + dev->driver = NULL; + dev->devio = NULL; + odp_ticketlock_unlock(&drv->probelock); + return 0; +} + +/* try to find a driver for the given device, trying all possible registered + * drivers against it: + * returns 0 on success or -1 on error + */ +static int probe_device(_odpdrv_device_t *dev) +{ + _odpdrv_driver_t *driver; + int ret = -1; + + /* go through the list of registered drivers: */ + driver_list_read_lock(); + driver = driver_lst.head; + while (driver) { + if (probe_device_driver(dev, driver) == 0) { + ret = 0; + break; + } + driver = driver->next; + } + driver_list_read_unlock(); + + return ret; +} + +/* try to find a driver for all the registered devices, trying all possible + * drivers-devices combinaison + */ +static void probe_all(void) +{ + _odpdrv_device_t *dev; - return ODPDRV_DRIVER_INVALID; + dev_list_read_lock(); + dev = device_lst.head; + while (dev) { + (void)probe_device(dev); + dev = dev->next; + } + dev_list_read_unlock(); } /* the following function is called each time probing is needed, i.e. @@ -571,6 +847,9 @@ void _odpdrv_driver_probe_drv_items(void) enumr = enumr->next; } enumr_list_read_unlock(); + + /* probe drivers for all devices */ + probe_all(); } int odpdrv_print_all(void) @@ -579,6 +858,7 @@ int odpdrv_print_all(void) _odpdrv_enumr_t *enumr; _odpdrv_device_t *dev; _odpdrv_devio_t *devio; + _odpdrv_driver_t *driver; /* we cannot use ODP_DBG before ODP init... */ if (init_global_status == UNDONE) @@ -618,11 +898,16 @@ int odpdrv_print_all(void) enumr = get_enumr(dev->param.enumerator); enumr_c = get_enumr_class(enumr->param.enumr_class); ODP_DBG(" device: address: %s, from enumerator class: %s " - " API: %s, Version: %d\n", + " API: %s, Version: %d, " + " handled by driver %s, with devio API: %s " + " (version %d)\n", dev->param.address, enumr_c->param.name, enumr->param.api_name, - enumr->param.api_version); + enumr->param.api_version, + dev->driver ? dev->driver->param.name : "", + dev->devio ? dev->devio->param.api_name : "", + dev->devio ? dev->devio->param.api_version : 0); dev = dev->next; } dev_list_read_unlock(); @@ -642,6 +927,17 @@ int odpdrv_print_all(void) } devio_list_read_unlock(); + /* print the list of registered drivers: */ + driver_list_read_lock(); + driver = driver_lst.head; + ODP_DBG("The following dev drivers have been registered:\n"); + while (driver) { + ODP_DBG(" driver: '%s'\n", + driver->param.name); + driver = driver->next; + } + driver_list_read_unlock(); + return 0; } @@ -660,6 +956,7 @@ int _odpdrv_driver_init_global(void) odp_rwlock_recursive_init(&enumr_lst.lock); odp_rwlock_recursive_init(&device_lst.lock); odp_rwlock_recursive_init(&devio_lst.lock); + odp_rwlock_recursive_init(&driver_lst.lock); /* probe things... */ _odpdrv_driver_probe_drv_items(); @@ -680,10 +977,36 @@ int _odpdrv_driver_term_global(void) _odpdrv_devio_t *devio; _odpdrv_enumr_class_t *enumr_c; _odpdrv_enumr_t *enumr; + _odpdrv_device_t *dev; + _odpdrv_driver_t *driver; if (init_global_status == UNDONE) return 0; + /* unbind any driver from any device: */ + dev_list_read_lock(); + dev = device_lst.head; + while (dev) { + unbind_device_driver(dev, NULL, 1); + dev = dev->next; + } + dev_list_read_unlock(); + + /* and remove all registered drivers: */ + driver_list_read_lock(); + while (driver_lst.head) { + driver = driver_lst.head; + if (driver->param.remove) { + if (driver->param.remove()) + ODP_ERR("driver removal indicated failure!\n"); + } + driver_lst.head = driver->next; + if (driver->pool) + _odp_ishm_pool_free(list_elt_pool, driver); + else + free(driver); + } + /* remove all devios which are registered: */ devio_list_write_lock(); while (devio_lst.head) {