mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
commit
46a062865b
4
.mailmap
4
.mailmap
|
|
@ -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>
|
||||
|
|
|
|||
14
MAINTAINERS
14
MAINTAINERS
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
10
fs/fat/dir.c
10
fs/fat/dir.c
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)(®->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)(®->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)(®->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)(®->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(®->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(®->hr_dev_mutex);
|
||||
ret = wait_event_interruptible(o2hb_steady_queue,
|
||||
atomic_read(®->hr_steady_iterations) == 0 ||
|
||||
reg->hr_node_deleted);
|
||||
mutex_lock(®->hr_dev_mutex);
|
||||
if (ret) {
|
||||
atomic_set(®->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(®->hr_dev_mutex);
|
||||
ret = __o2hb_region_dev_store(reg, page, count);
|
||||
mutex_unlock(®->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(®->hr_all_item);
|
||||
mutex_init(®->hr_arming_mutex);
|
||||
mutex_init(®->hr_dev_mutex);
|
||||
reg->hr_stopping = true;
|
||||
INIT_DELAYED_WORK(®->hr_write_timeout_work, o2hb_write_timeout);
|
||||
INIT_DELAYED_WORK(®->hr_nego_timeout_work, o2hb_nego_timeout);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = ⦥
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
146
fs/ocfs2/xattr.c
146
fs/ocfs2/xattr.c
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user