[PATCH 3/3] sysfs: add explicit sysfs_cache object

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

 



The implicit caching in sysfs is unsuitable for long-lived programs;
it would fill up with stale data.  Fortunately libudev doesn't use the
relevant functions, but this is not very clear.

Add an explicit sysfs_cache parameter to the relevant functions
(sysfs_attr_get_value, sysfs_device_get and variants).  The parameter
can be NULL for no caching.

sysfs_device objects returned by these functions must now be explicitly
released by calling sysfs_device_cleanup().

This makes traversing parent devices quite hairy, so we hide the details in
udev_device_parent_iter_*.

diff --git a/extras/usb_id/usb_id.c b/extras/usb_id/usb_id.c
index fd4d2f2..f8e38f0 100644
--- a/extras/usb_id/usb_id.c
+++ b/extras/usb_id/usb_id.c
@@ -228,33 +228,34 @@ static int usb_id(const char *devpath)
 	char if_subclass[NAME_SIZE];
 	int if_class_num;
 	int protocol = 0;
+	int retval = 1;
 	int status;
 
 	dbg("devpath %s\n", devpath);
 
 	/* get all usb specific information: dev_interface, if_class, dev_usb */
-	dev = sysfs_device_get(devpath);
+	dev = sysfs_device_get(NULL, devpath);
 	if (dev == NULL) {
 		err("unable to access '%s'\n", devpath);
-		return 1;
+		goto out;
 	}
 
 	/* usb interface directory */
-	dev_interface = sysfs_device_get_parent_with_subsystem(dev, "usb");
+	dev_interface = sysfs_device_get_parent_with_subsystem(NULL, dev, "usb");
 	if (dev_interface == NULL) {
 		info("unable to access usb_interface device of '%s'\n", devpath);
-		return 1;
+		goto out;
 	}
 
