Re: [PATCH 3/3] Avoid doing extra 'lstat()'s for d_type if we have an up-to-date cache entry

12 messages, 4 authors, 2016-06-15 · open the first message on its own page

Re: [PATCH 3/3] Avoid doing extra 'lstat()'s for d_type if we have an up-to-date cache entry

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:47:02

Linus Torvalds [off-list ref] writes:
We don't really verify the whole path when we mark things ce_uptodate(). 
Part of what read_directory() does is to find directory entries, and in 
the process things like "git add" will notice if there's a conflict with 
existing index entries.

So if a directory has changed into a symlink to a directory, this 
particular optimization will actually hide that, I suspect. I haven't 
tested, though. But it might be worth-while to see what happens when you 
had a directory structure, and then do

	mkdir dir
	touch dir/a
	touch dir/b
	git add dir

	mv dir new-dir
	ln -s new-dir dir
	git status
In existing codepaths, we have "has_symlink_leading_path()" checks to
notice that tracked dir/[ab] have disappeared.  "git diff" before or after
"git status" in the above sequence does notice what you did.

Would dir/a be marked as uptodate in the index, if somebody preloads the
index, after the above sequence?  I hope not.

Re: [PATCH 3/3] Avoid doing extra 'lstat()'s for d_type if we have an up-to-date cache entry

From: Linus Torvalds <torvalds@linux-foundation.org>
Date: 2016-06-15 22:47:02


On Thu, 9 Jul 2009, Junio C Hamano wrote:
Would dir/a be marked as uptodate in the index, if somebody preloads the
index, after the above sequence?  I hope not.
Index preloading does not care about directories. It does the standard

	if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY))
		continue;

and since it's all threaded (and the whole _point_ is that it's threaded), 
it can't do anything fancier. Our lstat cache is _not_ thread-safe.

But preloading isn't even the only thing to do that. All the merge logics 
also just do "ie_match_stat()", as does git checkout, although maybe the 
directory gets validated separately for those cases before recursion.

Looking at "ce_mark_uptodate()", I think diff-lib.c is the only one that 
actually does that whole "has_symlink_leading_path()" thing (in 
"check_removed()").

I guess we could make out lstat cache thread-safe, and have the callers 
pass in a per-thread "struct cache_def *". That would work well enough for 
preloading (and everybody else could just use some random static one and 
pass that in).

Added Kjetil to cc.

			Linus

[PATCH 4/3] Avoid using 'lstat()' to figure out directories

From: Linus Torvalds <torvalds@linux-foundation.org>
Date: 2016-06-15 22:47:02


From: Linus Torvalds <torvalds@linux-foundation.org>
Date: Thu, 9 Jul 2009 13:14:28 -0700
Subject: [PATCH 4/3] Avoid using 'lstat()' to figure out directories

If we have an up-to-date index entry for a file in that directory, we
can know that the directories leading up to that file must be
directories.  No need to do an lstat() on the directory.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---

This is the patch I already sent out earlier. Now it's just numbered. 
There's going to be an additional three patches to actually give the right 
behavior for index preloading, so that we can really say "if CE_UPTODATE 
is set, then the whole directory structure is valid".

 dir.c |   47 ++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 42 insertions(+), 5 deletions(-)
diff --git a/dir.c b/dir.c
index 8a9e7d8..e05b850 100644
--- a/dir.c
+++ b/dir.c
@@ -566,18 +566,55 @@ static int in_pathspec(const char *path, int len, const struct path_simplify *si
 	return 0;
 }
 
