On 23 February 2017 at 00:27, Bill Fischofer <bill.fischo...@linaro.org> wrote: > > > On Wed, Feb 22, 2017 at 6:56 AM, Christophe Milard > <christophe.mil...@linaro.org> wrote: >> >> Driver registration and probing is implemented for linux-gen ODP. >> >> Signed-off-by: Christophe Milard <christophe.mil...@linaro.org> >> --- >> platform/linux-generic/drv_driver.c | 348 >> ++++++++++++++++++++++++++++++++++-- >> 1 file changed, 335 insertions(+), 13 deletions(-) >> >> diff --git a/platform/linux-generic/drv_driver.c >> b/platform/linux-generic/drv_driver.c >> index eb0dc48..0148fc3 100644 >> --- a/platform/linux-generic/drv_driver.c >> +++ b/platform/linux-generic/drv_driver.c >> @@ -12,6 +12,7 @@ >> #include <odp/api/std_types.h> >> #include <odp/api/debug.h> >> #include <odp/api/rwlock_recursive.h> >> +#include <odp/api/spinlock.h> >> #include <odp/drv/driver.h> >> #include <odp/drv/spec/driver.h> >> #include <odp_debug_internal.h> >> @@ -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_spinlock_t probelock; /* to avoid concurrent probe on the same >> drv*/ > > > Why use a spinlock rather than a ticketlock here? We seem to have > standardized on ticketlocks as being more efficient and fair.
I did not know ticket locks were regarded as more efficient. I can change, no issue: => v2 > >> >> + 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); >> +} >> + >> odpdrv_enumr_class_t >> odpdrv_enumr_class_register(odpdrv_enumr_class_param_t >> *param) >> { >> @@ -331,6 +378,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; >> @@ -384,19 +433,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) >> @@ -517,10 +564,238 @@ 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; > > > driver_lst.head needs to be initialized as part of init_global() processing > along with lock. no: the initialisation of static variables to 0 is done by the compiler. Relying on this is needed as the list may be appended at __constructor__ running (initialisation) time > >> >> + 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(); >> >> - return ODPDRV_DRIVER_INVALID; >> + /* 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_spinlock_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 = (_odpdrv_enumr_t *) >> + (void *)dev->param.enumerator; >> + enumr_c = (_odpdrv_enumr_class_t *) >> + (void >> *)enumr->param.enumr_class; > > > These casts might be better done with static inline conversion routines. An > example would look like: > > static inline _odpdrv_enumr_t *enumr_hdr(odpdrv_enumr_t enumr) > { > return (_odpdrv_enumr_t *)(void *)enumr; > } > > and used as: > > enumr = enumr_hdr(dev->param.enumerator); > > This reduces clutter as well as preserves flexibility to change the internal > type representations. The packet system uses routines like this and it > really helped in recent streamlining changes Petri has been making as we > were able to change these fairly significantly for improved performance with > minimal code changes as only the converter functions needed to be updated. Yes. you are definitively right: => V2 > >> >> + >> + /* 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_spinlock_lock(&drv->probelock); >> + if (drv->param.probe((odpdrv_device_t)dev, >> + (odpdrv_devio_t)devio, i) == >> 0) { >> + /* the driver accepts this device */ >> + odp_spinlock_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_spinlock_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)(void *)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_spinlock_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_spinlock_unlock(&drv->probelock); >> + return -1; >> + } >> + >> + /* unbind succeeded */ >> + dev->driver = NULL; >> + dev->devio = NULL; >> + odp_spinlock_unlock(&drv->probelock); >> + return 0; >> +} >> + >> +/* try to find a driver for the given device, trying all possible >> registered >> + * drivers against it: >> + */ >> +static int probe_device(_odpdrv_device_t *dev) >> +{ >> + _odpdrv_driver_t *driver; >> + >> + /* print the list of registered drivers: */ >> + driver_list_read_lock(); >> + driver = driver_lst.head; >> + while (driver) { >> + probe_device_driver(dev, driver); > > > Should the rc be checked here? If not, use (void) on call to keep coverity > happy. Yes it should :-). and probing should be stopped after the first success: => V2 > >> >> + driver = driver->next; >> + } >> + driver_list_read_unlock(); >> + >> + return 0; >> +} >> + >> +/* try to find a driver for all the registered devices, trying all >> possible >> + * drivers-devices combinaison >> + */ >> +static int probe_all(void) >> +{ >> + _odpdrv_device_t *dev; >> + >> + dev_list_read_lock(); >> + dev = device_lst.head; >> + while (dev) { >> + probe_device(dev); > > > Same rc comment here. I'll put a (void) here: it is not really an error to have devices that have no ODP drivers > >> >> + dev = dev->next; >> + } >> + dev_list_read_unlock(); >> + >> + return 0; >> } >> >> /* the following function is called each time probing is needed, i.e. >> @@ -556,6 +831,9 @@ void _odpdrv_driver_probe_drv_items(void) >> enumr = enumr->next; >> } >> enumr_list_read_unlock(); >> + >> + /* probe drivers for all devices */ >> + probe_all(); > > > Same rc comment here. I'll change the return value of probe_all to void as some devices may be probed successfully and some others not. Christophe > >> >> } >> >> int odpdrv_print_all(void) >> @@ -564,6 +842,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) >> @@ -605,11 +884,16 @@ int odpdrv_print_all(void) >> enumr_c = (_odpdrv_enumr_class_t *) >> (void >> *)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 : "<none>", >> + dev->devio ? dev->devio->param.api_name : >> "<none>", >> + dev->devio ? dev->devio->param.api_version : 0); >> dev = dev->next; >> } >> dev_list_read_unlock(); >> @@ -629,6 +913,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; >> } >> >> @@ -647,6 +942,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(); >> @@ -667,10 +963,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) { >> -- >> 2.7.4 >> >