greybus: use ida for cport id allocation
authorAlex Elder <elder@linaro.org>
Fri, 3 Oct 2014 19:14:24 +0000 (14:14 -0500)
committerGreg Kroah-Hartman <greg@kroah.com>
Sat, 4 Oct 2014 02:00:10 +0000 (19:00 -0700)
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>
drivers/staging/greybus/connection.c
drivers/staging/greybus/core.c
drivers/staging/greybus/greybus.h

index 806eb8c..c50472d 100644 (file)
 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
  * between a CPort on a (local) Greybus host device and a CPort on
  * another Greybus module.
@@ -35,8 +68,9 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface,
                return NULL;
 
        hd = interface->gmod->hd;
-       connection->hd_cport_id = greybus_hd_cport_id_alloc(hd);
-       if (connection->hd_cport_id == CPORT_ID_BAD) {
+       connection->hd = hd;                    /* XXX refcount? */
+       if (!hd_connection_hd_cport_id_alloc(connection)) {
+               /* kref_put(connection->hd); */
                kfree(connection);
                return NULL;
        }
@@ -72,7 +106,7 @@ void gb_connection_destroy(struct gb_connection *connection)
        list_del(&connection->interface_links);
        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->hd); */
        kfree(connection);
index 16100e7..40c8996 100644 (file)
@@ -282,69 +282,6 @@ void greybus_remove_device(struct gb_module *gmod)
 
 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)
 {
        struct greybus_host_device *hd;
@@ -368,13 +305,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver
        hd->driver = driver;
        INIT_LIST_HEAD(&hd->modules);
        INIT_LIST_HEAD(&hd->connections);
-
-       /* 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;
-       }
+       ida_init(&hd->cport_id_map);
 
        return hd;
 }
index 460ace5..4404d93 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/slab.h>
 #include <linux/device.h>
 #include <linux/module.h>
+#include <linux/idr.h>
 
 #include "kernel_ver.h"
 #include "greybus_id.h"
@@ -194,7 +195,7 @@ struct greybus_host_device {
        struct list_head modules;
        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_next_free; /* Where to start checking anyway */
 
@@ -202,9 +203,6 @@ struct greybus_host_device {
        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 device *parent);
 void greybus_remove_hd(struct greybus_host_device *hd);