mm.git review status for linus..mm-nonmm-hotfixes-stable

Everything:
 
 Total patches:       22
 Fixes: tags:         19
 cc:stable patches:   17
 Reviews/patch:       0.77
 Reviewed rate:       68%
 
 22 patches. 17 are cc:stable.
 
 Most are OCFS2 fixes. These address extended-attribute corruption,
 missing attributes and kernel BUGs, along with crashes during cluster
 configuration, lock migration, recovery and unmount. Extent movement
 is also fixed to validate its range against a stable file size.
 
 Three FAT changes prevent a failed-write/FIBMAP race from spuriously
 making the filesystem read-only, correct directory link counts after
 rename, and speed up file expansion. The reported QEMU benchmarks show
 particularly large improvements on synchronous mounts.
 
 A taint-string fix prevents an out-of-bounds read which can expose slab
 contents in the kernel log. There are also build fixes for OpenRISC
 modules on CPUs without hardware multiply and for Clang 24.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQTTMBEPP41GrTpTJgfdBJ7gKXxAjgUCauwFhgAKCRDdBJ7gKXxA
 jukUAP4xNDGCuRNda8im/hJ7TzKmZg4j+raQASTj4SMaTmWR8AD8DrGZ1jLMM05q
 Ftj9PQ99DpBw74qIthIMH1nCzJdsSwI=
 =5/Ef
 -----END PGP SIGNATURE-----

Merge tag 'mm-nonmm-hotfixes-stable-2026-11-04-20-26' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm

Pull non-MM fixes from Andrew Morton:
 "22 patches. 17 are cc:stable.

  Most are OCFS2 fixes. These address extended-attribute corruption,
  missing attributes and kernel BUGs, along with crashes during cluster
  configuration, lock migration, recovery and unmount. Extent movement
  is also fixed to validate its range against a stable file size.

  Three FAT changes prevent a failed-write/FIBMAP race from spuriously
  making the filesystem read-only, correct directory link counts after
  rename, and speed up file expansion. The reported QEMU benchmarks show
  particularly large improvements on synchronous mounts.

  A taint-string fix prevents an out-of-bounds read which can expose
  slab contents in the kernel log. There are also build fixes for
  OpenRISC modules on CPUs without hardware multiply and for Clang 24"

* tag 'mm-nonmm-hotfixes-stable-2026-11-04-20-26' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: (22 commits)
  ocfs2: cache the mount context for dentry lock callbacks
  ocfs2: wait for filecheck kobject release after a failed add
  fat: fix parent link count when replacing a sibling directory
  ocfs2/cluster: unlink socket containers before releasing resources
  ocfs2: stop recovery before shutting down local allocators
  ocfs2/dlm: serialize migration MLE cleanup on failure
  panic: zero the dynamically allocated taint buffer
  ocfs2: keep locking_state within the cluster connection lifetime
  MAINTAINERS: mailmap: use Clément Léger's linux.dev mail address
  MAINTAINERS: add an IPC entry
  ocfs2: validate move extents against a stable EOF
  fat: hold truncate_lock in fat_write_failed()
  fat: preallocate space before expanding in fat_cont_expand()
  ocfs2: do not read the entry array of an empty xattr block
  ocfs2: do not index before the entry array when dividing an empty bucket
  ocfs2: do not read the entry array of an empty xattr bucket
  ocfs2: find the xattr entry again after converting a block to a bucket
  ocfs2: find the xattr entry again after defragmenting a bucket
  ocfs2: read the xattr entry hash before defragmenting the bucket
  ocfs2/heartbeat: serialize concurrent writes to a region's dev attribute
  ...
This commit is contained in:
Linus Torvalds 2026-11-05 13:05:13 -08:00
commit 46a062865b
22 changed files with 388 additions and 128 deletions

View File

@ -233,6 +233,10 @@ Chuck Lever <cel@kernel.org> <chuck.lever@oracle.com>
Chuck Lever <cel@kernel.org> <cel@netapp.com>
Chuck Lever <cel@kernel.org> <cel@citi.umich.edu>
Claudiu Beznea <claudiu.beznea@tuxon.dev> <claudiu.beznea@microchip.com>
Clément Léger <clement.leger@linux.dev> <cleger@kalray.eu>
Clément Léger <clement.leger@linux.dev> <clement.leger@bootlin.com>
Clément Léger <clement.leger@linux.dev> <cleger@rivosinc.com>
Clément Léger <clement.leger@linux.dev> <cleger@meta.com>
Coiby Xu <coiby.xu@gmail.com> <coxu@redhat.com>
Colin Ian King <colin.i.king@gmail.com> <colin.king@canonical.com>
Corey Minyard <minyard@acm.org>

View File

@ -14075,6 +14075,16 @@ S: Maintained
F: io_uring/bpf-ops.*
F: io_uring/loop.*
IPC
M: Andrew Morton <akpm@linux-foundation.org>
R: Manfred Spraul <manfred@colorfullife.com>
R: Davidlohr Bueso <dave@stgolabs.net>
L: linux-kernel@vger.kernel.org
S: Maintained
F: ipc/
F: tools/testing/selftests/ipc/
F: tools/testing/selftests/mqueue/
IPMI SUBSYSTEM
M: Corey Minyard <corey@minyard.net>
L: openipmi-developer@lists.sourceforge.net (moderated for non-subscribers)
@ -21003,7 +21013,7 @@ S: Maintained
F: drivers/char/hw_random/optee-rng.c
OP-TEE RTC DRIVER
M: Clément Léger <clement.leger@bootlin.com>
M: Clément Léger <clement.leger@linux.dev>
L: linux-rtc@vger.kernel.org
S: Maintained
F: drivers/rtc/rtc-optee.c
@ -23903,7 +23913,7 @@ F: Documentation/devicetree/bindings/rtc/renesas,rz-rtca3.yaml
F: drivers/rtc/rtc-renesas-rtca3.c
RENESAS RZ/N1 A5PSW SWITCH DRIVER
M: Clément Léger <clement.leger@bootlin.com>
M: Clément Léger <clement.leger@linux.dev>
L: linux-renesas-soc@vger.kernel.org
L: netdev@vger.kernel.org
S: Maintained

