BPF List
 help / color / mirror / Atom feed
* [PATCH 0/3] fs/ufs: replace kmap() with kmap_local_page()
@ 2022-12-11 21:31 Fabio M. De Francesco
  2022-12-11 21:31 ` [PATCH 1/3] fs/ufs: Use the offset_in_page() helper Fabio M. De Francesco
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Fabio M. De Francesco @ 2022-12-11 21:31 UTC (permalink / raw)
  To: Evgeniy Dushistov, Al Viro, Ira Weiny, linux-kernel, bpf,
	linux-fsdevel
  Cc: Fabio M. De Francesco, Fabio M . De Francesco

kmap() is being deprecated in favor of kmap_local_page().

There are two main problems with kmap(): (1) It comes with an overhead as
the mapping space is restricted and protected by a global lock for
synchronization and (2) it also requires global TLB invalidation when the
kmap’s pool wraps and it might block when the mapping space is fully
utilized until a slot becomes available.

With kmap_local_page() the mappings are per thread, CPU local, can take
page faults, and can be called from any context (including interrupts).
It is faster than kmap() in kernels with HIGHMEM enabled. Furthermore,
the tasks can be preempted and, when they are scheduled to run again, the
kernel virtual addresses are restored and still valid.

Since its use in fs/ufs is safe everywhere, it should be preferred.

Therefore, replace kmap() with kmap_local_page() in fs/ufs. kunmap_local()
requires the mapping address, so return that address from ufs_get_page()
to be used in ufs_put_page().

This series could have not been ever made because nothing prevented the
previous patch from working properly but Al Viro made a long series of
very appreciated comments about how many unnecessary and redundant lines
of code I could have removed. He could see things I was entirely unable
to notice. Furthermore, he also provided solutions and details about how
I could decompose a single patch into a small series of three
independent units.[1][2][3]

I want to thank him so much for the patience, kindness and the time he
decided to spend to provide those analysis and write three messages full
of interesting insights. I hope to have not misunderstood too many
things, however I'm pretty sure that I made many mistakes due to my
scarce knowledge of filesystem and, above all, lack of experience :-)

I decided to get rid of the previous numbers and start from scratch
(i.e., version 1) because this series has too little to share with the
design of the previous patch.[4]

[1] https://lore.kernel.org/lkml/Y4E++JERgUMoqfjG@ZenIV/
[2] https://lore.kernel.org/lkml/Y4FG0O7VWTTng5yh@ZenIV/
[3] https://lore.kernel.org/lkml/Y4ONIFJatIGsVNpf@ZenIV/
[4] https://lore.kernel.org/lkml/20221016163855.8173-1-fmdefrancesco@gmail.com/

Cc: Ira Weiny <ira.weiny@intel.com>
Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Fabio M. De Francesco <fmdrefrancesco@gmail.com>

Fabio M. De Francesco (3):
  fs/ufs: Use the offset_in_page() helper
  fs/ufs: Change the signature of ufs_get_page()
  fs/ufs: Replace kmap() with kmap_local_page()

 fs/ufs/dir.c | 140 +++++++++++++++++++++++++++------------------------
 1 file changed, 73 insertions(+), 67 deletions(-)

-- 
2.38.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 1/3] fs/ufs: Use the offset_in_page() helper
  2022-12-11 21:31 [PATCH 0/3] fs/ufs: replace kmap() with kmap_local_page() Fabio M. De Francesco
@ 2022-12-11 21:31 ` Fabio M. De Francesco
  2022-12-11 21:31 ` [PATCH 2/3] fs/ufs: Change the signature of ufs_get_page() Fabio M. De Francesco
  2022-12-11 21:31 ` [PATCH 3/3] fs/ufs: Replace kmap() with kmap_local_page() Fabio M. De Francesco
  2 siblings, 0 replies; 11+ messages in thread
From: Fabio M. De Francesco @ 2022-12-11 21:31 UTC (permalink / raw)
  To: Evgeniy Dushistov, Al Viro, Ira Weiny, linux-kernel, bpf,
	linux-fsdevel
  Cc: Fabio M. De Francesco

Use the offset_in_page() helper because it is more suitable than doing
explicit subtractions between pointers to directory entries and kernel
virtual addresses of mapped pages.

Cc: Ira Weiny <ira.weiny@intel.com>
Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Fabio M. De Francesco <fmdefrancesco@gmail.com>
---
 fs/ufs/dir.c | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c