-	status = sysfs_attr_get_value(dev_interface->devpath, "bInterfaceClass",
+	status = sysfs_attr_get_value(NULL, dev_interface->devpath, "bInterfaceClass",
 				      if_class, sizeof(if_class));
 	if (!status) {
 		info("%s: cannot get bInterfaceClass attribute\n", dev_interface->kernel);
-		return 1;
+		goto out;
 	}
 	if_class_num = strtoul(if_class, NULL, 16);
 	if (if_class_num == 8) {
-		status = sysfs_attr_get_value(dev_interface->devpath, "bInterfaceSubClass",
+		status = sysfs_attr_get_value(NULL, dev_interface->devpath, "bInterfaceSubClass",
 					      if_subclass, sizeof(if_subclass));
 		if (!status)
 			protocol = set_usb_mass_storage_ifsubtype(type_str, if_subclass, sizeof(type_str)-1);
@@ -264,10 +265,10 @@ static int usb_id(const char *devpath)
 	info("%s: if_class %d protocol %d\n", dev_interface->devpath, if_class_num, protocol);
 
 	/* usb device directory */
-	dev_usb = sysfs_device_get_parent_with_subsystem(dev_interface, "usb");
+	dev_usb = sysfs_device_get_parent_with_subsystem(NULL, dev_interface, "usb");
 	if (!dev_usb) {
 		info("unable to find parent 'usb' device of '%s'\n", devpath);
-		return 1;
+		goto out;
 	}
 
 	/* mass storage */
@@ -280,7 +281,7 @@ static int usb_id(const char *devpath)
 		int host, bus, target, lun;
 
 		/* get scsi device */
-		dev_scsi = sysfs_device_get_parent_with_subsystem(dev, "scsi");
+		dev_scsi = sysfs_device_get_parent_with_subsystem(NULL, dev, "scsi");
 		if (dev_scsi == NULL) {
 			info("unable to find parent 'scsi' device of '%s'\n", devpath);
 			goto fallback;
@@ -291,7 +292,7 @@ static int usb_id(const char *devpath)
 		}
 
 		/* Generic SPC-2 device */
-		status = sysfs_attr_get_value(dev_scsi->devpath, "vendor",
+		status = sysfs_attr_get_value(NULL, dev_scsi->devpath, "vendor",
 					      scsi_vendor, sizeof(scsi_vendor));
 		if (!status) {
 			info("%s: cannot get SCSI vendor attribute\n", dev_scsi->kernel);
@@ -299,7 +300,7 @@ static int usb_id(const char *devpath)
 		}
 		set_str(vendor_str, scsi_vendor, sizeof(vendor_str)-1);
 
-		status = sysfs_attr_get_value(dev_scsi->devpath, "model",
+		status = sysfs_attr_get_value(NULL, dev_scsi->devpath, "model",
 					      scsi_model, sizeof(scsi_model));
 		if (!status) {
 			info("%s: cannot get SCSI model attribute\n", dev_scsi->kernel);
@@ -307,7 +308,7 @@ static int usb_id(const char *devpath)
 		}
 		set_str(model_str, scsi_model, sizeof(model_str)-1);
 
-		status = sysfs_attr_get_value(dev_scsi->devpath, "type",
+		status = sysfs_attr_get_value(NULL, dev_scsi->devpath, "type",
 					      scsi_type, sizeof(scsi_type));
 		if (!status) {
 			info("%s: cannot get SCSI type attribute\n", dev_scsi->kernel);
@@ -315,7 +316,7 @@ static int usb_id(const char *devpath)
 		}
 		set_scsi_type(type_str, scsi_type, sizeof(type_str)-1);
 
-		status = sysfs_attr_get_value(dev_scsi->devpath, "rev",
+		status = sysfs_attr_get_value(NULL, dev_scsi->devpath, "rev",
 					      scsi_rev, sizeof(scsi_rev));
 		if (!status) {
 			info("%s: cannot get SCSI revision attribute\n", dev_scsi->kernel);
@@ -337,16 +338,16 @@ fallback:
 
 		status = 0;
 		if (!use_num_info)
-			status = sysfs_attr_get_value(dev_usb->devpath, "manufacturer",
+			status = sysfs_attr_get_value(NULL, dev_usb->devpath, "manufacturer",
 						      usb_vendor, sizeof(usb_vendor));
 
 		if (!status)
-			status = sysfs_attr_get_value(dev_usb->devpath, "idVendor",
+			status = sysfs_attr_get_value(NULL, dev_usb->devpath, "idVendor",
 						      usb_vendor, sizeof(usb_vendor));
 
 		if (!status) {
 			info("No USB vendor information available\n");
-			return 1;
+			goto out;
 		}
 		set_str(vendor_str, usb_vendor, sizeof(vendor_str)-1);
 	}
@@ -356,16 +357,16 @@ fallback:
 
 		status = 0;
 		if (!use_num_info)
-			status = sysfs_attr_get_value(dev_usb->devpath, "product",
+			status = sysfs_attr_get_value(NULL, dev_usb->devpath, "product",
 						      usb_model, sizeof(usb_model));
 
 		if (!status)
-			status = sysfs_attr_get_value(dev_usb->devpath, "idProduct",
+			status = sysfs_attr_get_value(NULL, dev_usb->devpath, "idProduct",
 						      usb_model, sizeof(usb_model));
 
 		if (!status) {
 			dbg("No USB model information available\n");
-			return 1;
+			goto out;
 		}
 		set_str(model_str, usb_model, sizeof(model_str)-1);
 	}
@@ -373,7 +374,7 @@ fallback:
 	if (revision_str[0] == '\0') {
 		char usb_rev[NAME_SIZE];
 
-		status = sysfs_attr_get_value(dev_usb->devpath, "bcdDevice",
+		status = sysfs_attr_get_value(NULL, dev_usb->devpath, "bcdDevice",
 					      usb_rev, sizeof(usb_rev));
 		if (status)
 			set_str(revision_str, usb_rev, sizeof(revision_str)-1);
@@ -382,12 +383,20 @@ fallback:
 	if (serial_str[0] == '\0') {
 		char usb_serial[NAME_SIZE];
 
-		status = sysfs_attr_get_value(dev_usb->devpath, "serial",
+		status = sysfs_attr_get_value(NULL, dev_usb->devpath, "serial",
 					      usb_serial, sizeof(usb_serial));
 		if (status)
 			set_str(serial_str, usb_serial, sizeof(serial_str)-1);
 	}
-	return 0;
+
+	/* success */
+	retval = 0;
+
+out:
+	sysfs_device_cleanup(dev);
+	sysfs_device_cleanup(dev_interface);
+	sysfs_device_cleanup(dev_usb);
+	return retval;
 }
 
 int main(int argc, char **argv)
diff --git a/udev/test-udev.c b/udev/test-udev.c
index 07628f7..878c477 100644
--- a/udev/test-udev.c
+++ b/udev/test-udev.c
@@ -62,7 +62,6 @@ static void asmlinkage sig_handler(int signum)
 
 int main(int argc, char *argv[])
 {
-	struct sysfs_device *dev;
 	struct udevice *udev;
 	const char *maj, *min;
 	struct udev_rules rules;
@@ -133,18 +132,23 @@ int main(int argc, char *argv[])
 	sysfs_init();
 	udev_rules_init(&rules, 0);
 
-	dev = sysfs_device_get(devpath);
-	if (dev == NULL) {
-		info("unable to open '%s'\n", devpath);
-		goto fail;
-	}
-
 	udev = udev_device_init();
 	if (udev == NULL)
 		goto fail;
 
+	/* valgrind can only verify sysfs_device_cleanup() calls if cache is disabled */
+#if 0
+	udev->cache = sysfs_cache_init();
+	if (udev->cache == NULL)
+		goto fail;
+#endif
+
 	/* override built-in sysfs device */
-	udev->dev = dev;
+	udev->dev = sysfs_device_get(udev->cache, devpath);
+	if (udev->dev == NULL) {
+		info("unable to open '%s'\n", devpath);
+		goto fail;
+	}
 	strlcpy(udev->action, action, sizeof(udev->action));
 
 	/* get dev_t from environment, which is needed for "remove" to work, "add" works also from sysfs */
@@ -164,8 +168,8 @@ int main(int argc, char *argv[])
 	if (retval == 0 && !udev->ignore_device && udev_run)
 		udev_rules_run(udev);
 
-	udev_device_cleanup(udev);
 fail:
+	udev_device_cleanup(udev);
 	udev_rules_cleanup(&rules);
 	sysfs_cleanup();
 	selinux_exit();
diff --git a/udev/udev.h b/udev/udev.h
index 28b59db..1037734 100644
--- a/udev/udev.h
+++ b/udev/udev.h
@@ -48,9 +48,13 @@
 
 struct udev_rules;
 
+struct sysfs_cache {
+	struct list_head dev_list;		/* device cache */
+	struct list_head attr_list;		/* attribute value cache */
+};
+
 struct sysfs_device {
 	struct list_head node;			/* for device cache */
-	struct sysfs_device *parent;		/* already cached parent*/
 	char devpath[PATH_SIZE];
 	char subsystem[NAME_SIZE];		/* $class, $bus, drivers, module */
 	char kernel[NAME_SIZE];			/* device instance name */
@@ -63,6 +67,7 @@ struct udevice {
 	struct sysfs_device *dev;		/* points to dev_local by default */
 	struct sysfs_device dev_local;
 	struct sysfs_device *dev_parent;	/* current parent device used for matching */
+	struct sysfs_cache *cache;
 	char action[NAME_SIZE];
 	char *devpath_old;
 
@@ -103,6 +108,9 @@ extern void udev_config_init(void);
 /* udev_device.c */
 extern struct udevice *udev_device_init(void);
 extern void udev_device_cleanup(struct udevice *udev);
+extern void udev_device_parents_iter_init(struct udevice *udev, struct sysfs_device **iter);
+extern void udev_device_parents_iter_next(struct udevice *udev, struct sysfs_device **iter);
+extern void udev_device_parents_iter_cleanup(struct udevice *udev, struct sysfs_device **iter);
 extern dev_t udev_device_get_devt(struct udevice *udev);
 
 /* udev_device_event.c */
@@ -112,12 +120,17 @@ extern int udev_device_event(struct udev_rules *rules, struct udevice *udev);
 extern char sysfs_path[PATH_SIZE];
 extern int sysfs_init(void);
 extern void sysfs_cleanup(void);
+extern struct sysfs_cache *sysfs_cache_init(void);
+extern void sysfs_cache_cleanup(struct sysfs_cache *cache);
 extern void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath,
 				    const char *subsystem, const char *driver);
-extern struct sysfs_device *sysfs_device_get(const char *devpath);
-extern struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev);
-extern struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem);
-extern int sysfs_attr_get_value(const char *devpath, const char *attr_name, char *value, size_t len);
+extern struct sysfs_device *sysfs_device_get(struct sysfs_cache *cache, const char *devpath);
+extern struct sysfs_device *sysfs_device_get_parent(struct sysfs_cache *cache, struct sysfs_device *dev);
+extern struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_cache *cache,
+								   struct sysfs_device *dev, const char *subsystem);
+extern void sysfs_device_cleanup(struct sysfs_device *dev);
+int sysfs_attr_get_value(struct sysfs_cache *cache, const char *devpath, const char *attr_name,
+			 char *value, size_t len);
 extern int sysfs_resolve_link(char *path, size_t size);
 extern int sysfs_lookup_devpath_by_subsys_id(char *devpath, size_t len, const char *subsystem, const char *id);
 
diff --git a/udev/udev_device.c b/udev/udev_device.c
index 13000b0..a81d80e 100644
--- a/udev/udev_device.c
+++ b/udev/udev_device.c
@@ -63,19 +63,46 @@ void udev_device_cleanup(struct udevice *udev)
 {
 	if (udev == NULL)
 		return;
+	if (udev->dev != &udev->dev_local)
+		sysfs_device_cleanup(udev->dev);
+	udev_device_parents_iter_cleanup(udev, &udev->dev_parent);
+	sysfs_cache_cleanup(udev->cache);
 	name_list_cleanup(&udev->symlink_list);
 	name_list_cleanup(&udev->run_list);
 	name_list_cleanup(&udev->env_list);
 	free(udev);
 }
 
+void udev_device_parents_iter_init(struct udevice *udev, struct sysfs_device **iter)
+{
+	*iter = udev->dev;
+}
+
+void udev_device_parents_iter_next(struct udevice *udev, struct sysfs_device **iter)
+{
+	struct sysfs_device *dev;
+
+	dev = *iter;
+	*iter = sysfs_device_get_parent(udev->cache, dev);
+
+	if (dev != udev->dev)
+		sysfs_device_cleanup(dev);
+}
+
+void udev_device_parents_iter_cleanup(struct udevice *udev, struct sysfs_device **iter)
+{
+	if (*iter != udev->dev)
+		sysfs_device_cleanup(*iter);
+	*iter = NULL;
+}
+
 dev_t udev_device_get_devt(struct udevice *udev)
 {
 	char attr[NAME_SIZE];
 	unsigned int maj, min;
 
 	/* read it from sysfs  */
-	if (sysfs_attr_get_value(udev->dev->devpath, "dev", attr, sizeof(attr))) {
+	if (sysfs_attr_get_value(udev->cache, udev->dev->devpath, "dev", attr, sizeof(attr))) {
 		if (sscanf(attr, "%u:%u", &maj, &min) == 2)
 			return makedev(maj, min);
 	}
diff --git a/udev/udev_node.c b/udev/udev_node.c
index a29c4b7..f489b67 100644
--- a/udev/udev_node.c
+++ b/udev/udev_node.c
@@ -380,7 +380,7 @@ int udev_node_add(struct udevice *udev)
 		int range;
 
 		/* take the maximum registered minor range */
-		if (sysfs_attr_get_value(udev->dev->devpath, "range", attr, sizeof(attr))) {
+		if (sysfs_attr_get_value(udev->cache, udev->dev->devpath, "range", attr, sizeof(attr))) {
 			range = atoi(attr);
 			if (range > 1)
 				udev->partitions = range-1;
diff --git a/udev/udev_rules.c b/udev/udev_rules.c
index 53c483a..da7a62a 100644
--- a/udev/udev_rules.c
+++ b/udev/udev_rules.c
@@ -416,43 +416,45 @@ static int import_program_into_env(struct udevice *udev, const char *program)
 static int import_parent_into_env(struct udevice *udev, const char *filter)
 {
 	struct sysfs_device *dev_parent;
+	struct udevice *udev_parent;
+	struct name_entry *name_loop;
 	int rc = -1;
 
-	dev_parent = sysfs_device_get_parent(udev->dev);
-	if (dev_parent != NULL) {
-		struct udevice *udev_parent;
-		struct name_entry *name_loop;
+	dev_parent = sysfs_device_get_parent(udev->cache, udev->dev);
+	if (dev_parent == NULL)
+		goto out;
 
-		dbg("found parent '%s', get the node name\n", dev_parent->devpath);
-		udev_parent = udev_device_init();
-		if (udev_parent == NULL)
-			return -1;
-		/* import the udev_db of the parent */
-		if (udev_db_get_device(udev_parent, dev_parent->devpath) == 0) {
-			dbg("import stored parent env '%s'\n", udev_parent->name);
-			list_for_each_entry(name_loop, &udev_parent->env_list, node) {
-				char name[NAME_SIZE];
-				char *pos;
-
-				strlcpy(name, name_loop->name, sizeof(name));
-				pos = strchr(name, '=');
-				if (pos) {
-					pos[0] = '\0';
-					pos++;
-					if (fnmatch(filter, name, 0) == 0) {
-						dbg("import key '%s'\n", name_loop->name);
-						name_list_add(&udev->env_list, name_loop->name, 0);
-						setenv(name, pos, 1);
-					} else
-						dbg("skip key '%s'\n", name_loop->name);
-				}
+	dbg("found parent '%s', get the node name\n", dev_parent->devpath);
+	udev_parent = udev_device_init();
+	if (udev_parent == NULL)
+		goto out;
+	/* import the udev_db of the parent */
+	if (udev_db_get_device(udev_parent, dev_parent->devpath) == 0) {
+		dbg("import stored parent env '%s'\n", udev_parent->name);
+		list_for_each_entry(name_loop, &udev_parent->env_list, node) {
+			char name[NAME_SIZE];
+			char *pos;
+
+			strlcpy(name, name_loop->name, sizeof(name));
+			pos = strchr(name, '=');
+			if (pos) {
+				pos[0] = '\0';
+				pos++;
+				if (fnmatch(filter, name, 0) == 0) {
+					dbg("import key '%s'\n", name_loop->name);
+					name_list_add(&udev->env_list, name_loop->name, 0);
+					setenv(name, pos, 1);
+				} else
+					dbg("skip key '%s'\n", name_loop->name);
 			}
-			rc = 0;
-		} else
-			dbg("parent not found in database\n");
-		udev_device_cleanup(udev_parent);
-	}
+		}
+		rc = 0;
+	} else
+		dbg("parent not found in database\n");
+	udev_device_cleanup(udev_parent);
 
+out:
+	sysfs_device_cleanup(dev_parent);
 	return rc;
 }
 
@@ -834,7 +836,7 @@ found:
 
 				if (attr_get_by_subsys_id(attr, devpath, sizeof(devpath), &attrib)) {
 					if (attrib != NULL)
-						found = sysfs_attr_get_value(devpath, attrib,
+						found = sysfs_attr_get_value(udev->cache, devpath, attrib,
 									     temp2, sizeof(temp2));
 					else
 						break;
@@ -842,20 +844,23 @@ found:
 
 				/* try the current device, other matches may have selected */
 				if (!found && udev->dev_parent != NULL && udev->dev_parent != udev->dev)
-					found = sysfs_attr_get_value(udev->dev_parent->devpath, attr,
+					found = sysfs_attr_get_value(udev->cache, udev->dev_parent->devpath, attr,
 								     temp2, sizeof(temp2));
 				/* look at all devices along the chain of parents */
 				if (!found) {
-					struct sysfs_device *dev_parent = udev->dev;
+					struct sysfs_device *dev_parent;
 
+					udev_device_parents_iter_init(udev, &dev_parent);
 					do {
 						dbg("looking at '%s'\n", dev_parent->devpath);
-						found = sysfs_attr_get_value(dev_parent->devpath, attr,
+						found = sysfs_attr_get_value(udev->cache, dev_parent->devpath, attr,
 									     temp2, sizeof(temp2));
 						if (found)
 							break;
-						dev_parent = sysfs_device_get_parent(dev_parent);
+						udev_device_parents_iter_next(udev, &dev_parent);
 					} while (dev_parent != NULL);
+
+					udev_device_parents_iter_cleanup(udev, &dev_parent);
 				}
 
 				if (!found)
@@ -876,7 +881,7 @@ found:
 			{
 				struct sysfs_device *dev_parent;
 
-				dev_parent = sysfs_device_get_parent(udev->dev);
+				dev_parent = sysfs_device_get_parent(udev->cache, udev->dev);
 				if (dev_parent != NULL) {
 					struct udevice *udev_parent;
 
@@ -891,6 +896,7 @@ found:
 							dbg("parent not found in database\n");
 						udev_device_cleanup(udev_parent);
 					}
+					sysfs_device_cleanup(dev_parent);
 				}
 			}
 			break;
@@ -1142,13 +1148,13 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule)
 
 			if (attr_get_by_subsys_id(key_name, devpath, sizeof(devpath), &attrib)) {
 				if (attrib != NULL)
-					found = sysfs_attr_get_value(devpath, attrib,
+					found = sysfs_attr_get_value(udev->cache, devpath, attrib,
 								     val, sizeof(val));
 				else
 					goto nomatch;
 			}
 			if (!found)
-				found = sysfs_attr_get_value(udev->dev->devpath, key_name,
+				found = sysfs_attr_get_value(udev->cache, udev->dev->devpath, key_name,
 							     val, sizeof(val));
 			if (!found)
 				goto nomatch;
@@ -1168,7 +1174,8 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule)
 	}
 
 	/* walk up the chain of parent devices and find a match */
-	udev->dev_parent = udev->dev;
+	udev_device_parents_iter_cleanup(udev, &udev->dev_parent);
+	udev_device_parents_iter_init(udev, &udev->dev_parent);
 	while (1) {
 		/* check for matching kernel device name */
 		if (match_key("KERNELS", rule, &rule->kernels, udev->dev_parent->kernel))
@@ -1194,10 +1201,10 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule)
 				size_t len;
 				int found;
 
-				found = sysfs_attr_get_value(udev->dev_parent->devpath, key_name,
+				found = sysfs_attr_get_value(udev->cache, udev->dev_parent->devpath, key_name,
 							     val, sizeof(val));
 				if (!found)
-					found = sysfs_attr_get_value(udev->dev->devpath, key_name,
+					found = sysfs_attr_get_value(udev->cache, udev->dev->devpath, key_name,
 								     val, sizeof(val));
 				if (!found)
 					goto try_parent;
@@ -1221,7 +1228,8 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule)
 try_parent:
 		/* move to parent device */
 		dbg("try parent sysfs device\n");
-		udev->dev_parent = sysfs_device_get_parent(udev->dev_parent);
+		udev_device_parents_iter_next(udev, &udev->dev_parent);
+
 		if (udev->dev_parent == NULL)
 			goto nomatch;
 		dbg("looking at dev_parent->devpath='%s'\n", udev->dev_parent->devpath);
diff --git a/udev/udev_sysfs.c b/udev/udev_sysfs.c
index 2d2457f..4028f7e 100644
--- a/udev/udev_sysfs.c
+++ b/udev/udev_sysfs.c
@@ -31,11 +31,6 @@
 
 char sysfs_path[PATH_SIZE];
 
-/* device cache */
-static LIST_HEAD(dev_list);
-
-/* attribute value cache */
-static LIST_HEAD(attr_list);
 struct sysfs_attr {
 	struct list_head node;
 	char path[PATH_SIZE];
@@ -55,27 +50,48 @@ int sysfs_init(void)
 		strlcpy(sysfs_path, "/sys", sizeof(sysfs_path));
 	dbg("sysfs_path='%s'\n", sysfs_path);
 
-	INIT_LIST_HEAD(&dev_list);
-	INIT_LIST_HEAD(&attr_list);
 	return 0;
 }
 
 void sysfs_cleanup(void)
 {
+}
+
+struct sysfs_cache *sysfs_cache_init()
+{
+	struct sysfs_cache *cache;
+
+	cache = malloc(sizeof(struct sysfs_cache));
+	if (cache == NULL)
+		return NULL;
+
+	INIT_LIST_HEAD(&cache->dev_list);
+	INIT_LIST_HEAD(&cache->attr_list);
+
+	return cache;
+}
+
+void sysfs_cache_cleanup(struct sysfs_cache *cache)
+{
 	struct sysfs_attr *attr_loop;
 	struct sysfs_attr *attr_temp;
 	struct sysfs_device *dev_loop;
 	struct sysfs_device *dev_temp;
 
-	list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) {
+	if (cache == NULL)
+		return;
+
+	list_for_each_entry_safe(attr_loop, attr_temp, &cache->attr_list, node) {
 		list_del(&attr_loop->node);
 		free(attr_loop);
 	}
 
-	list_for_each_entry_safe(dev_loop, dev_temp, &dev_list, node) {
+	list_for_each_entry_safe(dev_loop, dev_temp, &cache->dev_list, node) {
 		list_del(&dev_loop->node);
 		free(dev_loop);
 	}
+
+	free(cache);
 }
 
 void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath,
@@ -144,7 +160,7 @@ int sysfs_resolve_link(char *devpath, size_t size)
 	return 0;
 }
 
-struct sysfs_device *sysfs_device_get(const char *devpath)
+struct sysfs_device *sysfs_device_get(struct sysfs_cache *cache, const char *devpath)
 {
 	char path[PATH_SIZE];
 	char devpath_real[PATH_SIZE];
@@ -173,12 +189,14 @@ struct sysfs_device *sysfs_device_get(const char *devpath)
 		return NULL;
 
 	/* look for device already in cache (we never put an untranslated path in the cache) */
-	list_for_each_entry(dev_loop, &dev_list, node) {
+	if (cache != NULL) {
+		list_for_each_entry(dev_loop, &cache->dev_list, node) {
 			if (strcmp(dev_loop->devpath, devpath_real) == 0) {
 				dbg("found in cache '%s'\n", dev_loop->devpath);
 				return dev_loop;
 			}
 		}
+	}
 
 	/* if we got a link, resolve it to the real device */
 	strlcpy(path, sysfs_path, sizeof(path));
@@ -192,13 +210,15 @@ struct sysfs_device *sysfs_device_get(const char *devpath)
 			return NULL;
 
 		/* now look for device in cache after path translation */
-		list_for_each_entry(dev_loop, &dev_list, node) {
+		if (cache != NULL) {
+			list_for_each_entry(dev_loop, &cache->dev_list, node) {
 				if (strcmp(dev_loop->devpath, devpath_real) == 0) {
 					dbg("found in cache '%s'\n", dev_loop->devpath);
 					return dev_loop;
 				}
 			}
 		}
+	}
 
 	/* it is a new device */
 	dbg("new uncached device '%s'\n", devpath_real);
@@ -252,23 +272,22 @@ struct sysfs_device *sysfs_device_get(const char *devpath)
 			strlcpy(dev->driver, &pos[1], sizeof(dev->driver));
 	}
 
-	dbg("add to cache 'devpath=%s', subsystem='%s', driver='%s'\n", dev->devpath, dev->subsystem, dev->driver);
-	list_add(&dev->node, &dev_list);
+	dbg("cache 'devpath=%s', subsystem='%s', driver='%s'\n", dev->devpath, dev->subsystem, dev->driver);
+	if (cache != NULL)
+		list_add(&dev->node, &cache->dev_list);
+	else
+		INIT_LIST_HEAD(&dev->node);
 
 	return dev;
 }
 
-struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev)
+struct sysfs_device *sysfs_device_get_parent(struct sysfs_cache *cache, struct sysfs_device *dev)
 {
 	char parent_devpath[PATH_SIZE];
 	char *pos;
 
 	dbg("open '%s'\n", dev->devpath);
 
-	/* look if we already know the parent */
-	if (dev->parent != NULL)
-		return dev->parent;
-
 	strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath));
 	dbg("'%s'\n", parent_devpath);
 
@@ -295,9 +314,8 @@ struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev)
 	if (pos == NULL || pos == parent_devpath)
 		return NULL;
 
-	/* get parent and remember it */
-	dev->parent = sysfs_device_get(parent_devpath);
-	return dev->parent;
+	/* get parent */
+	return sysfs_device_get(cache, parent_devpath);
 
 device_link:
 	strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath));
@@ -305,25 +323,38 @@ device_link:
 	if (sysfs_resolve_link(parent_devpath, sizeof(parent_devpath)) != 0)
 		return NULL;
 
-	/* get parent and remember it */
-	dev->parent = sysfs_device_get(parent_devpath);
-	return dev->parent;
+	/* get parent */
+	return sysfs_device_get(cache, parent_devpath);
 }
 
-struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem)
+struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_cache *cache,
+							    struct sysfs_device *dev, const char *subsystem)
 {
 	struct sysfs_device *dev_parent;
 
-	dev_parent = sysfs_device_get_parent(dev);
+	dev_parent = sysfs_device_get_parent(cache, dev);
 	while (dev_parent != NULL) {
 		if (strcmp(dev_parent->subsystem, subsystem) == 0)
 			return dev_parent;
-		dev_parent = sysfs_device_get_parent(dev_parent);
+		dev_parent = sysfs_device_get_parent(cache, dev_parent);
 	}
 	return NULL;
 }
 
-int sysfs_attr_get_value(const char *devpath, const char *attr_name, char *value, size_t len)
+void sysfs_device_cleanup(struct sysfs_device *dev)
+{
+	if (dev == NULL)
+		return;
+
+	/* device is on a dev_list - will be freed by sysfs_cache_cleanup() */
+	if (!list_empty(&dev->node))
+		return;
+
+	free(dev);
+}
+
+int sysfs_attr_get_value(struct sysfs_cache *cache, const char *devpath, const char *attr_name,
+			 char *value, size_t len)
 {
 	char path_full[PATH_SIZE];
 	const char *path;
@@ -345,7 +376,8 @@ int sysfs_attr_get_value(const char *devpath, const char *attr_name, char *value
 	strlcat(path_full, attr_name, sizeof(path_full));
 
 	/* look for attribute in cache */
-	list_for_each_entry(attr_loop, &attr_list, node) {
+	if (cache != NULL) {
+		list_for_each_entry(attr_loop, &cache->attr_list, node) {
 			if (strcmp(attr_loop->path, path) == 0) {
 				dbg("found in cache '%s'\n", attr_loop->path);
 				if (!attr_loop->value)
@@ -364,7 +396,8 @@ int sysfs_attr_get_value(const char *devpath, const char *attr_name, char *value
 		memset(attr, 0x00, sizeof(struct sysfs_attr));
 		strlcpy(attr->path, path, sizeof(attr->path));
 		dbg("add to cache '%s'\n", path_full);
-	list_add(&attr->node, &attr_list);
+		list_add(&attr->node, &cache->attr_list);
+	}
 
 	if (lstat(path_full, &statbuf) != 0) {
 		dbg("stat '%s' failed: %s\n", path_full, strerror(errno));
@@ -417,9 +450,11 @@ int sysfs_attr_get_value(const char *devpath, const char *attr_name, char *value
 	remove_trailing_chars(val, '\n');
 
 out:
-	strlcpy(attr->value_local, val, sizeof(attr->value_local));
-	attr->value = attr->value_local;
-	dbg("cache '%s' with link value '%s'\n", path_full, attr->value);
+	if (cache != NULL) {
+		strlcpy(attr->value_local, val, sizeof(attr->value_local));
+		attr->value = attr->value_local;
+		dbg("cache '%s' with link value '%s'\n", path_full, attr->value);
+	}
 
 	if (strlcpy(value, val, len) > len-1)
 		return 0;
diff --git a/udev/udevinfo.c b/udev/udevinfo.c
index b76d11d..50d1bef 100644
--- a/udev/udevinfo.c
+++ b/udev/udevinfo.c
@@ -66,7 +66,7 @@ static void print_all_attributes(const char *devpath, const char *key)
 			if (S_ISLNK(statbuf.st_mode))
 				continue;
 
-			if (!sysfs_attr_get_value(devpath, dent->d_name, value, sizeof(value)))
+			if (!sysfs_attr_get_value(NULL, devpath, dent->d_name, value, sizeof(value)))
 				continue;
 			len = strlen(value);
 			dbg("attr '%s'='%s'(%zi)\n", dent->d_name, value, len);
@@ -89,7 +89,7 @@ static int print_device_chain(const char *devpath)
 {
 	struct sysfs_device *dev;
 
-	dev = sysfs_device_get(devpath);
+	dev = sysfs_device_get(NULL, devpath);
 	if (dev == NULL)
 		return -1;
 
@@ -109,9 +109,14 @@ static int print_device_chain(const char *devpath)
 
 	/* walk up the chain of devices */
 	while (1) {
-		dev = sysfs_device_get_parent(dev);
-		if (dev == NULL)
+		struct sysfs_device *dev_parent;
+
+		dev_parent = sysfs_device_get_parent(NULL, dev);
+		if (dev_parent == NULL)
 			break;
+		sysfs_device_cleanup(dev);
+		dev = dev_parent;
+
 		printf("  looking at parent device '%s':\n", dev->devpath);
 		printf("    KERNELS==\"%s\"\n", dev->kernel);
 		printf("    SUBSYSTEMS==\"%s\"\n", dev->subsystem);
@@ -120,6 +125,7 @@ static int print_device_chain(const char *devpath)
 		print_all_attributes(dev->devpath, "ATTRS");
 	}
 
+	sysfs_device_cleanup(dev);
 	return 0;
 }
 
diff --git a/udev/udevtest.c b/udev/udevtest.c
index 63603aa..cf96019 100644
--- a/udev/udevtest.c
+++ b/udev/udevtest.c
@@ -78,7 +78,6 @@ int udevtest(int argc, char *argv[])
 	const char *subsystem = NULL;
 	const char *devpath = NULL;
 	struct udevice *udev;
-	struct sysfs_device *dev;
 	struct udev_rules rules = {};
 	int retval;
 	int rc = 0;
@@ -150,25 +149,30 @@ int udevtest(int argc, char *argv[])
 	if (strncmp(devpath, sysfs_path, strlen(sysfs_path)) == 0)
 		devpath = &devpath[strlen(sysfs_path)];
 
-	dev = sysfs_device_get(devpath);
-	if (dev == NULL) {
-		fprintf(stderr, "unable to open device '%s'\n", devpath);
+	udev = udev_device_init();
+	if (udev == NULL) {
+		fprintf(stderr, "error initializing device\n");
 		rc = 2;
 		goto exit;
 	}
 
-	udev = udev_device_init();
-	if (udev == NULL) {
-		fprintf(stderr, "error initializing device\n");
+	udev->cache = sysfs_cache_init();
+	if (udev->cache == NULL) {
+		fprintf(stderr, "error initializing sysfs cache\n");
 		rc = 3;
 		goto exit;
 	}
 
+	/* override built-in sysfs device */
+	udev->dev = sysfs_device_get(udev->cache, devpath);
+	if (udev->dev == NULL) {
+		fprintf(stderr, "unable to open device '%s'\n", devpath);
+		rc = 4;
+		goto exit;
+	}
 	if (subsystem != NULL)
-		strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem));
+		strlcpy(udev->dev->subsystem, subsystem, sizeof(udev->dev->subsystem));
 
-	/* override built-in sysfs device */
-	udev->dev = dev;
 	strlcpy(udev->action, action, sizeof(udev->action));
 	udev->devt = udev_device_get_devt(udev);
 
@@ -198,9 +202,9 @@ int udevtest(int argc, char *argv[])
 			info("run: '%s'\n", program);
 		}
 	}
-	udev_device_cleanup(udev);
 
 exit:
+	udev_device_cleanup(udev);
 	udev_rules_cleanup(&rules);
 	sysfs_cleanup();
 	return rc;


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

[Index of Archives]     [Linux Kernel]     [Linux DVB]     [Asterisk Internet PBX]     [DCCP]     [Netdev]     [X.org]     [Util Linux NG]     [Fedora Women]     [ALSA Devel]     [Linux USB]

  Powered by Linux