View File

@ -35,6 +35,7 @@ DECLARE_EXPORT(__divsi3);
DECLARE_EXPORT(__umodsi3);
DECLARE_EXPORT(__modsi3);
DECLARE_EXPORT(__muldi3);
DECLARE_EXPORT(__mulsi3);
DECLARE_EXPORT(__ashrdi3);
DECLARE_EXPORT(__ashldi3);
DECLARE_EXPORT(__lshrdi3);

View File

@ -963,6 +963,16 @@ int fat_dir_empty(struct inode *dir)
}
EXPORT_SYMBOL_GPL(fat_dir_empty);
void fat_dir_drop_nlink(struct inode *dir)
{
if (dir->i_nlink >= 3)
drop_nlink(dir);
else
fat_fs_error(dir->i_sb, "parent dir link count too low (%u)",
dir->i_nlink);
}
EXPORT_SYMBOL_GPL(fat_dir_drop_nlink);
/*
* fat_subdirs counts the number of sub-directories of dir. It can be run
* on directories being created.

View File

@ -332,6 +332,7 @@ extern const struct file_operations fat_dir_operations;
extern int fat_search_long(struct inode *inode, const unsigned char *name,
int name_len, struct fat_slot_info *sinfo);
extern int fat_dir_empty(struct inode *dir);
void fat_dir_drop_nlink(struct inode *dir);
extern int fat_subdirs(struct inode *dir);
extern int fat_scan(struct inode *dir, const unsigned char *name,
struct fat_slot_info *sinfo);

View File

@ -217,13 +217,62 @@ const struct file_operations fat_file_operations = {
.setlease = generic_setlease,
};
static int fat_preallocate(struct inode *inode, loff_t new_size)
{
int nr_cluster; /* Number of clusters to be allocated */
loff_t mm_bytes; /* Number of bytes to be allocated for file */
loff_t ondisksize; /* block aligned on-disk size in bytes*/
struct super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
ondisksize = inode->i_blocks << 9;
if (new_size <= ondisksize)
return 0;
/* First compute the number of clusters to be allocated */
mm_bytes = new_size - ondisksize;
nr_cluster = (mm_bytes + (sbi->cluster_size - 1)) >>
sbi->cluster_bits;
/* Allocate the clusters. We are not zeroing them out */
return fat_add_clusters(inode, nr_cluster);
}
static int fat_cont_expand_simple(struct inode *inode, loff_t size)
{
struct address_space *mapping = inode->i_mapping;
const struct address_space_operations *aops = mapping->a_ops;
struct folio *folio;
void *fsdata = NULL;
int err;
err = aops->write_begin(NULL, mapping, size, 0, &folio, &fsdata);
if (err)
goto out;
err = aops->write_end(NULL, mapping, size, 0, 0, folio, fsdata);
out:
return err;
}
static int fat_cont_expand(struct inode *inode, loff_t size)
{
struct address_space *mapping = inode->i_mapping;
loff_t start = inode->i_size, count = size - inode->i_size;
int err;
err = generic_cont_expand_simple(inode, size);
/*
* Preallocating space here is more efficient than during
* fat_cont_expand_simple() because of batching.
*/
err = inode_newsize_ok(inode, size);
if (err)
goto out;
err = fat_preallocate(inode, size);
if (err)
goto out;
err = fat_cont_expand_simple(inode, size);
if (err)
goto out;
@ -263,12 +312,7 @@ static int fat_cont_expand(struct inode *inode, loff_t size)
static long fat_fallocate(struct file *file, int mode,
loff_t offset, loff_t len)
{
int nr_cluster; /* Number of clusters to be allocated */
loff_t mm_bytes; /* Number of bytes to be allocated for file */
loff_t ondisksize; /* block aligned on-disk size in bytes*/
struct inode *inode = file->f_mapping->host;
struct super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
int err = 0;
/* No support for hole punch or other fallocate flags. */
@ -281,19 +325,7 @@ static long fat_fallocate(struct file *file, int mode,
inode_lock(inode);
if (mode & FALLOC_FL_KEEP_SIZE) {
ondisksize = inode->i_blocks << 9;
if ((offset + len) <= ondisksize)
goto error;
/* First compute the number of clusters to be allocated */
mm_bytes = offset + len - ondisksize;
nr_cluster = (mm_bytes + (sbi->cluster_size - 1)) >>
sbi->cluster_bits;
/* Allocate the clusters. We are not zeroing them out */
err = fat_add_clusters(inode, nr_cluster);
if (err)
goto error;
err = fat_preallocate(inode, offset + len);
} else {
if ((offset + len) <= i_size_read(inode))
goto error;

View File

@ -217,7 +217,9 @@ static void fat_write_failed(struct address_space *mapping, loff_t to)
if (to > inode->i_size) {
truncate_pagecache(inode, inode->i_size);
down_write(&MSDOS_I(inode)->truncate_lock);
fat_truncate_blocks(inode, inode->i_size);
up_write(&MSDOS_I(inode)->truncate_lock);
}
}

View File

@ -333,12 +333,7 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
err = fat_remove_entries(dir, &sinfo); /* and releases bh */
if (err)
goto out;
if (dir->i_nlink >= 3)
drop_nlink(dir);
else {
fat_fs_error(sb, "parent dir link count too low (%u)",
dir->i_nlink);
}
fat_dir_drop_nlink(dir);
clear_nlink(inode);
fat_detach(inode);
@ -565,6 +560,8 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
(void)sync_inode_metadata(old_dir, 1);
if (new_inode) {
if (is_dir && old_dir == new_dir)
fat_dir_drop_nlink(old_dir);
drop_nlink(new_inode);
if (is_dir)
drop_nlink(new_inode);

View File

@ -809,12 +809,7 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
err = fat_remove_entries(dir, &sinfo); /* and releases bh */
if (err)
goto out;
if (dir->i_nlink >= 3)
drop_nlink(dir);
else {
fat_fs_error(sb, "parent dir link count too low (%u)",
dir->i_nlink);
}
fat_dir_drop_nlink(dir);
clear_nlink(inode);
fat_truncate_time(inode, NULL, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME);
@ -1011,6 +1006,8 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
vfat_update_dir_metadata(old_dir, &ts);
if (new_inode) {
if (is_dir && old_dir == new_dir)
fat_dir_drop_nlink(old_dir);
drop_nlink(new_inode);
if (is_dir)
drop_nlink(new_inode);

View File

@ -269,6 +269,8 @@ struct o2hb_region {
struct list_head hr_handler_list;
/* Serializes timeout arming against failed-start and teardown. */
struct mutex hr_arming_mutex;
/* Serializes the dev, block_bytes, start_block and blocks attributes. */
struct mutex hr_dev_mutex;
bool hr_stopping;
/* last hb status, 0 for success, other value for error. */
@ -1681,6 +1683,7 @@ static ssize_t o2hb_region_block_bytes_store(struct config_item *item,
unsigned long block_bytes;
unsigned int block_bits;
guard(mutex)(&reg->hr_dev_mutex);
if (reg->hr_bdev_file)
return -EINVAL;
@ -1710,6 +1713,7 @@ static ssize_t o2hb_region_start_block_store(struct config_item *item,
char *p = (char *)page;
ssize_t ret;
guard(mutex)(&reg->hr_dev_mutex);
if (reg->hr_bdev_file)
return -EINVAL;
@ -1736,6 +1740,7 @@ static ssize_t o2hb_region_blocks_store(struct config_item *item,
char *p = (char *)page;
int ret;
guard(mutex)(&reg->hr_dev_mutex);
if (reg->hr_bdev_file)
return -EINVAL;
@ -1753,10 +1758,12 @@ static ssize_t o2hb_region_blocks_store(struct config_item *item,
static ssize_t o2hb_region_dev_show(struct config_item *item, char *page)
{
struct o2hb_region *reg = to_o2hb_region(item);
unsigned int ret = 0;
if (to_o2hb_region(item)->hr_bdev_file)
ret = sprintf(page, "%pg\n", reg_bdev(to_o2hb_region(item)));
guard(mutex)(&reg->hr_dev_mutex);
if (reg->hr_bdev_file)
ret = sprintf(page, "%pg\n", reg_bdev(reg));
return ret;
}
@ -1872,11 +1879,9 @@ static int o2hb_populate_slot_data(struct o2hb_region *reg)
* this is acting as commit; we set up all of hr_bdev_file and hr_task or
* nothing
*/
static ssize_t o2hb_region_dev_store(struct config_item *item,
const char *page,
size_t count)
static ssize_t __o2hb_region_dev_store(struct o2hb_region *reg,
const char *page, size_t count)
{
struct o2hb_region *reg = to_o2hb_region(item);
struct task_struct *hb_task;
long fd;
int sectsize;
@ -1884,6 +1889,8 @@ static ssize_t o2hb_region_dev_store(struct config_item *item,
ssize_t ret = -EINVAL;
int live_threshold;
lockdep_assert_held(&reg->hr_dev_mutex);
if (reg->hr_bdev_file)
return -EINVAL;
@ -1987,9 +1994,15 @@ static ssize_t o2hb_region_dev_store(struct config_item *item,
wake_up_process(hb_task);
/*
* Don't hold hr_dev_mutex across the wait, which can take seconds.
* hr_bdev_file is already set, so other writers still get -EINVAL.
*/
mutex_unlock(&reg->hr_dev_mutex);
ret = wait_event_interruptible(o2hb_steady_queue,
atomic_read(&reg->hr_steady_iterations) == 0 ||
reg->hr_node_deleted);
mutex_lock(&reg->hr_dev_mutex);
if (ret) {
atomic_set(&reg->hr_steady_iterations, 0);
reg->hr_aborted_start = 1;
@ -2041,6 +2054,20 @@ static ssize_t o2hb_region_dev_store(struct config_item *item,
return ret;
}
static ssize_t o2hb_region_dev_store(struct config_item *item,
const char *page,
size_t count)
{
struct o2hb_region *reg = to_o2hb_region(item);
ssize_t ret;
mutex_lock(&reg->hr_dev_mutex);
ret = __o2hb_region_dev_store(reg, page, count);
mutex_unlock(&reg->hr_dev_mutex);
return ret;
}
static ssize_t o2hb_region_pid_show(struct config_item *item, char *page)
{
struct o2hb_region *reg = to_o2hb_region(item);
@ -2143,6 +2170,7 @@ static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *g
INIT_LIST_HEAD(&reg->hr_all_item);
mutex_init(&reg->hr_arming_mutex);
mutex_init(&reg->hr_dev_mutex);
reg->hr_stopping = true;
INIT_DELAYED_WORK(&reg->hr_write_timeout_work, o2hb_write_timeout);
INIT_DELAYED_WORK(&reg->hr_nego_timeout_work, o2hb_nego_timeout);

View File

@ -393,6 +393,9 @@ static void sc_kref_release(struct kref *kref)
sclog(sc, "releasing\n");
/* Unlink before freeing anything the debugfs readers dereference. */
o2net_debug_del_sc(sc);
if (sc->sc_sock) {
sock_release(sc->sc_sock);
sc->sc_sock = NULL;
@ -402,8 +405,6 @@ static void sc_kref_release(struct kref *kref)
o2nm_node_put(sc->sc_node);
sc->sc_node = NULL;
o2net_debug_del_sc(sc);
if (sc->sc_page)
__free_page(sc->sc_page);
kfree(sc);

View File

@ -17,9 +17,8 @@ struct ocfs2_dentry_lock {
u64 dl_parent_blkno;
/*
* The ocfs2_dentry_lock keeps an inode reference until
* dl_lockres has been destroyed. This is usually done in
* ->d_iput() anyway, so there should be minimal impact.
* The inode reference is dropped before dl_lockres is unlocked.
* Callbacks must not use it to find the mount context during teardown.
*/
struct inode *dl_inode;
struct ocfs2_lock_res dl_lockres;

View File

@ -2525,6 +2525,52 @@ static int dlm_is_lockres_migratable(struct dlm_ctxt *dlm,
*/
/*
* Drop the sender's references on a migration MLE after the attempt has
* failed. The finishing assert from the target may already have unlinked
* the MLE and consumed its base reference; decide under the locks the
* assert handler holds, and only drop the base reference while the MLE is
* still hashed. Returns true when the assert got there first: the target
* has become the master, so the caller must complete the migration.
*/
static bool dlm_put_migration_mle(struct dlm_ctxt *dlm,
struct dlm_master_list_entry *mle)
{
bool asserted;
spin_lock(&dlm->spinlock);
spin_lock(&dlm->master_lock);
__dlm_mle_detach_hb_events(dlm, mle);
asserted = hlist_unhashed(&mle->master_hash_node);
if (!asserted) {
__dlm_unlink_mle(dlm, mle);
__dlm_put_mle(mle);
}
spin_unlock(&dlm->master_lock);
spin_unlock(&dlm->spinlock);
dlm_put_mle_inuse(mle);
return asserted;
}
/*
* The finishing assert got there first: it switched the owner to the
* target and cleared DLM_LOCK_RES_MIGRATING. Drop the other nodes' locks
* as a completed migration does, unless recovery has taken the lockres
* over since, and let the usage be recalculated.
*/
static void dlm_migrate_lockres_asserted(struct dlm_ctxt *dlm,
struct dlm_lock_resource *res,
u8 target)
{
spin_lock(&res->spinlock);
if (res->owner == target)
dlm_remove_nonlocal_locks(dlm, res);
spin_unlock(&res->spinlock);
dlm_lockres_calc_usage(dlm, res);
}
static int dlm_migrate_lockres(struct dlm_ctxt *dlm,
struct dlm_lock_resource *res, u8 target)
{
@ -2609,9 +2655,10 @@ static int dlm_migrate_lockres(struct dlm_ctxt *dlm,
if (ret < 0) {
if (mle_added) {
dlm_mle_detach_hb_events(dlm, mle);
dlm_put_mle(mle);
dlm_put_mle_inuse(mle);
if (dlm_put_migration_mle(dlm, mle)) {
dlm_migrate_lockres_asserted(dlm, res, target);
ret = 0;
}
} else if (mle) {
kmem_cache_free(dlm_mle_cache, mle);
mle = NULL;
@ -2640,9 +2687,11 @@ static int dlm_migrate_lockres(struct dlm_ctxt *dlm,
mlog(0, "migration to node %u failed with %d\n",
target, ret);
/* migration failed, detach and clean up mle */
dlm_mle_detach_hb_events(dlm, mle);
dlm_put_mle(mle);
dlm_put_mle_inuse(mle);
if (dlm_put_migration_mle(dlm, mle)) {
dlm_migrate_lockres_asserted(dlm, res, target);
ret = 0;
goto leave;
}
spin_lock(&res->spinlock);
res->state &= ~DLM_LOCK_RES_MIGRATING;
wake = 1;
@ -2686,9 +2735,12 @@ static int dlm_migrate_lockres(struct dlm_ctxt *dlm,
res->lockname.name, target);
ret = -EINVAL;
/* migration failed, detach and clean up mle */
dlm_mle_detach_hb_events(dlm, mle);
dlm_put_mle(mle);
dlm_put_mle_inuse(mle);
if (dlm_put_migration_mle(dlm, mle)) {
dlm_migrate_lockres_asserted(dlm, res,
target);
ret = 0;
goto leave;
}
spin_lock(&res->spinlock);
res->state &= ~DLM_LOCK_RES_MIGRATING;
wake = 1;

View File

@ -58,7 +58,6 @@ struct ocfs2_mask_waiter {
#endif
};
static struct ocfs2_super *ocfs2_get_dentry_osb(struct ocfs2_lock_res *lockres);
static struct ocfs2_super *ocfs2_get_inode_osb(struct ocfs2_lock_res *lockres);
static struct ocfs2_super *ocfs2_get_file_osb(struct ocfs2_lock_res *lockres);
static struct ocfs2_super *ocfs2_get_qinfo_osb(struct ocfs2_lock_res *lockres);
@ -258,7 +257,6 @@ static const struct ocfs2_lock_res_ops ocfs2_orphan_scan_lops = {
};
static const struct ocfs2_lock_res_ops ocfs2_dentry_lops = {
.get_osb = ocfs2_get_dentry_osb,
.post_unlock = ocfs2_dentry_post_unlock,
.downconvert_worker = ocfs2_dentry_convert_worker,
.flags = 0,
@ -309,7 +307,7 @@ static inline struct ocfs2_dentry_lock *ocfs2_lock_res_dl(struct ocfs2_lock_res
{
BUG_ON(lockres->l_type != OCFS2_LOCK_TYPE_DENTRY);
return (struct ocfs2_dentry_lock *)lockres->l_priv;
return container_of(lockres, struct ocfs2_dentry_lock, dl_lockres);
}
static inline struct ocfs2_mem_dqinfo *ocfs2_lock_res_qinfo(struct ocfs2_lock_res *lockres)
@ -610,13 +608,6 @@ static __u64 ocfs2_get_dentry_lock_ino(struct ocfs2_lock_res *lockres)
return be64_to_cpu(inode_blkno_be);
}
static struct ocfs2_super *ocfs2_get_dentry_osb(struct ocfs2_lock_res *lockres)
{
struct ocfs2_dentry_lock *dl = lockres->l_priv;
return OCFS2_SB(dl->dl_inode->i_sb);
}
void ocfs2_dentry_lock_res_init(struct ocfs2_dentry_lock *dl,
u64 parent, struct inode *inode)
{
@ -624,6 +615,7 @@ void ocfs2_dentry_lock_res_init(struct ocfs2_dentry_lock *dl,
u64 inode_blkno = OCFS2_I(inode)->ip_blkno;
__be64 inode_blkno_be = cpu_to_be64(inode_blkno);
struct ocfs2_lock_res *lockres = &dl->dl_lockres;
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
ocfs2_lock_res_init_once(lockres);
@ -652,9 +644,9 @@ void ocfs2_dentry_lock_res_init(struct ocfs2_dentry_lock *dl,
memcpy(&lockres->l_name[OCFS2_DENTRY_LOCK_INO_START], &inode_blkno_be,
sizeof(__be64));
ocfs2_lock_res_init_common(OCFS2_SB(inode->i_sb), lockres,
ocfs2_lock_res_init_common(osb, lockres,
OCFS2_LOCK_TYPE_DENTRY, &ocfs2_dentry_lops,
dl);
osb);
}
static void ocfs2_super_lock_res_init(struct ocfs2_lock_res *res,
@ -3356,6 +3348,14 @@ static void ocfs2_dlm_shutdown_debug(struct ocfs2_super *osb)
{
struct ocfs2_dlm_debug *dlm_debug = osb->osb_dlm_debug;
/*
* Drain the readers before the cluster stack goes away: the
* locking_state reader resolves ocfs2_dlm_lvb() through the
* stack. Every caller removes the parent directory later.
*/
debugfs_lookup_and_remove("locking_state", osb->osb_debug_root);
debugfs_lookup_and_remove("locking_filter", osb->osb_debug_root);
if (dlm_debug)
ocfs2_put_dlm_debug(dlm_debug);
}
@ -3370,8 +3370,6 @@ int ocfs2_dlm_init(struct ocfs2_super *osb)
goto local;
}
ocfs2_dlm_init_debug(osb);
/* launch downconvert thread */
osb->dc_task = kthread_run(ocfs2_downconvert_thread, osb, "ocfs2dc-%s",
osb->uuid_str);
@ -3411,9 +3409,10 @@ int ocfs2_dlm_init(struct ocfs2_super *osb)
ocfs2_orphan_scan_lock_res_init(&osb->osb_orphan_scan.os_lockres, osb);
osb->cconn = conn;
if (conn)
ocfs2_dlm_init_debug(osb);
bail:
if (status < 0) {
ocfs2_dlm_shutdown_debug(osb);
if (osb->dc_task)
kthread_stop(osb->dc_task);
}
@ -3443,10 +3442,11 @@ void ocfs2_dlm_shutdown(struct ocfs2_super *osb,
ocfs2_lock_res_free(&osb->osb_orphan_scan.os_lockres);
if (osb->cconn) {
/* The disconnect below may release the selected stack. */
ocfs2_dlm_shutdown_debug(osb);
ocfs2_cluster_disconnect(osb->cconn, hangup_pending);
osb->cconn = NULL;
ocfs2_dlm_shutdown_debug(osb);
}
}

View File

@ -189,6 +189,8 @@ int ocfs2_filecheck_create_sysfs(struct ocfs2_super *osb)
/* Mount failure still calls ocfs2_filecheck_remove_sysfs(). */
entry->fs_fcheck = NULL;
kobject_put(&entry->fs_kobj);
/* Keep ocfs2_super alive until the kobject is released. */
wait_for_completion(&entry->fs_kobj_unregister);
kfree(fcheck);
return ret;
}

View File

@ -227,13 +227,22 @@ void ocfs2_recovery_disable_quota(struct ocfs2_super *osb)
ocfs2_recovery_disable(osb, OCFS2_REC_QUOTA_WANT_DISABLE);
}
/* Stop producers and drain completion work without freeing recovery state. */
void ocfs2_recovery_stop(struct ocfs2_super *osb)
{
ocfs2_recovery_disable(osb, OCFS2_REC_WANT_DISABLE);
}
void ocfs2_recovery_exit(struct ocfs2_super *osb)
{
struct ocfs2_recovery_map *rm;
/* disable any new recovery threads and wait for any currently
* running ones to exit. Do this before setting the vol_state. */
ocfs2_recovery_disable(osb, OCFS2_REC_WANT_DISABLE);
/*
* Disable producers and drain work before freeing recovery state.
* Do this before setting vol_state to VOLUME_DISMOUNTED: completion
* work may still be waiting in __ocfs2_wait_on_mount().
*/
ocfs2_recovery_stop(osb);
/*
* Now that recovery is shut down, and the osb is about to be

View File

@ -147,6 +147,7 @@ void ocfs2_complete_recovery(struct work_struct *work);
void ocfs2_wait_for_recovery(struct ocfs2_super *osb);
int ocfs2_recovery_init(struct ocfs2_super *osb);
void ocfs2_recovery_stop(struct ocfs2_super *osb);
void ocfs2_recovery_exit(struct ocfs2_super *osb);
void ocfs2_recovery_disable_quota(struct ocfs2_super *osb);

View File

@ -907,6 +907,30 @@ static int __ocfs2_move_extents_range(struct buffer_head *di_bh,
return ret;
}
static int ocfs2_validate_and_adjust_move_range(struct inode *inode,
struct ocfs2_move_extents *range)
{
u64 i_size = i_size_read(inode);
if (range->me_start > i_size)
return -EINVAL;
if (range->me_len > i_size - range->me_start)
range->me_len = i_size - range->me_start;
/*
* The default threshold for defragmentation is 1 MiB, which
* matches the maximum OCFS2 cluster size.
*/
if (!range->me_threshold)
range->me_threshold = 1024 * 1024;
if (range->me_threshold > i_size)
range->me_threshold = i_size;
return 0;
}
static int ocfs2_move_extents(struct ocfs2_move_extents_context *context)
{
int status;
@ -936,6 +960,21 @@ static int ocfs2_move_extents(struct ocfs2_move_extents_context *context)
goto out_rw_unlock;
}
status = ocfs2_validate_and_adjust_move_range(inode, context->range);
if (status) {
mlog_errno(status);
goto out_inode_unlock;
}
/* The goal check depends on the adjusted range length. */
if (!context->auto_defrag) {
status = ocfs2_validate_and_adjust_move_goal(inode, context->range);
if (status) {
mlog_errno(status);
goto out_inode_unlock;
}
}
/*
* remember ip_xattr_sem also needs to be held if necessary
*/
@ -1028,27 +1067,8 @@ int ocfs2_ioctl_move_extents(struct file *filp, void __user *argp)
goto out_free;
}
if (range.me_start > i_size_read(inode)) {
status = -EINVAL;
goto out_free;
}
if (range.me_start + range.me_len > i_size_read(inode))
range.me_len = i_size_read(inode) - range.me_start;
context->range = &range;
/*
* ok, the default threshold for the defragmentation
* is 1M, since our maximum clustersize was 1M also.
* any thought?
*/
if (!range.me_threshold)
range.me_threshold = 1024 * 1024;
if (range.me_threshold > i_size_read(inode))
range.me_threshold = i_size_read(inode);
if (range.me_flags & ~(OCFS2_MOVE_EXT_FL_AUTO_DEFRAG |
OCFS2_MOVE_EXT_FL_PART_DEFRAG)) {
status = -EINVAL;
@ -1060,23 +1080,11 @@ int ocfs2_ioctl_move_extents(struct file *filp, void __user *argp)
if (range.me_flags & OCFS2_MOVE_EXT_FL_PART_DEFRAG)
context->partial = 1;
} else {
/*
* first best-effort attempt to validate and adjust the goal
* (physical address in block), while it can't guarantee later
* operation can succeed all the time since global_bitmap may
* change a bit over time.
*/
status = ocfs2_validate_and_adjust_move_goal(inode, &range);
if (status)
goto out_copy;
}
status = ocfs2_move_extents(context);
if (status)
mlog_errno(status);
out_copy:
/*
* movement/defragmentation may end up being partially completed,
* that's the reason why we need to return userspace the finished

View File

@ -1831,6 +1831,9 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err)
/* Wait for worker to be done with the work structure in osb */
cancel_work_sync(&osb->dquot_drop_work);
/* Stop recovery producers before releasing their local allocators. */
ocfs2_recovery_stop(osb);
ocfs2_shutdown_local_alloc(osb);
ocfs2_truncate_log_shutdown(osb);

View File

@ -275,6 +275,7 @@ static int ocfs2_xattr_tree_list_index_block(struct inode *inode,
size_t buffer_size);
static int ocfs2_xattr_create_index_block(struct inode *inode,
struct ocfs2_xattr_info *xi,
struct ocfs2_xattr_search *xs,
struct ocfs2_xattr_set_ctxt *ctxt);
@ -3375,7 +3376,7 @@ static int ocfs2_xattr_block_set(struct inode *inode,
else if ((ret != -ENOSPC) || ctxt->set_abort)
goto end;
else {
ret = ocfs2_xattr_create_index_block(inode, xs, ctxt);
ret = ocfs2_xattr_create_index_block(inode, xi, xs, ctxt);
if (ret)
goto end;
}
@ -4703,6 +4704,21 @@ static void ocfs2_cp_xattr_block_to_bucket(struct inode *inode,
for (i = 0; i < blks; i++)
memset(bucket_block(bucket, i), 0, blocksize);
/*
* An empty block has no names or values to carry over, and the
* xh_entries[count - 1] below would be xh_entries[-1]. Leave the
* bucket empty, but carry over the hash ocfs2_xa_remove_entry() left
* in the block's xh_entries[0] so that the bucket still holds the key
* it is looked up by.
*/
if (!count) {
xh->xh_num_buckets = cpu_to_le16(1);
xh->xh_free_start = cpu_to_le16(OCFS2_XATTR_BUCKET_SIZE);
xh->xh_entries[0].xe_name_hash =
xb_xh->xh_entries[0].xe_name_hash;
return;
}
/*
* Since the xe_name_offset is based on ocfs2_xattr_header,
* there is a offset change corresponding to the change of
@ -4749,13 +4765,10 @@ static void ocfs2_cp_xattr_block_to_bucket(struct inode *inode,
* real place of the xattr.
*/
static void ocfs2_xattr_update_xattr_search(struct inode *inode,
struct ocfs2_xattr_search *xs,
struct buffer_head *old_bh)
struct ocfs2_xattr_info *xi,
struct ocfs2_xattr_search *xs)
{
char *buf = old_bh->b_data;
struct ocfs2_xattr_block *old_xb = (struct ocfs2_xattr_block *)buf;
struct ocfs2_xattr_header *old_xh = &old_xb->xb_attrs.xb_header;
int i;
int i, count;
xs->header = bucket_xh(xs->bucket);
xs->base = bucket_block(xs->bucket, 0);
@ -4764,8 +4777,43 @@ static void ocfs2_xattr_update_xattr_search(struct inode *inode,
if (xs->not_found)
return;
i = xs->here - old_xh->xh_entries;
xs->here = &xs->header->xh_entries[i];
/*
* A block holds its entries in insertion order while a bucket holds
* them by name hash, so ocfs2_cp_xattr_block_to_bucket() sorted the
* copies and the index this entry had in the block no longer says
* where it is. It copied every entry and every name unchanged, and
* (type, name) pairs are unique within a block, so find it again by
* name.
*
* Search for the name being set, not for the name xs->here carries.
* A failed set may have removed that entry, and ocfs2_xa_remove_entry()
* then memmoves its successor into the slot. Comparing every entry
* rather than narrowing on the hash also finds an entry stored under
* the legacy signed hash, which ocfs2_find_xe_in_bucket() would skip.
*/
count = le16_to_cpu(xs->header->xh_count);
for (i = 0; i < count; i++) {
struct ocfs2_xattr_entry *xe = &xs->header->xh_entries[i];
if (ocfs2_xattr_get_type(xe) != xi->xi_name_index)
continue;
if (xe->xe_name_len != xi->xi_name_len)
continue;
if (!memcmp(xs->base + le16_to_cpu(xe->xe_name_offset),
xi->xi_name, xi->xi_name_len)) {
xs->here = xe;
return;
}
}
/*
* The entry did not survive the failed set, so this has become an
* insert. Point xs->here back inside the bucket: it still refers to
* the block being converted, which is about to be rewritten as a tree
* root.
*/
xs->not_found = -ENODATA;
xs->here = xs->header->xh_entries;
}
/*
@ -4773,6 +4821,7 @@ static void ocfs2_xattr_update_xattr_search(struct inode *inode,
* is allocated and the xattr block header is rewritten.
*/
static int ocfs2_xattr_create_index_block(struct inode *inode,
struct ocfs2_xattr_info *xi,
struct ocfs2_xattr_search *xs,
struct ocfs2_xattr_set_ctxt *ctxt)
{
@ -4834,7 +4883,7 @@ static int ocfs2_xattr_create_index_block(struct inode *inode,
ocfs2_cp_xattr_block_to_bucket(inode, xb_bh, xs->bucket);
ocfs2_xattr_bucket_journal_dirty(handle, xs->bucket);
ocfs2_xattr_update_xattr_search(inode, xs, xb_bh);
ocfs2_xattr_update_xattr_search(inode, xi, xs);
/* Change from ocfs2_xattr_header to ocfs2_xattr_tree_root */
memset(&xb->xb_attrs, 0, inode->i_sb->s_blocksize -
@ -5194,7 +5243,15 @@ static int ocfs2_divide_xattr_bucket(struct inode *inode,
start = ocfs2_xattr_find_divide_pos(xh);
if (start == count) {
xe = &xh->xh_entries[start-1];
/*
* start == count means every entry shares one hash, so the
* first of them carries it too. An empty bucket has no
* entries[start - 1], but its entries[0] still carries the
* bucket key: either the hash ocfs2_xa_remove_entry()
* deliberately leaves behind, or the sentinel a previous
* divide wrote.
*/
xe = &xh->xh_entries[0];
/*
* initialized a new empty bucket here.
@ -6027,11 +6084,20 @@ static int ocfs2_check_xattr_bucket_collision(struct ocfs2_xattr_bucket *bucket,
u32 name_hash)
{
struct ocfs2_xattr_header *xh = bucket_xh(bucket);
u16 count = le16_to_cpu(xh->xh_count);
/*
* An empty bucket keeps the hash of the entry that was removed in
* xh_entries[0] so that it can still be found, but it holds no
* entries to collide with.
*/
if (!count)
return 0;
if (name_hash != le32_to_cpu(xh->xh_entries[0].xe_name_hash))
return 0;
if (xh->xh_entries[le16_to_cpu(xh->xh_count) - 1].xe_name_hash ==
if (xh->xh_entries[count - 1].xe_name_hash ==
xh->xh_entries[0].xe_name_hash) {
mlog(ML_ERROR, "Too much hash collision in xattr bucket %llu, "
"hash = %u\n",
@ -6052,13 +6118,18 @@ static int ocfs2_xattr_set_entry_bucket(struct inode *inode,
struct ocfs2_xattr_search *xs,
struct ocfs2_xattr_set_ctxt *ctxt)
{
int ret;
int ret, found = 0;
u16 xe_index;
u32 name_hash;
struct ocfs2_xa_loc loc;
trace_ocfs2_xattr_set_entry_bucket(xi->xi_name);
ocfs2_init_xattr_bucket_xa_loc(&loc, xs->bucket,
xs->not_found ? NULL : xs->here);
/* Defragmenting below may move this entry, so remember its hash. */
name_hash = loc.xl_entry ? le32_to_cpu(loc.xl_entry->xe_name_hash) : 0;
ret = ocfs2_xa_set(&loc, xi, ctxt);
if (!ret) {
xs->here = loc.xl_entry;
@ -6077,6 +6148,29 @@ static int ocfs2_xattr_set_entry_bucket(struct inode *inode,
goto out;
}
/*
* ocfs2_defrag_xattr_bucket() sorts the entries by name hash and
* lib/sort.c is not stable, so entries sharing a hash may have traded
* slots. Look the entry up again before writing through it.
*/
if (loc.xl_entry) {
ret = ocfs2_find_xe_in_bucket(inode, xs->bucket,
xi->xi_name_index, xi->xi_name,
name_hash, &xe_index, &found);
if (ret) {
mlog_errno(ret);
goto out;
}
if (!found) {
mlog(ML_ERROR, "xattr entry not found in bucket %llu after defrag\n",
(unsigned long long)bucket_blkno(xs->bucket));
ret = -EFSCORRUPTED;
goto out;
}
loc.xl_entry = &bucket_xh(xs->bucket)->xh_entries[xe_index];
xs->here = loc.xl_entry;
}
ret = ocfs2_xa_set(&loc, xi, ctxt);
if (!ret) {
xs->here = loc.xl_entry;
@ -6100,6 +6194,22 @@ static int ocfs2_xattr_set_entry_index_block(struct inode *inode,
trace_ocfs2_xattr_set_entry_index_block(xi->xi_name);
/*
* An existing entry keeps the hash it was stored under, and that is
* the hash a split has to work with. A new entry is stored under the
* unsigned one, which is what ocfs2_xa_add_entry() will write.
*
* Read it before the set below. Filling the bucket may defragment it,
* and ocfs2_defrag_xattr_bucket() re-sorts the entry array, so
* afterwards xs->here only denotes whichever entry the sort left in
* that slot.
*/
if (xs->not_found)
name_hash = ocfs2_xattr_name_hash(inode, xi->xi_name,
xi->xi_name_len);
else
name_hash = le32_to_cpu(xs->here->xe_name_hash);
ret = ocfs2_xattr_set_entry_bucket(inode, xi, xs, ctxt);
if (!ret)
goto out;
@ -6115,17 +6225,7 @@ static int ocfs2_xattr_set_entry_index_block(struct inode *inode,
* the maximum number of collisions we will allow for then is
* one bucket's worth, so check it here whether we need to
* add a new bucket for the insert.
*
* An existing entry keeps the hash it was stored under, and that is
* the hash a split has to work with. A new entry is stored under the
* unsigned one, which is what ocfs2_xa_add_entry() will write.
*/
if (xs->not_found)
name_hash = ocfs2_xattr_name_hash(inode, xi->xi_name,
xi->xi_name_len);
else
name_hash = le32_to_cpu(xs->here->xe_name_hash);
ret = ocfs2_check_xattr_bucket_collision(xs->bucket, name_hash);
if (ret) {
mlog_errno(ret);

View File

@ -884,7 +884,7 @@ static __init int alloc_taint_buf(void)
size += 1; /* For NULL terminator */
buf = kmalloc(size, GFP_KERNEL);
buf = kzalloc(size, GFP_KERNEL);
if (!buf) {
panic("Failed to allocate taint string buffer");

View File

@ -52,11 +52,14 @@ static const char base64_tables[][65] = {
INIT_32(0x60, ch_62, ch_63), \
[0x80 ... 0xff] = -1 }
__diag_push();
__diag_ignore(clang, all, "-Wconstant-conversion", "https://github.com/llvm/llvm-project/issues/223923");
static const s8 base64_rev_maps[][256] = {
[BASE64_STD] = BASE64_REV_INIT('+', '/'),
[BASE64_URLSAFE] = BASE64_REV_INIT('-', '_'),
[BASE64_IMAP] = BASE64_REV_INIT('+', ',')
};
__diag_pop();
#undef BASE64_REV_INIT
#undef INIT_32