index 391efaf1d528..69f78583c9c1 100644
--- a/fs/ufs/dir.c
+++ b/fs/ufs/dir.c
@@ -87,8 +87,7 @@ void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de,
 		  struct page *page, struct inode *inode,
 		  bool update_times)
 {
-	loff_t pos = page_offset(page) +
-			(char *) de - (char *) page_address(page);
+	loff_t pos = page_offset(page) + offset_in_page(de);
 	unsigned len = fs16_to_cpu(dir->i_sb, de->d_reclen);
 	int err;
 
@@ -371,8 +370,7 @@ int ufs_add_link(struct dentry *dentry, struct inode *inode)
 	return -EINVAL;
 
 got_it:
-	pos = page_offset(page) +
-			(char*)de - (char*)page_address(page);
+	pos = page_offset(page) + offset_in_page(de);
 	err = ufs_prepare_chunk(page, pos, rec_len);
 	if (err)
 		goto out_unlock;
@@ -497,8 +495,8 @@ int ufs_delete_entry(struct inode *inode, struct ufs_dir_entry *dir,
 {
 	struct super_block *sb = inode->i_sb;
 	char *kaddr = page_address(page);
-	unsigned from = ((char*)dir - kaddr) & ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1);
-	unsigned to = ((char*)dir - kaddr) + fs16_to_cpu(sb, dir->d_reclen);
+	unsigned int from = offset_in_page(dir) & ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1);
+	unsigned int to = offset_in_page(dir) + fs16_to_cpu(sb, dir->d_reclen);
 	loff_t pos;
 	struct ufs_dir_entry *pde = NULL;
 	struct ufs_dir_entry *de = (struct ufs_dir_entry *) (kaddr + from);
@@ -522,7 +520,7 @@ int ufs_delete_entry(struct inode *inode, struct ufs_dir_entry *dir,
 		de = ufs_next_entry(sb, de);
 	}
 	if (pde)
-		from = (char*)pde - (char*)page_address(page);
+		from = offset_in_page(pde);
 
 	pos = page_offset(page) + from;
 	lock_page(page);
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 2/3] fs/ufs: Change the signature of ufs_get_page()
  2022-12-11 21:31 [PATCH 0/3] fs/ufs: replace kmap() with kmap_local_page() Fabio M. De Francesco
  2022-12-11 21:31 ` [PATCH 1/3] fs/ufs: Use the offset_in_page() helper Fabio M. De Francesco
@ 2022-12-11 21:31 ` Fabio M. De Francesco
  2022-12-11 22:29   ` Al Viro
  2022-12-11 22:42   ` Al Viro
  2022-12-11 21:31 ` [PATCH 3/3] fs/ufs: Replace kmap() with kmap_local_page() Fabio M. De Francesco
  2 siblings, 2 replies; 11+ messages in thread
From: Fabio M. De Francesco @ 2022-12-11 21:31 UTC (permalink / raw)
  To: Evgeniy Dushistov, Al Viro, Ira Weiny, linux-kernel, bpf,
	linux-fsdevel
  Cc: Fabio M. De Francesco

Change the signature of ufs_get_page() in order to prepare this function
to the conversion to the use of kmap_local_page(). Change also those call
sites which are required to conform its invocations to the new
signature.

Cc: Ira Weiny <ira.weiny@intel.com>
Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Fabio M. De Francesco <fmdefrancesco@gmail.com>
---
 fs/ufs/dir.c | 58 ++++++++++++++++++++++------------------------------
 1 file changed, 24 insertions(+), 34 deletions(-)

diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c
index 69f78583c9c1..bb6b29ce1d3a 100644
--- a/fs/ufs/dir.c
+++ b/fs/ufs/dir.c
@@ -185,21 +185,21 @@ static bool ufs_check_page(struct page *page)
 	return false;
 }
 
-static struct page *ufs_get_page(struct inode *dir, unsigned long n)
+static void *ufs_get_page(struct inode *dir, unsigned long n, struct page **page)
 {
 	struct address_space *mapping = dir->i_mapping;
-	struct page *page = read_mapping_page(mapping, n, NULL);
-	if (!IS_ERR(page)) {
-		kmap(page);
-		if (unlikely(!PageChecked(page))) {
-			if (!ufs_check_page(page))
+	*page = read_mapping_page(mapping, n, NULL);
+	if (!IS_ERR(*page)) {
+		kmap(*page);
+		if (unlikely(!PageChecked(*page))) {
+			if (!ufs_check_page(*page))
 				goto fail;
 		}
 	}
 	return page;
 
 fail:
-	ufs_put_page(page);
+	ufs_put_page(*page);
 	return ERR_PTR(-EIO);
 }
 
@@ -227,15 +227,12 @@ ufs_next_entry(struct super_block *sb, struct ufs_dir_entry *p)
 
 struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct page **p)
 {
-	struct page *page = ufs_get_page(dir, 0);
-	struct ufs_dir_entry *de = NULL;
+	struct ufs_dir_entry *de = ufs_get_page(dir, 0, p);
 
-	if (!IS_ERR(page)) {
-		de = ufs_next_entry(dir->i_sb,
-				    (struct ufs_dir_entry *)page_address(page));
-		*p = page;
-	}
-	return de;
+	if (!IS_ERR(de))
+		return ufs_next_entry(dir->i_sb, de);
+	else
+		return NULL;
 }
 
 /*
@@ -273,11 +270,10 @@ struct ufs_dir_entry *ufs_find_entry(struct inode *dir, const struct qstr *qstr,
 		start = 0;
 	n = start;
 	do {
-		char *kaddr;
-		page = ufs_get_page(dir, n);
-		if (!IS_ERR(page)) {
-			kaddr = page_address(page);
-			de = (struct ufs_dir_entry *) kaddr;
+		char *kaddr = ufs_get_page(dir, n, &page);
+
+		if (!IS_ERR(kaddr)) {
+			de = (struct ufs_dir_entry *)kaddr;
 			kaddr += ufs_last_byte(dir, n) - reclen;
 			while ((char *) de <= kaddr) {
 				if (ufs_match(sb, namelen, name, de))
@@ -328,12 +324,10 @@ int ufs_add_link(struct dentry *dentry, struct inode *inode)
 	for (n = 0; n <= npages; n++) {
 		char *dir_end;
 
-		page = ufs_get_page(dir, n);
-		err = PTR_ERR(page);
-		if (IS_ERR(page))
-			goto out;
+		kaddr = ufs_get_page(dir, n, &page);
+		if (IS_ERR(kaddr))
+			return PTR_ERR(kaddr);
 		lock_page(page);
-		kaddr = page_address(page);
 		dir_end = kaddr + ufs_last_byte(dir, n);
 		de = (struct ufs_dir_entry *)kaddr;
 		kaddr += PAGE_SIZE - reclen;
@@ -395,8 +389,6 @@ int ufs_add_link(struct dentry *dentry, struct inode *inode)
 	/* OFFSET_CACHE */
 out_put:
 	ufs_put_page(page);
-out:
-	return err;
 out_unlock:
 	unlock_page(page);
 	goto out_put;
@@ -429,6 +421,7 @@ ufs_readdir(struct file *file, struct dir_context *ctx)
 	unsigned chunk_mask = ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1);
 	bool need_revalidate = !inode_eq_iversion(inode, file->f_version);
 	unsigned flags = UFS_SB(sb)->s_flags;
+	struct page *page;
 
 	UFSD("BEGIN\n");
 
@@ -439,16 +432,14 @@ ufs_readdir(struct file *file, struct dir_context *ctx)
 		char *kaddr, *limit;
 		struct ufs_dir_entry *de;
 
-		struct page *page = ufs_get_page(inode, n);
-
-		if (IS_ERR(page)) {
+		kaddr = ufs_get_page(inode, n, &page);
+		if (IS_ERR(kaddr)) {
 			ufs_error(sb, __func__,
 				  "bad page in #%lu",
 				  inode->i_ino);
 			ctx->pos += PAGE_SIZE - offset;
 			return -EIO;
 		}
-		kaddr = page_address(page);
 		if (unlikely(need_revalidate)) {
 			if (offset) {
 				offset = ufs_validate_entry(sb, kaddr, offset, chunk_mask);
@@ -595,12 +586,11 @@ int ufs_empty_dir(struct inode * inode)
 	for (i = 0; i < npages; i++) {
 		char *kaddr;
 		struct ufs_dir_entry *de;
-		page = ufs_get_page(inode, i);
 
-		if (IS_ERR(page))
+		kaddr = ufs_get_page(inode, i, &page);
+		if (IS_ERR(kaddr))
 			continue;
 
-		kaddr = page_address(page);
 		de = (struct ufs_dir_entry *)kaddr;
 		kaddr += ufs_last_byte(inode, i) - UFS_DIR_REC_LEN(1);
 
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 3/3] fs/ufs: Replace kmap() with kmap_local_page()
  2022-12-11 21:31 [PATCH 0/3] fs/ufs: replace kmap() with kmap_local_page() Fabio M. De Francesco
  2022-12-11 21:31 ` [PATCH 1/3] fs/ufs: Use the offset_in_page() helper Fabio M. De Francesco
  2022-12-11 21:31 ` [PATCH 2/3] fs/ufs: Change the signature of ufs_get_page() Fabio M. De Francesco
@ 2022-12-11 21:31 ` Fabio M. De Francesco
  2022-12-11 22:39   ` Al Viro
       [not found]   ` <202212120803.iPhHqCqR-lkp@intel.com>
  2 siblings, 2 replies; 11+ messages in thread
From: Fabio M. De Francesco @ 2022-12-11 21:31 UTC (permalink / raw)
  To: Evgeniy Dushistov, Al Viro, Ira Weiny, linux-kernel, bpf,
	linux-fsdevel
  Cc: Fabio M. De Francesco

kmap() is being deprecated in favor of kmap_local_page().

There are two main problems with kmap(): (1) It comes with an overhead as
the mapping space is restricted and protected by a global lock for
synchronization and (2) it also requires global TLB invalidation when the
kmap’s pool wraps and it might block when the mapping space is fully
utilized until a slot becomes available.

With kmap_local_page() the mappings are per thread, CPU local, can take
page faults, and can be called from any context (including interrupts).
It is faster than kmap() in kernels with HIGHMEM enabled. Furthermore,
the tasks can be preempted and, when they are scheduled to run again, the
kernel virtual addresses are restored and still valid.

Since its use in fs/ufs is safe everywhere, it should be preferred.

Therefore, replace kmap() with kmap_local_page() in fs/ufs. kunmap_local()
requires the mapping address, so return that address from ufs_get_page()
to be used in ufs_put_page().

Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
Suggested-by: Ira Weiny <ira.weiny@intel.com>
Signed-off-by: Fabio M. De Francesco <fmdefrancesco@gmail.com>
---
 fs/ufs/dir.c | 76 ++++++++++++++++++++++++++++++++--------------------
 1 file changed, 47 insertions(+), 29 deletions(-)

diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c
index bb6b29ce1d3a..fd57d8171c88 100644
--- a/fs/ufs/dir.c
+++ b/fs/ufs/dir.c
@@ -61,9 +61,9 @@ static int ufs_commit_chunk(struct page *page, loff_t pos, unsigned len)
 	return err;
 }
 