+static int get_index_dtype(const char *path, int len)
+{
+	int pos;
+	struct cache_entry *ce;
+
+	ce = cache_name_exists(path, len, 0);
+	if (ce) {
+		if (!ce_uptodate(ce))
+			return DT_UNKNOWN;
+		if (S_ISGITLINK(ce->ce_mode))
+			return DT_DIR;
+		/*
+		 * Nobody actually cares about the
+		 * difference between DT_LNK and DT_REG
+		 */
+		return DT_REG;
+	}
+
+	/* Try to look it up as a directory */
+	pos = cache_name_pos(path, len);
+	if (pos >= 0)
+		return DT_UNKNOWN;
+	pos = -pos-1;
+	while (pos < active_nr) {
+		ce = active_cache[pos++];
+		if (strncmp(ce->name, path, len))
+			break;
+		if (ce->name[len] > '/')
+			break;
+		if (ce->name[len] < '/')
+			continue;
+		if (!ce_uptodate(ce))
+			break;	/* continue? */
+		return DT_DIR;
+	}
+	return DT_UNKNOWN;
+}
+
 static int get_dtype(struct dirent *de, const char *path, int len)
 {
 	int dtype = de ? DTYPE(de) : DT_UNKNOWN;
-	struct cache_entry *ce;
 	struct stat st;
 
 	if (dtype != DT_UNKNOWN)
 		return dtype;
-	ce = cache_name_exists(path, len, 0);
-	if (ce && ce_uptodate(ce))
-		st.st_mode = ce->ce_mode;
-	else if (lstat(path, &st))
+	dtype = get_index_dtype(path, len);
+	if (dtype != DT_UNKNOWN)
+		return dtype;
+	if (lstat(path, &st))
 		return dtype;
 	if (S_ISREG(st.st_mode))
 		return DT_REG;
-- 
1.6.3.3.415.ga8877

[PATCH 5/3] Prepare symlink caching for thread-safety

From: Linus Torvalds <torvalds@linux-foundation.org>
Date: 2016-06-15 22:47:02


From: Linus Torvalds <torvalds@linux-foundation.org>
Date: Thu, 9 Jul 2009 13:23:59 -0700
Subject: [PATCH 5/3] Prepare symlink caching for thread-safety

This doesn't actually change the external interfaces, so they are still
thread-unsafe, but it makes the code internally pass a pointer to a
local 'struct cache_def' around, so that the core code can be made
thread-safe.

The threaded index preloading will want to verify that the paths leading
up to a pathname are all real directories.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---

No real changes, but I renamed the static 'cache' data structure 
'default_cache', and made all the internal functions take a pointer 
instead of using the static version.

The functions with external linkage are left semantically unchanged by 
just making them do a simple

	struct cache_def *cache = &default_cache;

and then using that.

 symlinks.c |   75 ++++++++++++++++++++++++++++++++----------------------------
 1 files changed, 40 insertions(+), 35 deletions(-)
diff --git a/symlinks.c b/symlinks.c
index 8dcd632..08ad353 100644
--- a/symlinks.c
+++ b/symlinks.c
@@ -38,13 +38,13 @@ static struct cache_def {
 	int flags;
 	int track_flags;
 	int prefix_len_stat_func;
-} cache;
+} default_cache;
 
