Blob Blame History Raw
 lib/label/label.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/lib/label/label.c b/lib/label/label.c
index 72be5ec..09bbb92 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -29,6 +29,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <sys/resource.h>
 
 /* FIXME Allow for larger labels?  Restricted to single sector currently */
 
@@ -867,6 +868,57 @@ static void _free_hints(struct dm_list *hints)
 }
 
 /*
+ * We don't know how many of num_devs will be PVs that we need to
+ * keep open, but if it's greater than the soft limit, then we'll
+ * need the soft limit raised, so do that before starting.
+ *
+ * If opens approach the raised soft/hard limit while scanning, then
+ * we could also attempt to raise the soft/hard limits during the scan.
+ */
+
+#define BASE_FD_COUNT 32 /* Number of open files we want apart from devs */
+
+static void _prepare_open_file_limit(struct cmd_context *cmd, unsigned int num_devs)
+{
+	struct rlimit old, new;
+	unsigned int want = num_devs + BASE_FD_COUNT;
+	int rv;
+
+	rv = prlimit(0, RLIMIT_NOFILE, NULL, &old);
+	if (rv < 0) {
+		log_debug("Checking fd limit for num_devs %u failed %d", num_devs, errno);
+		return;
+	}
+
+	log_debug("Checking fd limit for num_devs %u want %u soft %lld hard %lld",
+		  num_devs, want, (long long)old.rlim_cur, (long long)old.rlim_max);
+
+	/* Current soft limit is enough */
+	if (old.rlim_cur > want)
+		return;
+
+	/* Soft limit already raised to max */
+	if (old.rlim_cur == old.rlim_max)
+		return;
+
+	/* Raise soft limit up to hard/max limit */
+	new.rlim_cur = old.rlim_max;
+	new.rlim_max = old.rlim_max;
+
+	log_debug("Setting fd limit for num_devs %u soft %lld hard %lld",
+		  num_devs, (long long)new.rlim_cur, (long long)new.rlim_max);
+
+	rv = prlimit(0, RLIMIT_NOFILE, &new, &old);
+	if (rv < 0) {
+		if (errno == EPERM)
+			log_warn("WARNING: permission error setting open file limit for scanning %u devices.", num_devs);
+		else
+			log_warn("WARNING: cannot set open file limit for scanning %u devices.", num_devs);
+		return;
+	}
+}
+
+/*
  * Scan devices on the system to discover which are LVM devices.
  * Info about the LVM devices (PVs) is saved in lvmcache in a
  * basic/summary form (info/vginfo structs).  The vg_read phase
@@ -984,6 +1036,15 @@ int label_scan(struct cmd_context *cmd)
 	log_debug("Will scan %d devices skip %d", dm_list_size(&scan_devs), dm_list_size(&all_devs));
 
 	/*
+	 * If the total number of devices exceeds the soft open file
+	 * limit, then increase the soft limit to the hard/max limit
+	 * in case the number of PVs in scan_devs (it's only the PVs
+	 * which we want to keep open) is higher than the current
+	 * soft limit.
+	 */
+	_prepare_open_file_limit(cmd, dm_list_size(&scan_devs));
+
+	/*
 	 * Do the main scan.
 	 */
 	_scan_list(cmd, cmd->filter, &scan_devs, NULL);