-static inline void ufs_put_page(struct page *page)
+static inline void ufs_put_page(struct page *page, void *page_addr)
 {
-	kunmap(page);
+	kunmap((void *)((unsigned long)page_addr & PAGE_MASK));
 	put_page(page);
 }
 
@@ -76,7 +76,7 @@ ino_t ufs_inode_by_name(struct inode *dir, const struct qstr *qstr)
 	de = ufs_find_entry(dir, qstr, &page);
 	if (de) {
 		res = fs32_to_cpu(dir->i_sb, de->d_ino);
-		ufs_put_page(page);
+		ufs_put_page(page, de);
 	}
 	return res;
 }
@@ -99,18 +99,17 @@ void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de,
 	ufs_set_de_type(dir->i_sb, de, inode->i_mode);
 
 	err = ufs_commit_chunk(page, pos, len);
-	ufs_put_page(page);
+	ufs_put_page(page, de);
 	if (update_times)
 		dir->i_mtime = dir->i_ctime = current_time(dir);
 	mark_inode_dirty(dir);
 }
 
 
-static bool ufs_check_page(struct page *page)
+static bool ufs_check_page(struct page *page, char *kaddr)
 {
 	struct inode *dir = page->mapping->host;
 	struct super_block *sb = dir->i_sb;
-	char *kaddr = page_address(page);
 	unsigned offs, rec_len;
 	unsigned limit = PAGE_SIZE;
 	const unsigned chunk_mask = UFS_SB(sb)->s_uspi->s_dirblksize - 1;
@@ -185,21 +184,30 @@ static bool ufs_check_page(struct page *page)
 	return false;
 }
 
+/*
+ * Calls to ufs_get_page()/ufs_put_page() must be nested according to the
+ * rules documented in kmap_local_page()/kunmap_local().
+ *
+ * NOTE: ufs_find_entry() and ufs_dotdot() act as calls to ufs_get_page()
+ * and must be treated accordingly for nesting purposes.
+ */
 static void *ufs_get_page(struct inode *dir, unsigned long n, struct page **page)
 {
+	char *kaddr;
+
 	struct address_space *mapping = dir->i_mapping;
 	*page = read_mapping_page(mapping, n, NULL);
 	if (!IS_ERR(*page)) {
-		kmap(*page);
+		kmap_local_page(*page);
 		if (unlikely(!PageChecked(*page))) {
-			if (!ufs_check_page(*page))
+			if (!ufs_check_page(*page, kaddr))
 				goto fail;
 		}
 	}
-	return page;
+	return *page;
 
 fail:
-	ufs_put_page(*page);
+	ufs_put_page(*page, kaddr);
 	return ERR_PTR(-EIO);
 }
 
@@ -225,6 +233,13 @@ ufs_next_entry(struct super_block *sb, struct ufs_dir_entry *p)
 					fs16_to_cpu(sb, p->d_reclen));
 }
 
+/*
+ * Calls to ufs_get_page()/ufs_put_page() must be nested according to the
+ * rules documented in kmap_local_page()/kunmap_local().
+ *
+ * ufs_dotdot() acts as a call to ufs_get_page() and must be treated
+ * accordingly for nesting purposes.
+ */
 struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct page **p)
 {
 	struct ufs_dir_entry *de = ufs_get_page(dir, 0, p);
@@ -236,12 +251,15 @@ struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct page **p)
 }
 
 /*
- *	ufs_find_entry()
+ * Finds an entry in the specified directory with the wanted name. It returns a
+ * pointer to the directory's entry. The page in which the entry was found is
+ * in the res_page out parameter. The page is returned mapped and unlocked.
+ * The entry is guaranteed to be valid.
+ *
+ * On Success ufs_put_page() should be called on *res_page.
  *
- * finds an entry in the specified directory with the wanted name. It
- * returns the page in which the entry was found, and the entry itself
- * (as a parameter - res_dir). Page is returned mapped and unlocked.
- * Entry is guaranteed to be valid.
+ * ufs_find_entry() acts as a call to ufs_get_page() and must be treated
+ * accordingly for nesting purposes.
  */
 struct ufs_dir_entry *ufs_find_entry(struct inode *dir, const struct qstr *qstr,
 				     struct page **res_page)
@@ -280,7 +298,7 @@ struct ufs_dir_entry *ufs_find_entry(struct inode *dir, const struct qstr *qstr,
 					goto found;
 				de = ufs_next_entry(sb, de);
 			}
-			ufs_put_page(page);
+			ufs_put_page(page, kaddr);
 		}
 		if (++n >= npages)
 			n = 0;
@@ -358,7 +376,7 @@ int ufs_add_link(struct dentry *dentry, struct inode *inode)
 			de = (struct ufs_dir_entry *) ((char *) de + rec_len);
 		}
 		unlock_page(page);
-		ufs_put_page(page);
+		ufs_put_page(page, kaddr);
 	}
 	BUG();
 	return -EINVAL;
@@ -388,7 +406,8 @@ int ufs_add_link(struct dentry *dentry, struct inode *inode)
 	mark_inode_dirty(dir);
 	/* OFFSET_CACHE */
 out_put:
-	ufs_put_page(page);
+	ufs_put_page(page, kaddr);
+	return 0;
 out_unlock:
 	unlock_page(page);
 	goto out_put;
