RE: [PATCH 01/04] OMAP3 SRF: Generic shared resource f/w

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> -----Original Message-----
> From: Dasgupta, Romit [mailto:romit@xxxxxx] 
> Sent: Friday, October 17, 2008 11:47 AM
> To: Nayak, Rajendra; linux-omap@xxxxxxxxxxxxxxx
> Subject: RE: [PATCH 01/04] OMAP3 SRF: Generic shared resource f/w
> 
> Rajendra,
>                    Few bugs in the code
> 1) curr_level of the shared_resource is not updated in 
> update_resource_level

Yes, its not. Its done as part of the platform specific change_level call.

> 2) resource_request invokes spin_lock_irqsave and then if it 
> is a request for a new device it invokes get_user().
> get_user() calls kmalloc with GFP_KERNEL. So it can sleep. 
> Hence you will sleep with spinlocks held!!

Right, so I'll probably have to add a GFP_ATOMIC flag to that.
I am now thinking If I really need spinlocks, think I can do with mutex's instead.
The spinlocks were put in place to take care of the omap-pm hooks from 
clock f/w which no longer seem to be needed.
 
> 
> Trying to review the remaining patches.
> 
> Regards,
> -Romit
> 
> 
> >-----Original Message-----
> >From: linux-omap-owner@xxxxxxxxxxxxxxx [mailto:linux-omap-
> >owner@xxxxxxxxxxxxxxx] On Behalf Of Nayak, Rajendra
> >Sent: Thursday, October 16, 2008 7:42 PM
> >To: linux-omap@xxxxxxxxxxxxxxx
> >Subject: [PATCH 01/04] OMAP3 SRF: Generic shared resource f/w
> >
> >This patch defines and implements the Generic
> >Shared Resource Framework structures and API's
> >
> >Signed-off-by: Rajendra Nayak <rnayak@xxxxxx>
> >---
> > arch/arm/plat-omap/include/mach/resource.h |   79 +++++
> > arch/arm/plat-omap/resource.c              |  433 
> +++++++++++++++++++++++++++++
> > 2 files changed, 512 insertions(+)
> >
> >Index: linux-omap-2.6/arch/arm/plat-omap/resource.c
> >================================================================
> >===
> >--- /dev/null  1970-01-01 00:00:00.000000000 +0000
> >+++ linux-omap-2.6/arch/arm/plat-omap/resource.c       2008-10-16
> >18:04:05.000000000 +0530
> >@@ -0,0 +1,433 @@
> >+/*
> >+ * linux/arch/arm/plat-omap/resource.c
> >+ * Shared Resource Framework API implementation
> >+ *
> >+ * Copyright (C) 2007-2008 Texas Instruments, Inc.
> >+ * Rajendra Nayak <rnayak@xxxxxx>
> >+ *
> >+ * This program is free software; you can redistribute it 
> and/or modify
> >+ * it under the terms of the GNU General Public License version 2 as
> >+ * published by the Free Software Foundation.
> >+ *
> >+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
> >+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
> >+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
> >PURPOSE.
> >+ * History:
> >+ *
> >+ */
> >+
> >+#include <linux/errno.h>
> >+#include <linux/err.h>
> >+#include <mach/resource.h>
> >+#include <linux/slab.h>
> >+
> >+/*
> >+ * This is for statically defining the users pool. This 
> static pool is
> >+ * used early at bootup till kmalloc becomes available.
> >+ */
> >+#define MAX_USERS     10
> >+#define UNUSED                0x0
> >+#define       DYNAMIC_ALLOC   0x1
> >+#define STATIC_ALLOC  0x2
> >+
> >+/* res_list contains all registered struct shared_resource */
> >+static LIST_HEAD(res_list);
> >+
> >+/* res_lock protects res_list add and del ops */
> >+static DEFINE_SPINLOCK(res_lock);
> >+
> >+/* Static Pool of users for a resource used till kmalloc 
> becomes available */
> >+struct  users_list usr_list[MAX_USERS];
> >+
> >+/* Private/Internal functions */
> >+
> >+/**
> >+ * _resource_lookup - loop up a resource by its name, 
> return a pointer
> >+ * @name: The name of the resource to lookup
> >+ *
> >+ * Looks for a registered resource by its name. Returns a pointer to
> >+ * the struct shared_resource if found, else returns NULL.
> >+ * The function is not lock safe.
> >+ */
> >+static struct shared_resource *_resource_lookup(const char *name)
> >+{
> >+      struct shared_resource *res, *tmp_res;
> >+
> >+      if (!name)
> >+              return NULL;
> >+
> >+      res = NULL;
> >+
> >+      list_for_each_entry(tmp_res, &res_list, node) {
> >+              if (!strcmp(name, tmp_res->name)) {
> >+                      res = tmp_res;
> >+                      break;
> >+              }
> >+      }
> >+      return res;
> >+}
> >+
> >+/**
> >+ * resource_lookup - loop up a resource by its name, return 
> a pointer
> >+ * @name: The name of the resource to lookup
> >+ *
> >+ * Looks for a registered resource by its name. Returns a pointer to
> >+ * the struct shared_resource if found, else returns NULL.
> >+ * The function holds spinlocks and takes care of atomicity.
> >+ */
> >+static struct shared_resource *resource_lookup(const char *name)
> >+{
> >+      struct shared_resource *res;
> >+      unsigned long flags;
> >+
> >+      if (!name)
> >+              return NULL;
> >+      spin_lock_irqsave(&res_lock, flags);
> >+      res = _resource_lookup(name);
> >+      spin_unlock_irqrestore(&res_lock, flags);
> >+
> >+      return res;
> >+}
> >+
> >+/**
> >+ * update_resource_level - Regenerates and updates the 
> curr_level of the res
> >+ * @resp: Pointer to the resource
> >+ *
> >+ * This function looks at all the users of the given 
> resource and the levels
> >+ * requested by each of them, and recomputes a target level 
> for the resource
> >+ * acceptable to all its current usres. It then calls 
> platform specific
> >+ * change_level to change the level of the resource.
> >+ * Returns 0 on success, else a non-zero value returned by 
> the platform
> >+ * specific change_level function.
> >+ **/
> >+static int update_resource_level(struct shared_resource *resp)
> >+{
> >+      struct users_list *user;
> >+      unsigned long target_level;
> >+      int ret;
> >+
> >+      /* Regenerate the target_value for the resource */
> >+      target_level = RES_DEFAULTLEVEL;
> >+      list_for_each_entry(user, &resp->users_list, node)
> >+              if (user->level > target_level)
> >+                      target_level = user->level;
> >+
> >+      pr_debug("SRF: Changing Level for resource %s to %ld\n",
> >+                              resp->name, target_level);
> >+      ret = resp->ops->change_level(resp, target_level);
> >+      if (ret) {
> >+              printk(KERN_ERR "Unable to Change"
> >+                                      "level for resource 
> %s to %ld\n",
> >+              resp->name, target_level);
> >+      }
> >+      return ret;
> >+}
> >+
> >+/**
> >+ * get_user - gets a new users_list struct from static pool 
> or dynamically
> >+ *
> >+ * This function initally looks for availability in the 
> static pool and
> >+ * tries to dynamcially allocate only once the static pool is empty.
> >+ * We hope that during bootup by the time we hit a case of 
> dynamic allocation
> >+ * slab initialization would have happened.
> >+ * Returns a pointer users_list struct on success. On 
> dynamic allocation failure
> >+ * returns a ERR_PTR(-ENOMEM).
> >+ */
> >+static struct users_list *get_user(void)
> >+{
> >+      int ind = 0;
> >+      struct users_list *user;
> >+
> >+      /* See if something available in the static pool */
> >+      while (ind < MAX_USERS) {
> >+              if (usr_list[ind].usage == UNUSED)
> >+                      break;
> >+              else
> >+                      ind++;
> >+      }
> >+      if (ind < MAX_USERS) {
> >+              /* Pick from the static pool */
> >+              user = &usr_list[ind];
> >+              user->usage = STATIC_ALLOC;
> >+      } else {
> >+              /* By this time we hope slab is initialized */
> >+              if (slab_is_available()) {
> >+                      user = kmalloc(sizeof(struct  
> users_list), GFP_KERNEL);
> >+                      if (!user) {
> >+                              printk(KERN_ERR "SRF:FATAL 
> ERROR: kmalloc"
> >+                                                      "failed\n");
> >+                              return ERR_PTR(-ENOMEM);
> >+                      }
> >+                      user->usage = DYNAMIC_ALLOC;
> >+              } else {
> >+                      /* Dynamic alloc not available yet */
> >+                      printk(KERN_ERR "SRF: FATAL ERROR: users_list"
> >+                              "initial POOL EMPTY before 
> slab init\n");
> >+                      return ERR_PTR(-ENOMEM);
> >+              }
> >+      }
> >+      return user;
> >+}
> >+
> >+/**
> >+ * free_user - frees the dynamic users_list and marks the 
> static one unused
> >+ * @user: The struct users_list to be freed
> >+ *
> >+ * Looks at the usage flag and either frees the users_list if it was
> >+ * dynamically allocated, and if its from the static pool, 
> marks it unused.
> >+ * No return value.
> >+ */
> >+void free_user(struct users_list *user)
> >+{
> >+      if (user->usage == DYNAMIC_ALLOC) {
> >+              kfree(user);
> >+      } else {
> >+              user->usage = UNUSED;
> >+              user->level = RES_DEFAULTLEVEL;
> >+              user->dev = NULL;
> >+      }
> >+}
> >+
> >+/**
> >+ * resource_init - Initializes the Shared resource framework.
> >+ * @resources: List of all the resources modelled
> >+ *
> >+ * Loops through the list of resources and registers all that
> >+ * are available for the current CPU.
> >+ * No return value
> >+ */
> >+void resource_init(struct shared_resource **resources)
> >+{
> >+      struct shared_resource **resp;
> >+      int ind;
> >+
> >+      pr_debug("Initializing Shared Resource Framework\n");
> >+
> >+      if (!cpu_is_omap34xx()) {
> >+              /* This CPU is not supported */
> >+              printk(KERN_WARNING "Shared Resource 
> Framework does not"
> >+                      "support this CPU type.\n");
> >+              WARN_ON(1);
> >+      }
> >+
> >+      /* Init the users_list POOL */
> >+      for (ind = 0; ind < MAX_USERS; ind++) {
> >+              usr_list[ind].usage = UNUSED;
> >+              usr_list[ind].dev = NULL;
> >+              usr_list[ind].level = RES_DEFAULTLEVEL;
> >+      }
> >+
> >+      if (resources)
> >+              for (resp = resources; *resp; resp++)
> >+                      resource_register(*resp);
> >+}
> >+
> >+/**
> >+ * resource_register - registers and initializes a resource
> >+ * @res: struct shared_resource * to register
> >+ *
> >+ * Initializes the given resource and adds it to the resource list
> >+ * for the current CPU.
> >+ * Returns 0 on success, -EINVAL if given a NULL pointer, 
> -EEXIST if the
> >+ * resource is already registered.
> >+ */
> >+int resource_register(struct shared_resource *resp)
> >+{
> >+      unsigned long flags;
> >+
> >+      if (!resp)
> >+              return -EINVAL;
> >+
> >+      if (!omap_chip_is(resp->omap_chip))
> >+              return -EINVAL;
> >+
> >+      /* Verify that the resource is not already registered */
> >+      if (resource_lookup(resp->name))
> >+              return -EEXIST;
> >+
> >+      INIT_LIST_HEAD(&resp->users_list);
> >+
> >+      spin_lock_irqsave(&res_lock, flags);
> >+      /* Add the resource to the resource list */
> >+      list_add(&resp->node, &res_list);
> >+
> >+      /* Call the resource specific init*/
> >+      if (resp->ops->init)
> >+              resp->ops->init(resp);
> >+
> >+      spin_unlock_irqrestore(&res_lock, flags);
> >+      pr_debug("resource: registered %s\n", resp->name);
> >+
> >+      return 0;
> >+}
> >+EXPORT_SYMBOL(resource_register);
> >+
> >+/**
> >+ * resource_unregister - unregister a resource
> >+ * @res: struct shared_resource * to unregister
> >+ *
> >+ * Removes a resource from the resource list.
> >+ * Returns 0 on success, -EINVAL if passed a NULL pointer.
> >+ */
> >+int resource_unregister(struct shared_resource *resp)
> >+{
> >+      unsigned long flags;
> >+
> >+      if (!resp)
> >+              return -EINVAL;
> >+
> >+      spin_lock_irqsave(&res_lock, flags);
> >+      /* delete the resource from the resource list */
> >+      list_del(&resp->node);
> >+      spin_unlock_irqrestore(&res_lock, flags);
> >+
> >+      pr_debug("resource: unregistered %s\n", resp->name);
> >+
> >+      return 0;
> >+}
> >+EXPORT_SYMBOL(resource_unregister);
> >+
> >+/**
> >+ * resource_request - Request for a required level of a resource
> >+ * @name: The name of the resource requested
> >+ * @dev: Uniquely identifes the caller
> >+ * @level: The requested level for the resource
> >+ *
> >+ * This function recomputes the target level of the 
> resource based on
> >+ * the level requested by the user. The level of the resource is
> >+ * changed to the target level, if it is not the same as 
> the existing level
> >+ * of the resource. Multiple calls to this function by the 
> same device will
> >+ * replace the previous level requested
> >+ * Returns 0 on success, -EINVAL if the resource name 
> passed in invalid.
> >+ * -ENOMEM if no static pool available or dynamic allocations fails.
> >+ * Else returns a non-zero error value returned by one of 
> the failing
> >+ * shared_resource_ops.
> >+ */
> >+int resource_request(const char *name, struct device *dev,
> >+                                      unsigned long level)
> >+{
> >+      struct shared_resource *resp;
> >+      struct  users_list *user;
> >+      int     found = 0, ret = 0;
> >+      unsigned long flags;
> >+
> >+      spin_lock_irqsave(&res_lock, flags);
> >+      resp = _resource_lookup(name);
> >+      if (!resp) {
> >+              printk(KERN_ERR "resource_request: Invalid 
> resource name\n");
> >+              ret = -EINVAL;
> >+              goto res_unlock;
> >+      }
> >+
> >+      /* Call the resource specific validate function */
> >+      if (resp->ops->validate_level) {
> >+              ret = resp->ops->validate_level(resp, level);
> >+              if (ret)
> >+                      goto res_unlock;
> >+      }
> >+
> >+      list_for_each_entry(user, &resp->users_list, node) {
> >+              if (user->dev == dev) {
> >+                      found = 1;
> >+                      break;
> >+              }
> >+      }
> >+
> >+      if (!found) {
> >+              /* First time user */
> >+              user = get_user();
> >+              if (IS_ERR(user)) {
> >+                      ret = -ENOMEM;
> >+                      goto res_unlock;
> >+              }
> >+              user->dev = dev;
> >+              list_add(&user->node, &resp->users_list);
> >+              resp->no_of_users++;
> >+      }
> >+      user->level = level;
> >+
> >+      /* Recompute and set the current level for the resource */
> >+      ret = update_resource_level(resp);
> >+res_unlock:
> >+      spin_unlock_irqrestore(&res_lock, flags);
> >+      return ret;
> >+}
> >+EXPORT_SYMBOL(resource_request);
> >+
> >+/**
> >+ * resource_release - Release a previously requested level 
> of a resource
> >+ * @name: The name of the resource to be released
> >+ * @dev: Uniquely identifes the caller
> >+ *
> >+ * This function recomputes the target level of the 
> resource after removing
> >+ * the level requested by the user. The level of the resource is
> >+ * changed to the target level, if it is not the same as 
> the existing level
> >+ * of the resource.
> >+ * Returns 0 on success, -EINVAL if the resource name or 
> dev structure
> >+ * is invalid.
> >+ */
> >+int resource_release(const char *name, struct device *dev)
> >+{
> >+      struct shared_resource *resp;
> >+      struct users_list *user;
> >+      int found = 0, ret = 0;
> >+      unsigned long flags;
> >+
> >+      spin_lock_irqsave(&res_lock, flags);
> >+      resp = _resource_lookup(name);
> >+      if (!resp) {
> >+              printk(KERN_ERR "resource_release: Invalid 
> resource name\n");
> >+              ret = -EINVAL;
> >+              goto res_unlock;
> >+      }
> >+
> >+      list_for_each_entry(user, &resp->users_list, node) {
> >+              if (user->dev == dev) {
> >+                      found = 1;
> >+                      break;
> >+              }
> >+      }
> >+
> >+      if (!found) {
> >+              /* No such user exists */
> >+              ret = -EINVAL;
> >+              goto res_unlock;
> >+      }
> >+
> >+      resp->no_of_users--;
> >+      list_del(&user->node);
> >+      free_user(user);
> >+
> >+      /* Recompute and set the current level for the resource */
> >+      ret = update_resource_level(resp);
> >+res_unlock:
> >+      spin_unlock_irqrestore(&res_lock, flags);
> >+      return ret;
> >+}
> >+EXPORT_SYMBOL(resource_release);
> >+
> >+/**
> >+ * resource_get_level - Returns the current level of the resource
> >+ * @name: Name of the resource
> >+ *
> >+ * Returns the current level of the resource if found, else returns
> >+ * -EINVAL if the resource name is invalid.
> >+ */
> >+int resource_get_level(const char *name)
> >+{
> >+      struct shared_resource *resp;
> >+      u32 ret;
> >+      unsigned long flags;
> >+
> >+      spin_lock_irqsave(&res_lock, flags);
> >+      resp = _resource_lookup(name);
> >+      if (!resp) {
> >+              printk(KERN_ERR "resource_release: Invalid 
> resource name\n");
> >+              spin_unlock_irqrestore(&res_lock, flags);
> >+              return -EINVAL;
> >+      }
> >+      ret = resp->curr_level;
> >+      spin_unlock_irqrestore(&res_lock, flags);
> >+      return ret;
> >+}
> >+EXPORT_SYMBOL(resource_get_level);
> >Index: linux-omap-2.6/arch/arm/plat-omap/include/mach/resource.h
> >================================================================
> >===
> >--- /dev/null  1970-01-01 00:00:00.000000000 +0000
> >+++ 
> linux-omap-2.6/arch/arm/plat-omap/include/mach/resource.h  2008-10-16
> >18:01:57.000000000 +0530
> >@@ -0,0 +1,79 @@
> >+/*
> >+ * linux/include/asm-arm/arch-omap/resource.h
> >+ * Structure definitions for Shared resource Framework
> >+ *
> >+ * Copyright (C) 2007-2008 Texas Instruments, Inc.
> >+ * Written by Rajendra Nayak <rnayak@xxxxxx>
> >+ *
> >+ * This program is free software; you can redistribute it 
> and/or modify
> >+ * it under the terms of the GNU General Public License version 2 as
> >+ * published by the Free Software Foundation.
> >+ *
> >+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
> >+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
> >+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
> >PURPOSE.
> >+ *
> >+ * History:
> >+ *
> >+ */
> >+
> >+#ifndef __ARCH_ARM_OMAP_RESOURCE_H
> >+#define __ARCH_ARM_OMAP_RESOURCE_H
> >+
> >+#include <linux/list.h>
> >+#include <linux/mutex.h>
> >+#include <linux/device.h>
> >+#include <mach/cpu.h>
> >+
> >+#define RES_DEFAULTLEVEL      0x0
> >+
> >+struct shared_resource_ops; /* forward declaration */
> >+
> >+/* Used to model a Shared Multilevel Resource */
> >+struct shared_resource {
> >+      /* Resource name */
> >+      const char *name;
> >+      /* Used to represent the OMAP chip types containing 
> this res */
> >+      const struct omap_chip_id omap_chip;
> >+      /* Total no of users at any point of this resource */
> >+      u8 no_of_users;
> >+      /* Current level of this resource */
> >+      u32 curr_level;
> >+      /* Used to store any resource specific data */
> >+      void  *resource_data;
> >+      /* List of all the current users for this resource */
> >+      struct list_head users_list;
> >+      /* Shared resource operations */
> >+      struct shared_resource_ops *ops;
> >+      struct list_head node;
> >+};
> >+
> >+struct shared_resource_ops {
> >+      /* Init function for the resource */
> >+      void (*init)(struct shared_resource *res);
> >+      /* Function to change the level of the resource */
> >+      int (*change_level)(struct shared_resource *res, u32 
> target_level);
> >+      /* Function to validate the requested level of the resource */
> >+      int (*validate_level)(struct shared_resource *res, 
> u32 target_level);
> >+};
> >+
> >+/* Used to represent a user of a shared resource */
> >+struct users_list {
> >+      /* Device pointer used to uniquely identify the user */
> >+      struct device *dev;
> >+      /* Current level as requested for the resource by the user */
> >+      u32 level;
> >+      struct list_head node;
> >+      u8 usage;
> >+};
> >+
> >+/* Shared resource Framework API's */
> >+void resource_init(struct shared_resource **resources);
> >+int resource_register(struct shared_resource *res);
> >+int resource_unregister(struct shared_resource *res);
> >+int resource_request(const char *name, struct device *dev,
> >+                                               unsigned long level);
> >+int resource_release(const char *name, struct device *dev);
> >+int resource_get_level(const char *name);
> >+
> >+#endif /* __ARCH_ARM_OMAP_RESOURCE_H */
> >
> >--
> >To unsubscribe from this list: send the line "unsubscribe 
> linux-omap" in
> >the body of a message to majordomo@xxxxxxxxxxxxxxx
> >More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux