We can't just wholesale replace get_next_ino to use ino_t and 64-bit atomics because of a few setbacks: 1. This may break some 32-bit userspace applications on a 64-bit kernel which cannot handle a 64-bit ino_t -- see the comment above get_next_ino; 2. Some applications inside the kernel already make use of the ino_t high bits. For example, overlayfs' xino feature uses these to merge inode numbers and fsid indexes to form a new identifier. As such we need to make using the full width of ino_t an option for users without being a requirement. This will later be used to power inode64 in tmpfs, and can be used elsewhere for other filesystems as desired. Signed-off-by: Chris Down <chris@xxxxxxxxxxxxxx> Cc: Al Viro <viro@xxxxxxxxxxxxxxxxxx> Cc: Matthew Wilcox <willy@xxxxxxxxxxxxx> Cc: Amir Goldstein <amir73il@xxxxxxxxx> Cc: Jeff Layton <jlayton@xxxxxxxxxx> Cc: Johannes Weiner <hannes@xxxxxxxxxxx> Cc: Tejun Heo <tj@xxxxxxxxxx> Cc: linux-fsdevel@xxxxxxxxxxxxxxx Cc: linux-kernel@xxxxxxxxxxxxxxx Cc: kernel-team@xxxxxx --- fs/inode.c | 41 +++++++++++++++++++++++++++++++++++++---- include/linux/fs.h | 1 + 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index 255a4ae81b65..1d96ad8b71f6 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -878,16 +878,17 @@ static struct inode *find_inode_fast(struct super_block *sb, * here to attempt to avoid that. */ #define LAST_INO_BATCH 1024 -static DEFINE_PER_CPU(unsigned int, last_ino); +static DEFINE_PER_CPU(unsigned int, last_ino_ui); /* * As get_next_ino returns a type with a small width (typically 32 bits), * consider reusing inode numbers in your filesystem if you have a private inode - * cache in order to reduce the risk of wraparound. + * cache in order to reduce the risk of wraparound, or consider providing the + * option to use get_next_ino_full instead. */ unsigned int get_next_ino(void) { - unsigned int *p = &get_cpu_var(last_ino); + unsigned int *p = &get_cpu_var(last_ino_ui); unsigned int res = *p; #ifdef CONFIG_SMP @@ -904,11 +905,43 @@ unsigned int get_next_ino(void) if (unlikely(!res)) res++; *p = res; - put_cpu_var(last_ino); + put_cpu_var(last_ino_ui); return res; } EXPORT_SYMBOL(get_next_ino); +static DEFINE_PER_CPU(ino_t, last_ino_full); + +ino_t get_next_ino_full(void) +{ + ino_t *p = &get_cpu_var(last_ino_full); + ino_t res = *p; + +#ifdef CONFIG_SMP + if (unlikely((res & (LAST_INO_BATCH-1)) == 0)) { + static atomic64_t shared_last_ino; + u64 next = atomic64_add_return(LAST_INO_BATCH, + &shared_last_ino); + + /* + * This might get truncated if ino_t is 32-bit, and so be more + * susceptible to wrap around than on environments where ino_t + * is 64-bit. + */ + res = next - LAST_INO_BATCH; + } +#endif + + res++; + /* get_next_ino_full should not provide a 0 inode number */ + if (unlikely(!res)) + res++; + *p = res; + put_cpu_var(last_ino_full); + return res; +} +EXPORT_SYMBOL(get_next_ino_full); + /** * new_inode_pseudo - obtain an inode * @sb: superblock diff --git a/include/linux/fs.h b/include/linux/fs.h index 190c45039359..c73896d993c1 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3053,6 +3053,7 @@ static inline void lockdep_annotate_inode_mutex_key(struct inode *inode) { }; extern void unlock_new_inode(struct inode *); extern void discard_new_inode(struct inode *); extern unsigned int get_next_ino(void); +extern ino_t get_next_ino_full(void); extern void evict_inodes(struct super_block *sb); extern void __iget(struct inode * inode); -- 2.24.1