@@ -465,13 +484,13 @@ ufs_readdir(struct file *file, struct dir_context *ctx)
 					       ufs_get_de_namlen(sb, de),
 					       fs32_to_cpu(sb, de->d_ino),
 					       d_type)) {
-					ufs_put_page(page);
+					ufs_put_page(page, kaddr);
 					return 0;
 				}
 			}
 			ctx->pos += fs16_to_cpu(sb, de->d_reclen);
 		}
-		ufs_put_page(page);
+		ufs_put_page(page, kaddr);
 	}
 	return 0;
 }
@@ -482,10 +501,10 @@ ufs_readdir(struct file *file, struct dir_context *ctx)
  * previous entry.
  */
 int ufs_delete_entry(struct inode *inode, struct ufs_dir_entry *dir,
-		     struct page * page)
+		     struct page *page)
 {
 	struct super_block *sb = inode->i_sb;
-	char *kaddr = page_address(page);
+	char *kaddr = (char *)((unsigned long)dir & PAGE_MASK);
 	unsigned int from = offset_in_page(dir) & ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1);
 	unsigned int to = offset_in_page(dir) + fs16_to_cpu(sb, dir->d_reclen);
 	loff_t pos;
@@ -524,7 +543,7 @@ int ufs_delete_entry(struct inode *inode, struct ufs_dir_entry *dir,
 	inode->i_ctime = inode->i_mtime = current_time(inode);
 	mark_inode_dirty(inode);
 out:
-	ufs_put_page(page);
+	ufs_put_page(page, kaddr);
 	UFSD("EXIT\n");
 	return err;
 }
@@ -548,8 +567,7 @@ int ufs_make_empty(struct inode * inode, struct inode *dir)
 		goto fail;
 	}
 
-	kmap(page);
-	base = (char*)page_address(page);
+	base = kmap_local_page(page);
 	memset(base, 0, PAGE_SIZE);
 
 	de = (struct ufs_dir_entry *) base;
@@ -566,7 +584,7 @@ int ufs_make_empty(struct inode * inode, struct inode *dir)
 	de->d_reclen = cpu_to_fs16(sb, chunk_size - UFS_DIR_REC_LEN(1));
 	ufs_set_de_namlen(sb, de, 2);
 	strcpy (de->d_name, "..");
-	kunmap(page);
+	kunmap_local(base);
 
 	err = ufs_commit_chunk(page, 0, chunk_size);
 fail:
@@ -582,9 +600,9 @@ int ufs_empty_dir(struct inode * inode)
 	struct super_block *sb = inode->i_sb;
 	struct page *page = NULL;
 	unsigned long i, npages = dir_pages(inode);
+	char *kaddr;
 
 	for (i = 0; i < npages; i++) {
-		char *kaddr;
 		struct ufs_dir_entry *de;
 
 		kaddr = ufs_get_page(inode, i, &page);
@@ -617,12 +635,12 @@ int ufs_empty_dir(struct inode * inode)
 			}
 			de = ufs_next_entry(sb, de);
 		}
-		ufs_put_page(page);
+		ufs_put_page(page, kaddr);
 	}
 	return 1;
 
 not_empty:
-	ufs_put_page(page);
+	ufs_put_page(page, kaddr);
 	return 0;
 }
 
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/3] fs/ufs: Change the signature of ufs_get_page()
  2022-12-11 21:31 ` [PATCH 2/3] fs/ufs: Change the signature of ufs_get_page() Fabio M. De Francesco
@ 2022-12-11 22:29   ` Al Viro
  2022-12-11 23:56     ` Fabio M. De Francesco
  2022-12-11 22:42   ` Al Viro
  1 sibling, 1 reply; 11+ messages in thread
From: Al Viro @ 2022-12-11 22:29 UTC (permalink / raw)
  To: Fabio M. De Francesco
  Cc: Evgeniy Dushistov, Ira Weiny, linux-kernel, bpf, linux-fsdevel

