Skip to content

Commit 8300527

Browse files
xp4ns3Al Viro
authored andcommitted
fs/sysv: Replace kmap() with kmap_local_page()
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 kmap_local_page() would not break the strict rules of local mappings (i.e., the thread locality and the stack based nesting), this function can be easily and safely replace the deprecated API. Therefore, replace kmap() with kmap_local_page() in fs/sysv. kunmap_local() requires the mapping address, so return that address from dir_get_page() to be used in dir_put_page(). Suggested-by: Al Viro <[email protected]> Suggested-by: Ira Weiny <[email protected]> Signed-off-by: Fabio M. De Francesco <[email protected]> Signed-off-by: Al Viro <[email protected]>
1 parent c26ddc4 commit 8300527

File tree

3 files changed

+39
-23
lines changed

3 files changed

+39
-23
lines changed

fs/sysv/dir.c

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ const struct file_operations sysv_dir_operations = {
2828
.fsync = generic_file_fsync,
2929
};
3030

31-
inline void dir_put_page(struct page *page)
31+
inline void dir_put_page(struct page *page, void *page_addr)
3232
{
33-
kunmap(page);
33+
kunmap_local((void *)((unsigned long)page_addr & PAGE_MASK));
3434
put_page(page);
3535
}
3636

@@ -57,15 +57,21 @@ static int sysv_handle_dirsync(struct inode *dir)
5757
return err;
5858
}
5959

60+
/*
61+
* Calls to dir_get_page()/dir_put_page() must be nested according to the
62+
* rules documented in mm/highmem.rst.
63+
*
64+
* NOTE: sysv_find_entry() and sysv_dotdot() act as calls to dir_get_page()
65+
* and must be treated accordingly for nesting purposes.
66+
*/
6067
static void *dir_get_page(struct inode *dir, unsigned long n, struct page **p)
6168
{
6269
struct address_space *mapping = dir->i_mapping;
6370
struct page *page = read_mapping_page(mapping, n, NULL);
6471
if (IS_ERR(page))
6572
return ERR_CAST(page);
66-
kmap(page);
6773
*p = page;
68-
return page_address(page);
74+
return kmap_local_page(page);
6975
}
7076

7177
static int sysv_readdir(struct file *file, struct dir_context *ctx)
@@ -103,11 +109,11 @@ static int sysv_readdir(struct file *file, struct dir_context *ctx)
103109
if (!dir_emit(ctx, name, strnlen(name,SYSV_NAMELEN),
104110
fs16_to_cpu(SYSV_SB(sb), de->inode),
105111
DT_UNKNOWN)) {
106-
dir_put_page(page);
112+
dir_put_page(page, kaddr);
107113
return 0;
108114
}
109115
}
110-
dir_put_page(page);
116+
dir_put_page(page, kaddr);
111117
}
112118
return 0;
113119
}
@@ -130,6 +136,11 @@ static inline int namecompare(int len, int maxlen,
130136
* returns the cache buffer in which the entry was found, and the entry
131137
* itself (as a parameter - res_dir). It does NOT read the inode of the
132138
* entry - you'll have to do that yourself if you want to.
139+
*
140+
* On Success dir_put_page() should be called on *res_page.
141+
*
142+
* sysv_find_entry() acts as a call to dir_get_page() and must be treated
143+
* accordingly for nesting purposes.
133144
*/
134145
struct sysv_dir_entry *sysv_find_entry(struct dentry *dentry, struct page **res_page)
135146
{
@@ -161,7 +172,7 @@ struct sysv_dir_entry *sysv_find_entry(struct dentry *dentry, struct page **res_
161172
name, de->name))
162173
goto found;
163174
}
164-
dir_put_page(page);
175+
dir_put_page(page, kaddr);
165176
}
166177

167178
if (++n >= npages)
@@ -204,7 +215,7 @@ int sysv_add_link(struct dentry *dentry, struct inode *inode)
204215
goto out_page;
205216
de++;
206217
}
207-
dir_put_page(page);
218+
dir_put_page(page, kaddr);
208219
}
209220
BUG();
210221
return -EINVAL;
@@ -223,7 +234,7 @@ int sysv_add_link(struct dentry *dentry, struct inode *inode)
223234
mark_inode_dirty(dir);
224235
err = sysv_handle_dirsync(dir);
225236
out_page:
226-
dir_put_page(page);
237+
dir_put_page(page, kaddr);
227238
return err;
228239
out_unlock:
229240
unlock_page(page);
@@ -241,7 +252,7 @@ int sysv_delete_entry(struct sysv_dir_entry *de, struct page *page)
241252
BUG_ON(err);
242253
de->inode = 0;
243254
dir_commit_chunk(page, pos, SYSV_DIRSIZE);
244-
dir_put_page(page);
255+
dir_put_page(page, de);
245256
inode->i_ctime = inode->i_mtime = current_time(inode);
246257
mark_inode_dirty(inode);
247258
return sysv_handle_dirsync(inode);
@@ -261,9 +272,7 @@ int sysv_make_empty(struct inode *inode, struct inode *dir)
261272
unlock_page(page);
262273
goto fail;
263274
}
264-
kmap(page);
265-
266-
base = (char*)page_address(page);
275+
base = kmap_local_page(page);
267276
memset(base, 0, PAGE_SIZE);
268277