-static inline void reset_lstat_cache(void)
+static inline void reset_lstat_cache(struct cache_def *cache)
 {
-	cache.path[0] = '\0';
-	cache.len = 0;
-	cache.flags = 0;
+	cache->path[0] = '\0';
+	cache->len = 0;
+	cache->flags = 0;
 	/*
 	 * The track_flags and prefix_len_stat_func members is only
 	 * set by the safeguard rule inside lstat_cache()
@@ -70,23 +70,23 @@ static inline void reset_lstat_cache(void)
  * of the prefix, where the cache should use the stat() function
  * instead of the lstat() function to test each path component.
  */
-static int lstat_cache(const char *name, int len,
+static int lstat_cache(struct cache_def *cache, const char *name, int len,
 		       int track_flags, int prefix_len_stat_func)
 {
 	int match_len, last_slash, last_slash_dir, previous_slash;
 	int match_flags, ret_flags, save_flags, max_len, ret;
 	struct stat st;
 
-	if (cache.track_flags != track_flags ||
-	    cache.prefix_len_stat_func != prefix_len_stat_func) {
+	if (cache->track_flags != track_flags ||
+	    cache->prefix_len_stat_func != prefix_len_stat_func) {
 		/*
 		 * As a safeguard rule we clear the cache if the
 		 * values of track_flags and/or prefix_len_stat_func
 		 * does not match with the last supplied values.
 		 */
-		reset_lstat_cache();
-		cache.track_flags = track_flags;
-		cache.prefix_len_stat_func = prefix_len_stat_func;
+		reset_lstat_cache(cache);
+		cache->track_flags = track_flags;
+		cache->prefix_len_stat_func = prefix_len_stat_func;
 		match_len = last_slash = 0;
 	} else {
 		/*
@@ -94,10 +94,10 @@ static int lstat_cache(const char *name, int len,
 		 * the 2 "excluding" path types.
 		 */
 		match_len = last_slash =
-			longest_path_match(name, len, cache.path, cache.len,
+			longest_path_match(name, len, cache->path, cache->len,
 					   &previous_slash);
-		match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK);
-		if (match_flags && match_len == cache.len)
+		match_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK);
+		if (match_flags && match_len == cache->len)
 			return match_flags;
 		/*
 		 * If we now have match_len > 0, we would know that
@@ -121,18 +121,18 @@ static int lstat_cache(const char *name, int len,
 	max_len = len < PATH_MAX ? len : PATH_MAX;
 	while (match_len < max_len) {
 		do {
-			cache.path[match_len] = name[match_len];
+			cache->path[match_len] = name[match_len];
 			match_len++;
 		} while (match_len < max_len && name[match_len] != '/');
 		if (match_len >= max_len && !(track_flags & FL_FULLPATH))
 			break;
 		last_slash = match_len;
-		cache.path[last_slash] = '\0';
+		cache->path[last_slash] = '\0';
 
 		if (last_slash <= prefix_len_stat_func)
-			ret = stat(cache.path, &st);
+			ret = stat(cache->path, &st);
 		else
-			ret = lstat(cache.path, &st);
+			ret = lstat(cache->path, &st);
 
 		if (ret) {
 			ret_flags = FL_LSTATERR;
@@ -156,9 +156,9 @@ static int lstat_cache(const char *name, int len,
 	 */
 	save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK);
 	if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) {
-		cache.path[last_slash] = '\0';
-		cache.len = last_slash;
-		cache.flags = save_flags;
+		cache->path[last_slash] = '\0';
+		cache->len = last_slash;
+		cache->flags = save_flags;
 	} else if ((track_flags & FL_DIR) &&
 		   last_slash_dir > 0 && last_slash_dir <= PATH_MAX) {
 		/*
@@ -172,11 +172,11 @@ static int lstat_cache(const char *name, int len,
 		 * can still cache the path components before the last
 		 * one (the found symlink or non-existing component).
 		 */
-		cache.path[last_slash_dir] = '\0';
-		cache.len = last_slash_dir;
-		cache.flags = FL_DIR;
+		cache->path[last_slash_dir] = '\0';
+		cache->len = last_slash_dir;
+		cache->flags = FL_DIR;
 	} else {
-		reset_lstat_cache();
+		reset_lstat_cache(cache);
 	}
 	return ret_flags;
 }
@@ -188,16 +188,17 @@ static int lstat_cache(const char *name, int len,
 void invalidate_lstat_cache(const char *name, int len)
 {
 	int match_len, previous_slash;
+	struct cache_def *cache = &default_cache;	/* FIXME */
 
-	match_len = longest_path_match(name, len, cache.path, cache.len,
+	match_len = longest_path_match(name, len, cache->path, cache->len,
 				       &previous_slash);
 	if (len == match_len) {
-		if ((cache.track_flags & FL_DIR) && previous_slash > 0) {
-			cache.path[previous_slash] = '\0';
-			cache.len = previous_slash;
-			cache.flags = FL_DIR;
+		if ((cache->track_flags & FL_DIR) && previous_slash > 0) {
+			cache->path[previous_slash] = '\0';
+			cache->len = previous_slash;
+			cache->flags = FL_DIR;
 		} else {
-			reset_lstat_cache();
+			reset_lstat_cache(cache);
 		}
 	}
 }
@@ -207,7 +208,8 @@ void invalidate_lstat_cache(const char *name, int len)
  */
 void clear_lstat_cache(void)
 {
-	reset_lstat_cache();
+	struct cache_def *cache = &default_cache;	/* FIXME */
+	reset_lstat_cache(cache);
 }
 
 #define USE_ONLY_LSTAT  0
@@ -217,7 +219,8 @@ void clear_lstat_cache(void)
  */
 int has_symlink_leading_path(const char *name, int len)
 {
-	return lstat_cache(name, len,
+	struct cache_def *cache = &default_cache;	/* FIXME */
+	return lstat_cache(cache, name, len,
 			   FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) &
 		FL_SYMLINK;
 }
@@ -228,7 +231,8 @@ int has_symlink_leading_path(const char *name, int len)
  */
 int has_symlink_or_noent_leading_path(const char *name, int len)
 {
-	return lstat_cache(name, len,
+	struct cache_def *cache = &default_cache;	/* FIXME */
+	return lstat_cache(cache, name, len,
 			   FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) &
 		(FL_SYMLINK|FL_NOENT);
 }
@@ -242,7 +246,8 @@ int has_symlink_or_noent_leading_path(const char *name, int len)
  */
 int has_dirs_only_path(const char *name, int len, int prefix_len)
 {
-	return lstat_cache(name, len,
+	struct cache_def *cache = &default_cache;	/* FIXME */
+	return lstat_cache(cache, name, len,
 			   FL_DIR|FL_FULLPATH, prefix_len) &
 		FL_DIR;
 }
-- 
1.6.3.3.415.ga8877

[PATCH 6/3] Export thread-safe version of 'has_symlink_leading_path()'

From: Linus Torvalds <torvalds@linux-foundation.org>
Date: 2016-06-15 22:47:02


From: Linus Torvalds <torvalds@linux-foundation.org>
Date: Thu, 9 Jul 2009 13:35:31 -0700
Subject: [PATCH 6/3] Export thread-safe version of 'has_symlink_leading_path()'

The threaded index preloading will want it, so that it can avoid
locking by simply using a per-thread symlink/directory cache.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
This just exposes a thread-safe version of the symlink checking by 
allowing a caller to pass in its own local 'struct cache_def' to the 
function.

No users of this yet, but the next step is trivial and obvious..

 cache.h    |   10 ++++++++++
 symlinks.c |   21 ++++++++++-----------
 2 files changed, 20 insertions(+), 11 deletions(-)
diff --git a/cache.h b/cache.h
index 871c984..f1e5ede 100644
--- a/cache.h
+++ b/cache.h
@@ -744,7 +744,17 @@ struct checkout {
 };
 
 extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
+
+struct cache_def {
+	char path[PATH_MAX + 1];
+	int len;
+	int flags;
+	int track_flags;
+	int prefix_len_stat_func;
+};
+
 extern int has_symlink_leading_path(const char *name, int len);
+extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
 extern int has_symlink_or_noent_leading_path(const char *name, int len);
 extern int has_dirs_only_path(const char *name, int len, int prefix_len);
 extern void invalidate_lstat_cache(const char *name, int len);
diff --git a/symlinks.c b/symlinks.c
index 08ad353..4bdded3 100644
--- a/symlinks.c
+++ b/symlinks.c
@@ -32,13 +32,7 @@ static int longest_path_match(const char *name_a, int len_a,
 	return match_len;
 }
 
-static struct cache_def {
-	char path[PATH_MAX + 1];
-	int len;
-	int flags;
-	int track_flags;
-	int prefix_len_stat_func;
-} default_cache;
+static struct cache_def default_cache;
 
 static inline void reset_lstat_cache(struct cache_def *cache)
 {
@@ -217,12 +211,17 @@ void clear_lstat_cache(void)
 /*
  * Return non-zero if path 'name' has a leading symlink component
  */
+int threaded_has_symlink_leading_path(struct cache_def *cache, const char *name, int len)
+{
+	return lstat_cache(cache, name, len, FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & FL_SYMLINK;
+}
+
+/*
+ * Return non-zero if path 'name' has a leading symlink component
+ */
 int has_symlink_leading_path(const char *name, int len)
 {
-	struct cache_def *cache = &default_cache;	/* FIXME */
-	return lstat_cache(cache, name, len,
-			   FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) &
-		FL_SYMLINK;
+	return threaded_has_symlink_leading_path(&default_cache, name, len);
 }
 
 /*
-- 
1.6.3.3.415.ga8877

[PATCH 7/3] Make index preloading check the whole path to the file

From: Linus Torvalds <torvalds@linux-foundation.org>
Date: 2016-06-15 22:47:02

From: Linus Torvalds <torvalds@linux-foundation.org>
Date: Thu, 9 Jul 2009 13:37:02 -0700
Subject: [PATCH 7/3] Make index preloading check the whole path to the file

This uses the new thread-safe 'threaded_has_symlink_leading_path()'
function to efficiently verify that the whole path leading up to the
filename is a proper path, and does not contain symlinks.

This makes 'ce_uptodate()' a much stronger guarantee: it no longer just
guarantees that the 'lstat()' of the path would match, it also means
that we know that people haven't played games with moving directories
around and covered it up with symlinks.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---

Totally trivial, now that we have a thread-safe symlink checker.

If we have leading symlinks in the cache-entry path, we will refuse to 
mark it up-to-date. There's no need to even try to stat anything under 
that directory.

 preload-index.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/preload-index.c b/preload-index.c
index 88edc5f..c3462dc 100644
--- a/preload-index.c
+++ b/preload-index.c
@@ -34,6 +34,7 @@ static void *preload_thread(void *_data)
 	struct thread_data *p = _data;
 	struct index_state *index = p->index;
 	struct cache_entry **cep = index->cache + p->offset;
+	struct cache_def cache;
 
 	nr = p->nr;
 	if (nr + p->offset > index->cache_nr)
@@ -49,6 +50,8 @@ static void *preload_thread(void *_data)
 			continue;
 		if (!ce_path_match(ce, p->pathspec))
 			continue;
+		if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
+			continue;
 		if (lstat(ce->name, &st))
 			continue;
 		if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY))
-- 
1.6.3.3.415.ga8877

Re: [PATCH 7/3] Make index preloading check the whole path to the file

From: Linus Torvalds <torvalds@linux-foundation.org>
Date: 2016-06-15 22:47:02


Ok, with these patches, the strace of the index preload looks very clean, 
and has the required tests for the directory components too:

	...
	26504 lstat("connect.c", {st_mode=S_IFREG|0664, st_size=14312, ...}) = 0
	26504 lstat("contrib", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
	26504 lstat("contrib/README", {st_mode=S_IFREG|0664, st_size=2113, ...}) = 0
	26504 lstat("contrib/blameview", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
	26504 lstat("contrib/blameview/blameview.perl", {st_mode=S_IFREG|0775, st_size=3776, ...}) = 0
	...

ie now it actualyl verifies that the directories leading up to filenames 
are really directories by doing lstat() on them. And the symlink cache 
means that it doesn't do it for every single pathname, only for the first 
lookup per thread and directory.

Maybe Kjetil wants to check the changes, but quite frankly, it looked 
pretty trivial to make that whole has_symlink_leading_path() be 
thread-safe.

			Linus

Re: [PATCH 4/3] Avoid using 'lstat()' to figure out directories

From: Paolo Bonzini <hidden>
Date: 2016-06-15 22:47:02

+		if (ce->name[len]>  '/')
+			break;
+		if (ce->name[len]<  '/')
+			continue;
What about

	if (ce->name[len] < '/') {
		if (strchr(ce->name + len + 1, '/'))
			break;
		else
			continue;
	}

to just punt if we'd go into a directory?  I'm not much worried about 
accessing foo-0001, foo-0002, foo-0003 while looking for foo/a (that 
would be O(number of files in a directory), which is bearable), but 
risking to go down a huge subtree is not very nice.

Paolo

Re: [PATCH 4/3] Avoid using 'lstat()' to figure out directories

From: Linus Torvalds <torvalds@linux-foundation.org>
Date: 2016-06-15 22:47:02

On Fri, 10 Jul 2009, Paolo Bonzini wrote:
I'm not much worried about accessing foo-0001, foo-0002, foo-0003 while 
looking for foo/a (that would be O(number of files in a directory), 
which is bearable), but risking to go down a huge subtree is not very 
nice.
That sounds rather unlikely, and the thing is, even if it were to happen, 
it really wouldn't be that slow. Our data structures are pretty efficient, 
and it wouldn't be _that_ slow to traverse them.

That said, I don't love that loop. It would be better to do that whole 
cache_name_pos() call with the '/' simply appended to the path, and then 
we'd do the binary search directly to the first entry.

Of course, since 'path' is a 'const char *', we'd need to either do a 
silly copy, or we'd need to change a whole lot of the code to make it 
clear that we can actually add a slash to the end (which we can: I think 
it's already always going to be an array that we _will_ add a slash to in 
case it turns out to be a directory).

So there's definitely room for improvement there. I just think that the 
improvement isn't the patch you suggest.

			Linus

Re: [PATCH 4/3] Avoid using 'lstat()' to figure out directories

From: Linus Torvalds <torvalds@linux-foundation.org>
Date: 2016-06-15 22:47:02


On Thu, 9 Jul 2009, Linus Torvalds wrote:
Of course, since 'path' is a 'const char *', we'd need to either do a 
silly copy, or we'd need to change a whole lot of the code to make it 
clear that we can actually add a slash to the end (which we can: I think 
it's already always going to be an array that we _will_ add a slash to in 
case it turns out to be a directory).
No, I was wrong. We really do give it an array that we can't change 
through the 'excluded()' function.

So we'd need to do the whole "copy name and add '/' at the end" thing. But 
the upside would then be that after that, we'd not need any looping to 
find the right ce. So it might be the right thing to do despite the 
extra copy.

			Linus

Re: [PATCH 4/3] Avoid using 'lstat()' to figure out directories

From: Linus Torvalds <torvalds@linux-foundation.org>
Date: 2016-06-15 22:47:02


On Thu, 9 Jul 2009, Linus Torvalds wrote:
So we'd need to do the whole "copy name and add '/' at the end" thing. But 
the upside would then be that after that, we'd not need any looping to 
find the right ce. So it might be the right thing to do despite the 
extra copy.
Naah. I did the numbers. For any normal repository, the 'loop' is going to 
hit exactly once. Trying to be smarter about the initial binary search 
isn't going to help, and copying the pathname around is only going to 
hurt.

In the Linux repo, there's a small handful of cases like this, eg

 - "arch/x86/vdso32"
	arch/x86/vdso/vdso32-setup.c
	arch/x86/vdso/vdso32.S
	arch/x86/vdso/vdsp32/
 - "drivers/scsi/megaraid"
	drivers/scsi/megaraid.c
	drivers/scsi/megaraid.h
	drivers/scsi/megaraid/
 - "include/linux/i2c"
	include/linux/i2c-algo-bit.h
	include/linux/i2c-algo-pca.h
	include/linux/i2c-algo-pcf.h
	include/linux/i2c-dev.h
	include/linux/i2c-gpio.h
	include/linux/i2c-id.h
	include/linux/i2c-ocores.h
	include/linux/i2c-pca-platform.h
	include/linux/i2c-pnx.h
	include/linux/i2c-pxa.h
	include/linux/i2c.h
	include/linux/i2c/

etc (for a total of 45 cases in the whole kernel, if I did my script 
right), where we'd loop a few times. But we'd spend more effort trying to 
avoid looping than we spend now on the loop.

		Linus

Re: [PATCH 6/3] Export thread-safe version of 'has_symlink_leading_path()'

From: Kjetil Barvik <hidden>
Date: 2016-06-15 22:47:03

Linus Torvalds [off-list ref] writes:
quoted hunk
From: Linus Torvalds <torvalds@linux-foundation.org>
Date: Thu, 9 Jul 2009 13:35:31 -0700
Subject: [PATCH 6/3] Export thread-safe version of 'has_symlink_leading_path()'

The threaded index preloading will want it, so that it can avoid
locking by simply using a per-thread symlink/directory cache.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
This just exposes a thread-safe version of the symlink checking by 
allowing a caller to pass in its own local 'struct cache_def' to the 
function.

No users of this yet, but the next step is trivial and obvious..

 cache.h    |   10 ++++++++++
 symlinks.c |   21 ++++++++++-----------
 2 files changed, 20 insertions(+), 11 deletions(-)
diff --git a/cache.h b/cache.h
index 871c984..f1e5ede 100644
--- a/cache.h
+++ b/cache.h
@@ -744,7 +744,17 @@ struct checkout {
 };
 
 extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
+
+struct cache_def {
+	char path[PATH_MAX + 1];
+	int len;
+	int flags;
+	int track_flags;
+	int prefix_len_stat_func;
+};
+
 extern int has_symlink_leading_path(const char *name, int len);
+extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
 extern int has_symlink_or_noent_leading_path(const char *name, int len);
 extern int has_dirs_only_path(const char *name, int len, int prefix_len);
 extern void invalidate_lstat_cache(const char *name, int len);
diff --git a/symlinks.c b/symlinks.c
index 08ad353..4bdded3 100644
--- a/symlinks.c
+++ b/symlinks.c
@@ -32,13 +32,7 @@ static int longest_path_match(const char *name_a, int len_a,
 	return match_len;
 }
 
-static struct cache_def {
-	char path[PATH_MAX + 1];
-	int len;
-	int flags;
-	int track_flags;
-	int prefix_len_stat_func;
-} default_cache;
+static struct cache_def default_cache;
 
 static inline void reset_lstat_cache(struct cache_def *cache)
 {
@@ -217,12 +211,17 @@ void clear_lstat_cache(void)
 /*
  * Return non-zero if path 'name' has a leading symlink component
  */
+int threaded_has_symlink_leading_path(struct cache_def *cache, const char *name, int len)
+{
+	return lstat_cache(cache, name, len, FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & FL_SYMLINK;
  OK, to follow the style the 3 previous lstat_cache() calls was made
  with (and also let the line length be less than 80), it should have
  been written like this:

     	return lstat_cache(cache, name, len,
			   FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) &
		FL_SYMLINK;

  Notice that the parmeters which is just copied as arguments to l_c()
  is in the same order and on the first line for it self.  The next line
  contains the rest of the arguments, and the &-part is also on it
  a separate line.

  Stylefix only, so not a big deal.
+}
+
+/*
+ * Return non-zero if path 'name' has a leading symlink component
+ */
 int has_symlink_leading_path(const char *name, int len)
 {
-	struct cache_def *cache = &default_cache;	/* FIXME */
   This would make it inconsistent with the 2 has_*_() functions below,
   which both have such a line.  Only stylefix, no change in semantics.

   I personally liked this line, since it will then be easier to
   "threadify" the function with an extra parameter named "cache".
-	return lstat_cache(cache, name, len,
-			   FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) &
-		FL_SYMLINK;
+	return threaded_has_symlink_leading_path(&default_cache, name, len);
 }
 
 /*
  I have looked at and tested (the version from the origin/pu branch, so
  it contains the memset() line squashed in) patch 5/3, 6/3 and 7/3, and
  all 3 patches looks correct, so you can add

     Reviewed-and-tested-by: Kjetil Barvik

  if you want to.

  But, I guess it is me which is a litle late to comment things, since I
  already see that all 3 patches is in the pu, next and master branches
  already, less than 3 days after beeing posted to the malinglist.

  But, would'nt it be a good thing to let all patches at least be in the
  pu branch for minimum x days before entering next and master?  Or: let
  it go minimum x days after beeing posted to the list before entering
  the next and master branch?  x = 4?

  Since the patches is already in master and next, I guess it is not as
  easy as if the patche(es) has been in pu to make a new version of a
  patch, since both master and next is expected to be fast-forward
  branches.

  -- kjetil, which was too late this time, too  :-)
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help