On Sun, Dec 11, 2022 at 10:31:10PM +0100, Fabio M. De Francesco wrote:
> -static struct page *ufs_get_page(struct inode *dir, unsigned long n)
> +static void *ufs_get_page(struct inode *dir, unsigned long n, struct page **page)
>  {
>  	struct address_space *mapping = dir->i_mapping;
> -	struct page *page = read_mapping_page(mapping, n, NULL);
> -	if (!IS_ERR(page)) {
> -		kmap(page);
> -		if (unlikely(!PageChecked(page))) {
> -			if (!ufs_check_page(page))
> +	*page = read_mapping_page(mapping, n, NULL);
> +	if (!IS_ERR(*page)) {
> +		kmap(*page);
> +		if (unlikely(!PageChecked(*page))) {
> +			if (!ufs_check_page(*page))
>  				goto fail;
>  		}
>  	}
>  	return page;

	return page_address(page), surely?
>  
>  fail:
> -	ufs_put_page(page);
> +	ufs_put_page(*page);
>  	return ERR_PTR(-EIO);
>  }
>  
> @@ -227,15 +227,12 @@ ufs_next_entry(struct super_block *sb, struct ufs_dir_entry *p)
>  
>  struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct page **p)
>  {
> -	struct page *page = ufs_get_page(dir, 0);
> -	struct ufs_dir_entry *de = NULL;
> +	struct ufs_dir_entry *de = ufs_get_page(dir, 0, p);

... considering e.g. this.  The caller expects the pointer to beginning of
page, not pointer to struct page itself.  Other callers are also like that...

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/3] fs/ufs: Replace kmap() with kmap_local_page()
  2022-12-11 21:31 ` [PATCH 3/3] fs/ufs: Replace kmap() with kmap_local_page() Fabio M. De Francesco
@ 2022-12-11 22:39   ` Al Viro
  2022-12-12  0:14     ` Fabio M. De Francesco
       [not found]   ` <202212120803.iPhHqCqR-lkp@intel.com>
  1 sibling, 1 reply; 11+ messages in thread
From: Al Viro @ 2022-12-11 22:39 UTC (permalink / raw)
  To: Fabio M. De Francesco
  Cc: Evgeniy Dushistov, Ira Weiny, linux-kernel, bpf, linux-fsdevel

On Sun, Dec 11, 2022 at 10:31:11PM +0100, Fabio M. De Francesco wrote:

> +/*
> + * Calls to ufs_get_page()/ufs_put_page() must be nested according to the
> + * rules documented in kmap_local_page()/kunmap_local().
> + *
> + * NOTE: ufs_find_entry() and ufs_dotdot() act as calls to ufs_get_page()
> + * and must be treated accordingly for nesting purposes.
> + */
>  static void *ufs_get_page(struct inode *dir, unsigned long n, struct page **page)
>  {
> +	char *kaddr;
> +
>  	struct address_space *mapping = dir->i_mapping;
>  	*page = read_mapping_page(mapping, n, NULL);
>  	if (!IS_ERR(*page)) {
> -		kmap(*page);
> +		kmap_local_page(*page);
>  		if (unlikely(!PageChecked(*page))) {
> -			if (!ufs_check_page(*page))
> +			if (!ufs_check_page(*page, kaddr))

	Er...  Building the patched tree is occasionally useful.
Here kaddr is obviously uninitialized and compiler would've
probably caught that.

	And return value of kmap_local_page() is lost, which
is related to the previous issue ;-)


>  				goto fail;
>  		}
>  	}
> -	return page;
> +	return *page;

Hell, no.  Callers expect the pointer to the first byte of
your page.  What it should return is kaddr.

> @@ -388,7 +406,8 @@ int ufs_add_link(struct dentry *dentry, struct inode *inode)
>  	mark_inode_dirty(dir);
>  	/* OFFSET_CACHE */
>  out_put:
> -	ufs_put_page(page);
> +	ufs_put_page(page, kaddr);
> +	return 0;
>  out_unlock:
>  	unlock_page(page);
>  	goto out_put;

That can't be right.  Places like
        if (err)
		goto out_unlock;
do not expect err to be lost.  You end up returning 0 now.  Something strange
happened here (in the previous commit, perhaps?)

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/3] fs/ufs: Change the signature of ufs_get_page()
  2022-12-11 21:31 ` [PATCH 2/3] fs/ufs: Change the signature of ufs_get_page() Fabio M. De Francesco
  2022-12-11 22:29   ` Al Viro
@ 2022-12-11 22:42   ` Al Viro
  2022-12-12 20:08     ` Fabio M. De Francesco
  1 sibling, 1 reply; 11+ messages in thread
From: Al Viro @ 2022-12-11 22:42 UTC (permalink / raw)
  To: Fabio M. De Francesco
  Cc: Evgeniy Dushistov, Ira Weiny, linux-kernel, bpf, linux-fsdevel

On Sun, Dec 11, 2022 at 10:31:10PM +0100, Fabio M. De Francesco wrote:
>  out_put:
>  	ufs_put_page(page);
> -out:
> -	return err;
>  out_unlock:
>  	unlock_page(page);
>  	goto out_put;

Something strange has happened, all right - look at the situation
after that patch.  You've got

out_put:
	ufs_put_page(page);
out_unlock:
	unlock_page(page);
	goto out_put;

Which is obviously bogus.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/3] fs/ufs: Change the signature of ufs_get_page()
  2022-12-11 22:29   ` Al Viro
@ 2022-12-11 23:56     ` Fabio M. De Francesco
  0 siblings, 0 replies; 11+ messages in thread
From: Fabio M. De Francesco @ 2022-12-11 23:56 UTC (permalink / raw)
  To: Al Viro; +Cc: Evgeniy Dushistov, Ira Weiny, linux-kernel, bpf, linux-fsdevel