269278
de = (struct sysv_dir_entry *) base;
@@ -273,7 +282,7 @@ int sysv_make_empty(struct inode *inode, struct inode *dir)
273282
de->inode = cpu_to_fs16(SYSV_SB(inode->i_sb), dir->i_ino);
274283
strcpy(de->name,"..");
275284

276-
kunmap(page);
285+
kunmap_local(base);
277286
dir_commit_chunk(page, 0, 2 * SYSV_DIRSIZE);
278287
err = sysv_handle_dirsync(inode);
279288
fail:
@@ -289,10 +298,10 @@ int sysv_empty_dir(struct inode * inode)
289298
struct super_block *sb = inode->i_sb;
290299
struct page *page = NULL;
291300
unsigned long i, npages = dir_pages(inode);
301+
char *kaddr;
292302

293303
for (i = 0; i < npages; i++) {
294-
char *kaddr;
295-
struct sysv_dir_entry * de;
304+
struct sysv_dir_entry *de;
296305

297306
kaddr = dir_get_page(inode, i, &page);
298307
if (IS_ERR(kaddr))
@@ -316,12 +325,12 @@ int sysv_empty_dir(struct inode * inode)
316325
if (de->name[1] != '.' || de->name[2])
317326
goto not_empty;
318327
}
319-
dir_put_page(page);
328+
dir_put_page(page, kaddr);
320329
}
321330
return 1;
322331

323332
not_empty:
324-
dir_put_page(page);
333+
dir_put_page(page, kaddr);
325334
return 0;
326335
}
327336

@@ -338,12 +347,19 @@ void sysv_set_link(struct sysv_dir_entry *de, struct page *page,
338347
BUG_ON(err);
339348
de->inode = cpu_to_fs16(SYSV_SB(inode->i_sb), inode->i_ino);
340349
dir_commit_chunk(page, pos, SYSV_DIRSIZE);
341-
dir_put_page(page);
350+
dir_put_page(page, de);
342351
dir->i_mtime = dir->i_ctime = current_time(dir);
343352
mark_inode_dirty(dir);
344353
sysv_handle_dirsync(inode);
345354
}
346355

356+
/*
357+
* Calls to dir_get_page()/dir_put_page() must be nested according to the
358+
* rules documented in mm/highmem.rst.
359+
*
360+
* sysv_dotdot() acts as a call to dir_get_page() and must be treated
361+
* accordingly for nesting purposes.
362+
*/
347363
struct sysv_dir_entry *sysv_dotdot(struct inode *dir, struct page **p)
348364
{
349365
struct sysv_dir_entry *de = dir_get_page(dir, 0, p);
@@ -362,7 +378,7 @@ ino_t sysv_inode_by_name(struct dentry *dentry)
362378

363379
if (de) {
364380
res = fs16_to_cpu(SYSV_SB(dentry->d_sb), de->inode);
365-
dir_put_page(page);
381+
dir_put_page(page, de);
366382
}
367383
return res;
368384
}

fs/sysv/namei.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,9 @@ static int sysv_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
251251

252252
out_dir:
253253
if (dir_de)
254-
dir_put_page(dir_page);
254+
dir_put_page(dir_page, dir_de);
255255
out_old:
256-
dir_put_page(old_page);
256+
dir_put_page(old_page, old_de);
257257
out:
258258
return err;
259259
}

fs/sysv/sysv.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ extern void sysv_destroy_icache(void);
148148

149149

150150
/* dir.c */
151-
extern void dir_put_page(struct page *page);
151+
extern void dir_put_page(struct page *page, void *vaddr);
152152
extern struct sysv_dir_entry *sysv_find_entry(struct dentry *, struct page **);
153153
extern int sysv_add_link(struct dentry *, struct inode *);
154154
extern int sysv_delete_entry(struct sysv_dir_entry *, struct page *);

0 commit comments

Comments
 (0)