greybus: use ida for cport id allocation
The ida mechanism for allocating ids may be overkill but it works. Don't preallocate the id 0 for control. That should be done when initializing connections based on the manifest anyway. Signed-off-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
063e6ec221
commit
177404bd20
@ -13,6 +13,39 @@
|
|||||||
|
|
||||||
static DEFINE_SPINLOCK(gb_connections_lock);
|
static DEFINE_SPINLOCK(gb_connections_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate an available CPort Id for use for the host side of the
|
||||||
|
* given connection. The lowest-available id is returned, so the
|
||||||
|
* first call is guaranteed to allocate CPort Id 0.
|
||||||
|
*
|
||||||
|
* Assigns the connection's hd_cport_id and returns true if successful.
|
||||||
|
* Returns false otherwise.
|
||||||
|
*/
|
||||||
|
static bool hd_connection_hd_cport_id_alloc(struct gb_connection *connection)
|
||||||
|
{
|
||||||
|
struct ida *ida = &connection->hd->cport_id_map;
|
||||||
|
int id;
|
||||||
|
|
||||||
|
id = ida_simple_get(ida, 0, HOST_DEV_CPORT_ID_MAX, GFP_KERNEL);
|
||||||
|
if (id < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
connection->hd_cport_id = (u16)id;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free a previously-allocated CPort Id on the given host device.
|
||||||
|
*/
|
||||||
|
static void hd_connection_hd_cport_id_free(struct gb_connection *connection)
|
||||||
|
{
|
||||||
|
struct ida *ida = &connection->hd->cport_id_map;
|
||||||
|
|
||||||
|
ida_simple_remove(ida, connection->hd_cport_id);
|
||||||
|
connection->hd_cport_id = CPORT_ID_BAD;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up a Greybus connection, representing the bidirectional link
|
* Set up a Greybus connection, representing the bidirectional link
|
||||||
* between a CPort on a (local) Greybus host device and a CPort on
|
* between a CPort on a (local) Greybus host device and a CPort on
|
||||||
@ -35,8 +68,9 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface,
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
hd = interface->gmod->hd;
|
hd = interface->gmod->hd;
|
||||||
connection->hd_cport_id = greybus_hd_cport_id_alloc(hd);
|
connection->hd = hd; /* XXX refcount? */
|
||||||
if (connection->hd_cport_id == CPORT_ID_BAD) {
|
if (!hd_connection_hd_cport_id_alloc(connection)) {
|
||||||
|
/* kref_put(connection->hd); */
|
||||||
kfree(connection);
|
kfree(connection);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -72,7 +106,7 @@ void gb_connection_destroy(struct gb_connection *connection)
|
|||||||
list_del(&connection->interface_links);
|
list_del(&connection->interface_links);
|
||||||
spin_unlock_irq(&gb_connections_lock);
|
spin_unlock_irq(&gb_connections_lock);
|
||||||
|
|
||||||
greybus_hd_cport_id_free(connection->hd, connection->hd_cport_id);
|
hd_connection_hd_cport_id_free(connection);
|
||||||
/* kref_put(connection->interface); */
|
/* kref_put(connection->interface); */
|
||||||
/* kref_put(connection->hd); */
|
/* kref_put(connection->hd); */
|
||||||
kfree(connection);
|
kfree(connection);
|
||||||
|
@ -282,69 +282,6 @@ void greybus_remove_device(struct gb_module *gmod)
|
|||||||
|
|
||||||
static DEFINE_MUTEX(hd_mutex);
|
static DEFINE_MUTEX(hd_mutex);
|
||||||
|
|
||||||
/*
|
|
||||||
* Allocate an available CPort Id for use on the given host device.
|
|
||||||
* Returns the CPort Id, or CPORT_ID_BAD of none remain.
|
|
||||||
*
|
|
||||||
* The lowest-available id is returned, so the first call is
|
|
||||||
* guaranteed to allocate CPort Id 0.
|
|
||||||
*/
|
|
||||||
u16 greybus_hd_cport_id_alloc(struct greybus_host_device *hd)
|
|
||||||
{
|
|
||||||
unsigned long cport_id;
|
|
||||||
|
|
||||||
/* If none left, return BAD */
|
|
||||||
if (hd->cport_id_count == HOST_DEV_CPORT_ID_MAX)
|
|
||||||
return CPORT_ID_BAD;
|
|
||||||
|
|
||||||
spin_lock_irq(&cport_id_map_lock);
|
|
||||||
cport_id = find_next_zero_bit(hd->cport_id_map, HOST_DEV_CPORT_ID_MAX,
|
|
||||||
hd->cport_id_next_free);
|
|
||||||
if (cport_id < HOST_DEV_CPORT_ID_MAX) {
|
|
||||||
hd->cport_id_next_free = cport_id + 1; /* Success */
|
|
||||||
hd->cport_id_count++;
|
|
||||||
} else {
|
|
||||||
/* Lost a race for the last one */
|
|
||||||
if (hd->cport_id_count != HOST_DEV_CPORT_ID_MAX) {
|
|
||||||
pr_err("bad cport_id_count in alloc");
|
|
||||||
hd->cport_id_count = HOST_DEV_CPORT_ID_MAX;
|
|
||||||
}
|
|
||||||
cport_id = CPORT_ID_BAD;
|
|
||||||
}
|
|
||||||
spin_unlock_irq(&cport_id_map_lock);
|
|
||||||
|
|
||||||
return cport_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Free a previously-allocated CPort Id on the given host device.
|
|
||||||
*/
|
|
||||||
void greybus_hd_cport_id_free(struct greybus_host_device *hd, u16 cport_id)
|
|
||||||
{
|
|
||||||
if (cport_id >= HOST_DEV_CPORT_ID_MAX) {
|
|
||||||
pr_err("bad cport_id %hu\n", cport_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!hd->cport_id_count) {
|
|
||||||
pr_err("too many cport_id frees\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock_irq(&cport_id_map_lock);
|
|
||||||
if (test_and_clear_bit(cport_id, hd->cport_id_map)) {
|
|
||||||
if (hd->cport_id_count) {
|
|
||||||
hd->cport_id_count--;
|
|
||||||
if (cport_id < hd->cport_id_next_free)
|
|
||||||
hd->cport_id_next_free = cport_id;
|
|
||||||
} else {
|
|
||||||
pr_err("bad cport_id_count in free");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pr_err("duplicate cport_id %hu free\n", cport_id);
|
|
||||||
}
|
|
||||||
spin_unlock_irq(&cport_id_map_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_hd(struct kref *kref)
|
static void free_hd(struct kref *kref)
|
||||||
{
|
{
|
||||||
struct greybus_host_device *hd;
|
struct greybus_host_device *hd;
|
||||||
@ -368,13 +305,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver
|
|||||||
hd->driver = driver;
|
hd->driver = driver;
|
||||||
INIT_LIST_HEAD(&hd->modules);
|
INIT_LIST_HEAD(&hd->modules);
|
||||||
INIT_LIST_HEAD(&hd->connections);
|
INIT_LIST_HEAD(&hd->connections);
|
||||||
|
ida_init(&hd->cport_id_map);
|
||||||
/* Pre-allocate CPort 0 for control stuff. XXX */
|
|
||||||
if (greybus_hd_cport_id_alloc(hd) != 0) {
|
|
||||||
pr_err("couldn't allocate cport 0\n");
|
|
||||||
kfree(hd);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hd;
|
return hd;
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/idr.h>
|
||||||
|
|
||||||
#include "kernel_ver.h"
|
#include "kernel_ver.h"
|
||||||
#include "greybus_id.h"
|
#include "greybus_id.h"
|
||||||
@ -194,7 +195,7 @@ struct greybus_host_device {
|
|||||||
struct list_head modules;
|
struct list_head modules;
|
||||||
struct list_head connections;
|
struct list_head connections;
|
||||||
|
|
||||||
DECLARE_BITMAP(cport_id_map, HOST_DEV_CPORT_ID_MAX);
|
struct ida cport_id_map;
|
||||||
u16 cport_id_count; /* How many have been allocated */
|
u16 cport_id_count; /* How many have been allocated */
|
||||||
u16 cport_id_next_free; /* Where to start checking anyway */
|
u16 cport_id_next_free; /* Where to start checking anyway */
|
||||||
|
|
||||||
@ -202,9 +203,6 @@ struct greybus_host_device {
|
|||||||
unsigned long hd_priv[0] __attribute__ ((aligned(sizeof(s64))));
|
unsigned long hd_priv[0] __attribute__ ((aligned(sizeof(s64))));
|
||||||
};
|
};
|
||||||
|
|
||||||
u16 greybus_hd_cport_id_alloc(struct greybus_host_device *hd);
|
|
||||||
void greybus_hd_cport_id_free(struct greybus_host_device *hd, u16 cport_id);
|
|
||||||
|
|
||||||
struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *host_driver,
|
struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *host_driver,
|
||||||
struct device *parent);
|
struct device *parent);
|
||||||
void greybus_remove_hd(struct greybus_host_device *hd);
|
void greybus_remove_hd(struct greybus_host_device *hd);
|
||||||
|
Reference in New Issue
Block a user