On domenica 11 dicembre 2022 23:29:31 CET Al Viro wrote:
> On Sun, Dec 11, 2022 at 10:31:10PM +0100, Fabio M. De Francesco wrote:
> > -static struct page *ufs_get_page(struct inode *dir, unsigned long n)
> > +static void *ufs_get_page(struct inode *dir, unsigned long n, struct page
> > **page)> 
> >  {
> >  
> >  	struct address_space *mapping = dir->i_mapping;
> > 
> > -	struct page *page = read_mapping_page(mapping, n, NULL);
> > -	if (!IS_ERR(page)) {
> > -		kmap(page);
> > -		if (unlikely(!PageChecked(page))) {
> > -			if (!ufs_check_page(page))
> > +	*page = read_mapping_page(mapping, n, NULL);
> > +	if (!IS_ERR(*page)) {
> > +		kmap(*page);
> > +		if (unlikely(!PageChecked(*page))) {
> > +			if (!ufs_check_page(*page))
> > 
> >  				goto fail;
> >  		
> >  		}
> >  	
> >  	}
> >  	return page;
> 
> 	return page_address(page), surely?
> 
Yes, I'm sorry for these kinds of silly mistakes because while I was expecting 
to err on much more difficult topics I overlooked what I know pretty well  :-(

Shouldn't it be "return page_address(*page)" because of "page" being a pointer 
to pointer to "struct page"? Am I overlooking something?

However, the greater mistake was about doing the decomposition into three 
patches starting from the old single thing. I think that I had to start from 
scratch. I made the process the other way round. 
>
> >  fail:
> > -	ufs_put_page(page);
> > +	ufs_put_page(*page);
> > 
> >  	return ERR_PTR(-EIO);
> >  
> >  }
> > 
> > @@ -227,15 +227,12 @@ ufs_next_entry(struct super_block *sb, struct
> > ufs_dir_entry *p)> 
> >  struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct page **p)
> >  {
> > 
> > -	struct page *page = ufs_get_page(dir, 0);
> > -	struct ufs_dir_entry *de = NULL;
> > +	struct ufs_dir_entry *de = ufs_get_page(dir, 0, p);
> 
> ... considering e.g. this.  The caller expects the pointer to beginning of
> page, not pointer to struct page itself.  Other callers are also like 
that...
>
I'll send next version within tomorrow (before or after work time) because 
here it is 00.40 CET.

Thank you very much for your immediate reply.

Fabio




^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/3] fs/ufs: Replace kmap() with kmap_local_page()
  2022-12-11 22:39   ` Al Viro
@ 2022-12-12  0:14     ` Fabio M. De Francesco
  0 siblings, 0 replies; 11+ messages in thread
From: Fabio M. De Francesco @ 2022-12-12  0:14 UTC (permalink / raw)
  To: Al Viro; +Cc: Evgeniy Dushistov, Ira Weiny, linux-kernel, bpf, linux-fsdevel

On domenica 11 dicembre 2022 23:39:44 CET Al Viro wrote:
> On Sun, Dec 11, 2022 at 10:31:11PM +0100, Fabio M. De Francesco wrote:
> > +/*
> > + * Calls to ufs_get_page()/ufs_put_page() must be nested according to the
> > + * rules documented in kmap_local_page()/kunmap_local().
> > + *
> > + * NOTE: ufs_find_entry() and ufs_dotdot() act as calls to ufs_get_page()
> > + * and must be treated accordingly for nesting purposes.
> > + */
> > 
> >  static void *ufs_get_page(struct inode *dir, unsigned long n, struct page
> >  **page) {
> > 
> > +	char *kaddr;
> > +
> > 
> >  	struct address_space *mapping = dir->i_mapping;
> >  	*page = read_mapping_page(mapping, n, NULL);
> >  	if (!IS_ERR(*page)) {
> > 
> > -		kmap(*page);
> > +		kmap_local_page(*page);
> > 
> >  		if (unlikely(!PageChecked(*page))) {
> > 
> > -			if (!ufs_check_page(*page))
> > +			if (!ufs_check_page(*page, kaddr))
> 
> 	Er...  Building the patched tree is occasionally useful.
>
I don't know why gcc didn't catch this (gcc version 12.2.1 20221020 [revision 
0aaef83351473e8f4eb774f8f999bbe87a4866d7] (SUSE Linux)):

setarch i686
make ARCH=i386 O=../build-linux-x86_32-debug/ -j12
make[1]: Entering directory '/usr/src/git/kernels/build-linux-x86_32-debug'
  GEN     Makefile
  DESCEND bpf/resolve_btfids
  CALL    /usr/src/git/kernels/linux/scripts/checksyscalls.sh
  CC [M]  fs/ufs/dir.o
  LD [M]  fs/ufs/ufs.o
  MODPOST Module.symvers
Kernel: arch/x86/boot/bzImage is ready  (#3)
  LD [M]  fs/ufs/ufs.ko
  BTF [M] fs/ufs/ufs.ko
make[1]: Leaving directory '/usr/src/git/kernels/build-linux-x86_32-debug'

> Here kaddr is obviously uninitialized and compiler would've
> probably caught that.
>
I'd better use option W=1 next time.
>
> 	And return value of kmap_local_page() is lost, which
> is related to the previous issue ;-)
> 
> >  				goto fail;
> >  		
> >  		}
> >  	
> >  	}
> > 
> > -	return page;
> > +	return *page;
> 
> Hell, no.  Callers expect the pointer to the first byte of
> your page.  What it should return is kaddr.
>
I'm sorry that I entirely missed this :-(
> 
> > @@ -388,7 +406,8 @@ int ufs_add_link(struct dentry *dentry, struct inode
> > *inode)> 
> >  	mark_inode_dirty(dir);
> >  	/* OFFSET_CACHE */
> >  
> >  out_put:
> > -	ufs_put_page(page);
> > +	ufs_put_page(page, kaddr);
> > +	return 0;
> > 
> >  out_unlock:
> >  	unlock_page(page);
> >  	goto out_put;
> 
> That can't be right.  Places like
>         if (err)
> 		goto out_unlock;
> do not expect err to be lost.  You end up returning 0 now.  Something 
strange
> happened here (in the previous commit, perhaps?)
>
I don't yet know. Maybe that it is related to a copy-paste error or something 
like that...

As said, I'll send next version ASAP.

Again thanks for your kind help,

Fabio




^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/3] fs/ufs: Replace kmap() with kmap_local_page()
       [not found]   ` <202212120803.iPhHqCqR-lkp@intel.com>
@ 2022-12-12  1:07     ` Al Viro
  0 siblings, 0 replies; 11+ messages in thread
From: Al Viro @ 2022-12-12  1:07 UTC (permalink / raw)
  To: kernel test robot
  Cc: Fabio M. De Francesco, Evgeniy Dushistov, Ira Weiny, linux-kernel,
	bpf, linux-fsdevel, llvm, oe-kbuild-all

On Mon, Dec 12, 2022 at 08:52:47AM +0800, kernel test robot wrote:

> >> fs/ufs/dir.c:210:22: warning: variable 'kaddr' is uninitialized when used here [-Wuninitialized]
>            ufs_put_page(*page, kaddr);
>                                ^~~~~
>    fs/ufs/dir.c:196:13: note: initialize the variable 'kaddr' to silence this warning
>            char *kaddr;
>                       ^
>                        = NULL
>    1 warning generated.

Warning is right, suggestion - worse than useless...

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/3] fs/ufs: Change the signature of ufs_get_page()
  2022-12-11 22:42   ` Al Viro
@ 2022-12-12 20:08     ` Fabio M. De Francesco
  0 siblings, 0 replies; 11+ messages in thread
From: Fabio M. De Francesco @ 2022-12-12 20:08 UTC (permalink / raw)
  To: Al Viro, Al Viro
  Cc: Evgeniy Dushistov, Ira Weiny, linux-kernel, bpf, linux-fsdevel,
	Evgeniy Dushistov, Ira Weiny, linux-kernel, bpf, linux-fsdevel

On domenica 11 dicembre 2022 23:42:26 CET Al Viro wrote:
> On Sun, Dec 11, 2022 at 10:31:10PM +0100, Fabio M. De Francesco wrote:
> >  out_put:
> >  	ufs_put_page(page);
> > 
> > -out:
> > -	return err;
> > 
> >  out_unlock:
> >  	unlock_page(page);
> >  	goto out_put;
> 
> Something strange has happened, all right - look at the situation
> after that patch.  You've got
> 
> out_put:
> 	ufs_put_page(page);
> out_unlock:
> 	unlock_page(page);
> 	goto out_put;
> 
> Which is obviously bogus.

I finally could go back to this small series and while working to fix the 
errors that yesterday you had found out I think I saw what happened...

Are you talking about ufs_add_link, right?

If so, you wrote what follows at point 14 of one of your emails:

-----

14) ufs_add_link() - similar adjustment to new calling conventions
for ufs_get_page().  Uses of page_addr: fed to ufs_put_page() (same as
in ufs_find_entry() kaddr is guaranteed to point into the same page and
thus can be used instead) and calculation of position in directory, same
as we'd seen in ufs_set_link().  The latter becomes page_offset(page) +
offset_in_page(de), killing page_addr off.  BTW, we get
                kaddr = ufs_get_page(dir, n, &page);
                err = PTR_ERR(kaddr);
                if (IS_ERR(kaddr))
                        goto out;
