All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alan Jenkins <alan-jenkins@tuffmail.co.uk>
To: linux-hotplug@vger.kernel.org
Subject: [PATCH 3/3] sysfs: add explicit sysfs_cache object
Date: Thu, 04 Sep 2008 14:22:54 +0000	[thread overview]
Message-ID: <48BFEF3E.3070006@tuffmail.co.uk> (raw)

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;



             reply	other threads:[~2008-09-04 14:22 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-09-04 14:22 Alan Jenkins [this message]
2008-09-09 13:04 ` [PATCH 3/3] sysfs: add explicit sysfs_cache object Alan Jenkins

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=48BFEF3E.3070006@tuffmail.co.uk \
    --to=alan-jenkins@tuffmail.co.uk \
    --cc=linux-hotplug@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.