with out: being just 'return err;', which suggests
                kaddr = ufs_get_page(dir, n, &page);
                if (IS_ERR(kaddr))
                        return ERR_PTR(kaddr);
instead (and that was the only goto out; so the label can be removed).
The value stored in err in case !IS_ERR(kaddr) is (thankfully) never
used - would've been a bug otherwise.  So this is an equivalent 
transformation.

-----

Did you notice "so the label can be removed"?
I must have misinterpreted what you wrote there. Did I?

I removed the "out" label, according to what it seemed to me the correct way 
to interpret your words.

However at that moment I didn't see the endless loop at the end of the 
function. Then I "fixed" (sigh!) it in 3/3 by terminating that endless loop
with a "return 0". 

However that was another mistake because after "got_it:" label we have "err = 
ufs_commit_chunk(page, pos, rec_len);". 

To summarize: I can delete _only_ the label and leave the "return err;" in the 
block after the "out_put:" label. 

Am I looking at it correctly now?

Thanks,

Fabio



^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2022-12-12 20:09 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-12-11 21:31 [PATCH 0/3] fs/ufs: replace kmap() with kmap_local_page() Fabio M. De Francesco
2022-12-11 21:31 ` [PATCH 1/3] fs/ufs: Use the offset_in_page() helper Fabio M. De Francesco
2022-12-11 21:31 ` [PATCH 2/3] fs/ufs: Change the signature of ufs_get_page() Fabio M. De Francesco
2022-12-11 22:29   ` Al Viro
2022-12-11 23:56     ` Fabio M. De Francesco
2022-12-11 22:42   ` Al Viro
2022-12-12 20:08     ` Fabio M. De Francesco
2022-12-11 21:31 ` [PATCH 3/3] fs/ufs: Replace kmap() with kmap_local_page() Fabio M. De Francesco
2022-12-11 22:39   ` Al Viro
2022-12-12  0:14     ` Fabio M. De Francesco
     [not found]   ` <202212120803.iPhHqCqR-lkp@intel.com>
2022-12-12  1:07     ` Al Viro

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox