mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-01-11 17:10:13 +00:00
Compare commits
29 Commits
8f0b4cce44
...
40fbbd64bb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40fbbd64bb | ||
|
|
53ec4a79ff | ||
|
|
115fada16b | ||
|
|
dbf89321bf | ||
|
|
6b63f90fa2 | ||
|
|
e1b4c6a583 | ||
|
|
bb27226f0d | ||
|
|
95d7a890e4 | ||
|
|
cafb57f7bd | ||
|
|
8dd2e58b62 | ||
|
|
0446356e9f | ||
|
|
d180b1d9c7 | ||
|
|
c1fb124f2a | ||
|
|
49ca214774 | ||
|
|
3010f06c52 | ||
|
|
579a3297b2 | ||
|
|
f5e1e5ec20 | ||
|
|
530b6637c7 | ||
|
|
37343524f0 | ||
|
|
b57f2ddd28 | ||
|
|
9f769637a9 | ||
|
|
313ef70a9f | ||
|
|
428e1b114c | ||
|
|
5630f7557d | ||
|
|
266273eaf4 | ||
|
|
564d59410c | ||
|
|
3309b63a22 | ||
|
|
12b5cd99a0 | ||
|
|
517a44d185 |
@ -2019,13 +2019,14 @@ out:
|
||||
else
|
||||
btrfs_delalloc_release_space(inode, data_reserved, page_start,
|
||||
reserved_space, true);
|
||||
extent_changeset_free(data_reserved);
|
||||
out_noreserve:
|
||||
if (only_release_metadata)
|
||||
btrfs_check_nocow_unlock(inode);
|
||||
|
||||
sb_end_pagefault(inode->vfs_inode.i_sb);
|
||||
|
||||
extent_changeset_free(data_reserved);
|
||||
|
||||
if (ret < 0)
|
||||
return vmf_error(ret);
|
||||
|
||||
|
||||
@ -256,6 +256,7 @@ static void print_data_reloc_error(const struct btrfs_inode *inode, u64 file_off
|
||||
if (ret < 0) {
|
||||
btrfs_err_rl(fs_info, "failed to lookup extent item for logical %llu: %d",
|
||||
logical, ret);
|
||||
btrfs_release_path(&path);
|
||||
return;
|
||||
}
|
||||
eb = path.nodes[0];
|
||||
|
||||
@ -1243,14 +1243,7 @@ out:
|
||||
btrfs_end_transaction(trans);
|
||||
else if (trans)
|
||||
ret = btrfs_end_transaction(trans);
|
||||
|
||||
/*
|
||||
* At this point we either failed at allocating prealloc, or we
|
||||
* succeeded and passed the ownership to it to add_qgroup_rb(). In any
|
||||
* case, this needs to be NULL or there is something wrong.
|
||||
*/
|
||||
ASSERT(prealloc == NULL);
|
||||
|
||||
kfree(prealloc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1682,12 +1675,7 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid)
|
||||
ret = btrfs_sysfs_add_one_qgroup(fs_info, qgroup);
|
||||
out:
|
||||
mutex_unlock(&fs_info->qgroup_ioctl_lock);
|
||||
/*
|
||||
* At this point we either failed at allocating prealloc, or we
|
||||
* succeeded and passed the ownership to it to add_qgroup_rb(). In any
|
||||
* case, this needs to be NULL or there is something wrong.
|
||||
*/
|
||||
ASSERT(prealloc == NULL);
|
||||
kfree(prealloc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -3279,7 +3267,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
|
||||
struct btrfs_root *quota_root;
|
||||
struct btrfs_qgroup *srcgroup;
|
||||
struct btrfs_qgroup *dstgroup;
|
||||
struct btrfs_qgroup *prealloc = NULL;
|
||||
struct btrfs_qgroup *prealloc;
|
||||
struct btrfs_qgroup_list **qlist_prealloc = NULL;
|
||||
bool free_inherit = false;
|
||||
bool need_rescan = false;
|
||||
@ -3520,14 +3508,7 @@ out:
|
||||
}
|
||||
if (free_inherit)
|
||||
kfree(inherit);
|
||||
|
||||
/*
|
||||
* At this point we either failed at allocating prealloc, or we
|
||||
* succeeded and passed the ownership to it to add_qgroup_rb(). In any
|
||||
* case, this needs to be NULL or there is something wrong.
|
||||
*/
|
||||
ASSERT(prealloc == NULL);
|
||||
|
||||
kfree(prealloc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@ -187,7 +187,6 @@ static int remove_extent_ref(struct btrfs_root *root, u64 bytenr,
|
||||
ret = btrfs_search_slot(&trans, root, &key, path, -1, 1);
|
||||
if (ret) {
|
||||
test_err("couldn't find backref %d", ret);
|
||||
btrfs_free_path(path);
|
||||
return ret;
|
||||
}
|
||||
btrfs_del_item(&trans, root, path);
|
||||
|
||||
@ -5865,14 +5865,6 @@ static int log_new_dir_dentries(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_inode *curr_inode = start_inode;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* If we are logging a new name, as part of a link or rename operation,
|
||||
* don't bother logging new dentries, as we just want to log the names
|
||||
* of an inode and that any new parents exist.
|
||||
*/
|
||||
if (ctx->logging_new_name)
|
||||
return 0;
|
||||
|
||||
path = btrfs_alloc_path();
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
@ -6051,6 +6043,33 @@ static int conflicting_inode_is_dir(struct btrfs_root *root, u64 ino,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool can_log_conflicting_inode(const struct btrfs_trans_handle *trans,
|
||||
const struct btrfs_inode *inode)
|
||||
{
|
||||
if (!S_ISDIR(inode->vfs_inode.i_mode))
|
||||
return true;
|
||||
|
||||
if (inode->last_unlink_trans < trans->transid)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* If this is a directory and its unlink_trans is not from a past
|
||||
* transaction then we must fallback to a transaction commit in order
|
||||
* to avoid getting a directory with 2 hard links after log replay.
|
||||
*
|
||||
* This happens if a directory A is renamed, moved from one parent
|
||||
* directory to another one, a new file is created in the old parent
|
||||
* directory with the old name of our directory A, the new file is
|
||||
* fsynced, then we moved the new file to some other parent directory
|
||||
* and fsync again the new file. This results in a log tree where we
|
||||
* logged that directory A existed, with the INODE_REF item for the
|
||||
* new location but without having logged its old parent inode, so
|
||||
* that on log replay we add a new link for the new location but the
|
||||
* old link remains, resulting in a link count of 2.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
static int add_conflicting_inode(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
@ -6154,6 +6173,11 @@ static int add_conflicting_inode(struct btrfs_trans_handle *trans,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!can_log_conflicting_inode(trans, inode)) {
|
||||
btrfs_add_delayed_iput(inode);
|
||||
return BTRFS_LOG_FORCE_COMMIT;
|
||||
}
|
||||
|
||||
btrfs_add_delayed_iput(inode);
|
||||
|
||||
ino_elem = kmalloc(sizeof(*ino_elem), GFP_NOFS);
|
||||
@ -6218,6 +6242,12 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
|
||||
break;
|
||||
}
|
||||
|
||||
if (!can_log_conflicting_inode(trans, inode)) {
|
||||
btrfs_add_delayed_iput(inode);
|
||||
ret = BTRFS_LOG_FORCE_COMMIT;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Always log the directory, we cannot make this
|
||||
* conditional on need_log_inode() because the directory
|
||||
|
||||
@ -7128,6 +7128,7 @@ static struct btrfs_fs_devices *open_seed_devices(struct btrfs_fs_info *fs_info,
|
||||
|
||||
fs_devices->seeding = true;
|
||||
fs_devices->opened = 1;
|
||||
list_add(&fs_devices->seed_list, &fs_info->fs_devices->seed_list);
|
||||
return fs_devices;
|
||||
}
|
||||
|
||||
|
||||
50
fs/libfs.c
50
fs/libfs.c
@ -346,22 +346,22 @@ void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry)
|
||||
* User space expects the directory offset value of the replaced
|
||||
* (new) directory entry to be unchanged after a rename.
|
||||
*
|
||||
* Returns zero on success, a negative errno value on failure.
|
||||
* Caller must have grabbed a slot for new_dentry in the maple_tree
|
||||
* associated with new_dir, even if dentry is negative.
|
||||
*/
|
||||
int simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry)
|
||||
void simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry)
|
||||
{
|
||||
struct offset_ctx *old_ctx = old_dir->i_op->get_offset_ctx(old_dir);
|
||||
struct offset_ctx *new_ctx = new_dir->i_op->get_offset_ctx(new_dir);
|
||||
long new_offset = dentry2offset(new_dentry);
|
||||
|
||||
simple_offset_remove(old_ctx, old_dentry);
|
||||
if (WARN_ON(!new_offset))
|
||||
return;
|
||||
|
||||
if (new_offset) {
|
||||
offset_set(new_dentry, 0);
|
||||
return simple_offset_replace(new_ctx, old_dentry, new_offset);
|
||||
}
|
||||
return simple_offset_add(new_ctx, old_dentry);
|
||||
simple_offset_remove(old_ctx, old_dentry);
|
||||
offset_set(new_dentry, 0);
|
||||
WARN_ON(simple_offset_replace(new_ctx, old_dentry, new_offset));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -388,31 +388,23 @@ int simple_offset_rename_exchange(struct inode *old_dir,
|
||||
long new_index = dentry2offset(new_dentry);
|
||||
int ret;
|
||||
|
||||
simple_offset_remove(old_ctx, old_dentry);
|
||||
simple_offset_remove(new_ctx, new_dentry);
|
||||
if (WARN_ON(!old_index || !new_index))
|
||||
return -EINVAL;
|
||||
|
||||
ret = simple_offset_replace(new_ctx, old_dentry, new_index);
|
||||
if (ret)
|
||||
goto out_restore;
|
||||
ret = mtree_store(&new_ctx->mt, new_index, old_dentry, GFP_KERNEL);
|
||||
if (WARN_ON(ret))
|
||||
return ret;
|
||||
|
||||
ret = simple_offset_replace(old_ctx, new_dentry, old_index);
|
||||
if (ret) {
|
||||
simple_offset_remove(new_ctx, old_dentry);
|
||||
goto out_restore;
|
||||
ret = mtree_store(&old_ctx->mt, old_index, new_dentry, GFP_KERNEL);
|
||||
if (WARN_ON(ret)) {
|
||||
mtree_store(&new_ctx->mt, new_index, new_dentry, GFP_KERNEL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = simple_rename_exchange(old_dir, old_dentry, new_dir, new_dentry);
|
||||
if (ret) {
|
||||
simple_offset_remove(new_ctx, old_dentry);
|
||||
simple_offset_remove(old_ctx, new_dentry);
|
||||
goto out_restore;
|
||||
}
|
||||
offset_set(old_dentry, new_index);
|
||||
offset_set(new_dentry, old_index);
|
||||
simple_rename_exchange(old_dir, old_dentry, new_dir, new_dentry);
|
||||
return 0;
|
||||
|
||||
out_restore:
|
||||
(void)simple_offset_replace(old_ctx, old_dentry, old_index);
|
||||
(void)simple_offset_replace(new_ctx, new_dentry, new_index);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -132,6 +132,14 @@ struct smbdirect_socket {
|
||||
|
||||
struct smbdirect_socket_parameters parameters;
|
||||
|
||||
/*
|
||||
* The state for connect/negotiation
|
||||
*/
|
||||
struct {
|
||||
spinlock_t lock;
|
||||
struct work_struct work;
|
||||
} connect;
|
||||
|
||||
/*
|
||||
* The state for keepalive and timeout handling
|
||||
*/
|
||||
@ -353,6 +361,10 @@ static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc)
|
||||
INIT_WORK(&sc->disconnect_work, __smbdirect_socket_disabled_work);
|
||||
disable_work_sync(&sc->disconnect_work);
|
||||
|
||||
spin_lock_init(&sc->connect.lock);
|
||||
INIT_WORK(&sc->connect.work, __smbdirect_socket_disabled_work);
|
||||
disable_work_sync(&sc->connect.work);
|
||||
|
||||
INIT_WORK(&sc->idle.immediate_work, __smbdirect_socket_disabled_work);
|
||||
disable_work_sync(&sc->idle.immediate_work);
|
||||
INIT_DELAYED_WORK(&sc->idle.timer_work, __smbdirect_socket_disabled_work);
|
||||
|
||||
@ -325,8 +325,10 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
|
||||
sess = ksmbd_session_lookup(conn, id);
|
||||
if (!sess && conn->binding)
|
||||
sess = ksmbd_session_lookup_slowpath(id);
|
||||
if (sess && sess->state != SMB2_SESSION_VALID)
|
||||
if (sess && sess->state != SMB2_SESSION_VALID) {
|
||||
ksmbd_user_session_put(sess);
|
||||
sess = NULL;
|
||||
}
|
||||
return sess;
|
||||
}
|
||||
|
||||
|
||||
@ -2363,7 +2363,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
|
||||
int rc = 0;
|
||||
unsigned int next = 0;
|
||||
|
||||
if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength +
|
||||
if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength + 1 +
|
||||
le16_to_cpu(eabuf->EaValueLength))
|
||||
return -EINVAL;
|
||||
|
||||
@ -2440,7 +2440,7 @@ next:
|
||||
break;
|
||||
}
|
||||
|
||||
if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength +
|
||||
if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength + 1 +
|
||||
le16_to_cpu(eabuf->EaValueLength)) {
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
|
||||
@ -1307,9 +1307,6 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
|
||||
granted |= le32_to_cpu(ace->access_req);
|
||||
ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size));
|
||||
}
|
||||
|
||||
if (!pdacl->num_aces)
|
||||
granted = GENERIC_ALL_FLAGS;
|
||||
}
|
||||
|
||||
if (!uid)
|
||||
|
||||
@ -242,6 +242,7 @@ static void smb_direct_disconnect_rdma_work(struct work_struct *work)
|
||||
* disable[_delayed]_work_sync()
|
||||
*/
|
||||
disable_work(&sc->disconnect_work);
|
||||
disable_work(&sc->connect.work);
|
||||
disable_work(&sc->recv_io.posted.refill_work);
|
||||
disable_delayed_work(&sc->idle.timer_work);
|
||||
disable_work(&sc->idle.immediate_work);
|
||||
@ -297,6 +298,7 @@ smb_direct_disconnect_rdma_connection(struct smbdirect_socket *sc)
|
||||
* not queued again but here we don't block and avoid
|
||||
* disable[_delayed]_work_sync()
|
||||
*/
|
||||
disable_work(&sc->connect.work);
|
||||
disable_work(&sc->recv_io.posted.refill_work);
|
||||
disable_work(&sc->idle.immediate_work);
|
||||
disable_delayed_work(&sc->idle.timer_work);
|
||||
@ -467,6 +469,7 @@ static void free_transport(struct smb_direct_transport *t)
|
||||
*/
|
||||
smb_direct_disconnect_wake_up_all(sc);
|
||||
|
||||
disable_work_sync(&sc->connect.work);
|
||||
disable_work_sync(&sc->recv_io.posted.refill_work);
|
||||
disable_delayed_work_sync(&sc->idle.timer_work);
|
||||
disable_work_sync(&sc->idle.immediate_work);
|
||||
@ -635,28 +638,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
|
||||
|
||||
switch (sc->recv_io.expected) {
|
||||
case SMBDIRECT_EXPECT_NEGOTIATE_REQ:
|
||||
if (wc->byte_len < sizeof(struct smbdirect_negotiate_req)) {
|
||||
put_recvmsg(sc, recvmsg);
|
||||
smb_direct_disconnect_rdma_connection(sc);
|
||||
return;
|
||||
}
|
||||
sc->recv_io.reassembly.full_packet_received = true;
|
||||
/*
|
||||
* Some drivers (at least mlx5_ib) might post a
|
||||
* recv completion before RDMA_CM_EVENT_ESTABLISHED,
|
||||
* we need to adjust our expectation in that case.
|
||||
*/
|
||||
if (!sc->first_error && sc->status == SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING)
|
||||
sc->status = SMBDIRECT_SOCKET_NEGOTIATE_NEEDED;
|
||||
if (SMBDIRECT_CHECK_STATUS_WARN(sc, SMBDIRECT_SOCKET_NEGOTIATE_NEEDED)) {
|
||||
put_recvmsg(sc, recvmsg);
|
||||
smb_direct_disconnect_rdma_connection(sc);
|
||||
return;
|
||||
}
|
||||
sc->status = SMBDIRECT_SOCKET_NEGOTIATE_RUNNING;
|
||||
enqueue_reassembly(sc, recvmsg, 0);
|
||||
wake_up(&sc->status_wait);
|
||||
return;
|
||||
/* see smb_direct_negotiate_recv_done */
|
||||
break;
|
||||
case SMBDIRECT_EXPECT_DATA_TRANSFER: {
|
||||
struct smbdirect_data_transfer *data_transfer =
|
||||
(struct smbdirect_data_transfer *)recvmsg->packet;
|
||||
@ -742,6 +725,126 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
|
||||
smb_direct_disconnect_rdma_connection(sc);
|
||||
}
|
||||
|
||||
static void smb_direct_negotiate_recv_work(struct work_struct *work);
|
||||
|
||||
static void smb_direct_negotiate_recv_done(struct ib_cq *cq, struct ib_wc *wc)
|
||||
{
|
||||
struct smbdirect_recv_io *recv_io =
|
||||
container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe);
|
||||
struct smbdirect_socket *sc = recv_io->socket;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* reset the common recv_done for later reuse.
|
||||
*/
|
||||
recv_io->cqe.done = recv_done;
|
||||
|
||||
if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) {
|
||||
put_recvmsg(sc, recv_io);
|
||||
if (wc->status != IB_WC_WR_FLUSH_ERR) {
|
||||
pr_err("Negotiate Recv error. status='%s (%d)' opcode=%d\n",
|
||||
ib_wc_status_msg(wc->status), wc->status,
|
||||
wc->opcode);
|
||||
smb_direct_disconnect_rdma_connection(sc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ksmbd_debug(RDMA, "Negotiate Recv completed. status='%s (%d)', opcode=%d\n",
|
||||
ib_wc_status_msg(wc->status), wc->status,
|
||||
wc->opcode);
|
||||
|
||||
ib_dma_sync_single_for_cpu(sc->ib.dev,
|
||||
recv_io->sge.addr,
|
||||
recv_io->sge.length,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
/*
|
||||
* This is an internal error!
|
||||
*/
|
||||
if (WARN_ON_ONCE(sc->recv_io.expected != SMBDIRECT_EXPECT_NEGOTIATE_REQ)) {
|
||||
put_recvmsg(sc, recv_io);
|
||||
smb_direct_disconnect_rdma_connection(sc);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't reset timer to the keepalive interval in
|
||||
* this will be done in smb_direct_negotiate_recv_work.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Only remember the recv_io if it has enough bytes,
|
||||
* this gives smb_direct_negotiate_recv_work enough
|
||||
* information in order to disconnect if it was not
|
||||
* valid.
|
||||
*/
|
||||
sc->recv_io.reassembly.full_packet_received = true;
|
||||
if (wc->byte_len >= sizeof(struct smbdirect_negotiate_req))
|
||||
enqueue_reassembly(sc, recv_io, 0);
|
||||
else
|
||||
put_recvmsg(sc, recv_io);
|
||||
|
||||
/*
|
||||
* Some drivers (at least mlx5_ib and irdma in roce mode)
|
||||
* might post a recv completion before RDMA_CM_EVENT_ESTABLISHED,
|
||||
* we need to adjust our expectation in that case.
|
||||
*
|
||||
* So we defer further processing of the negotiation
|
||||
* to smb_direct_negotiate_recv_work().
|
||||
*
|
||||
* If we are already in SMBDIRECT_SOCKET_NEGOTIATE_NEEDED
|
||||
* we queue the work directly otherwise
|
||||
* smb_direct_cm_handler() will do it, when
|
||||
* RDMA_CM_EVENT_ESTABLISHED arrived.
|
||||
*/
|
||||
spin_lock_irqsave(&sc->connect.lock, flags);
|
||||
if (!sc->first_error) {
|
||||
INIT_WORK(&sc->connect.work, smb_direct_negotiate_recv_work);
|
||||
if (sc->status == SMBDIRECT_SOCKET_NEGOTIATE_NEEDED)
|
||||
queue_work(sc->workqueue, &sc->connect.work);
|
||||
}
|
||||
spin_unlock_irqrestore(&sc->connect.lock, flags);
|
||||
}
|
||||
|
||||
static void smb_direct_negotiate_recv_work(struct work_struct *work)
|
||||
{
|
||||
struct smbdirect_socket *sc =
|
||||
container_of(work, struct smbdirect_socket, connect.work);
|
||||
const struct smbdirect_socket_parameters *sp = &sc->parameters;
|
||||
struct smbdirect_recv_io *recv_io;
|
||||
|
||||
if (sc->first_error)
|
||||
return;
|
||||
|
||||
ksmbd_debug(RDMA, "Negotiate Recv Work running\n");
|
||||
|
||||
/*
|
||||
* Reset timer to the keepalive interval in
|
||||
* order to trigger our next keepalive message.
|
||||
*/
|
||||
sc->idle.keepalive = SMBDIRECT_KEEPALIVE_NONE;
|
||||
mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
|
||||
msecs_to_jiffies(sp->keepalive_interval_msec));
|
||||
|
||||
/*
|
||||
* If smb_direct_negotiate_recv_done() detected an
|
||||
* invalid request we want to disconnect.
|
||||
*/
|
||||
recv_io = get_first_reassembly(sc);
|
||||
if (!recv_io) {
|
||||
smb_direct_disconnect_rdma_connection(sc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (SMBDIRECT_CHECK_STATUS_WARN(sc, SMBDIRECT_SOCKET_NEGOTIATE_NEEDED)) {
|
||||
smb_direct_disconnect_rdma_connection(sc);
|
||||
return;
|
||||
}
|
||||
sc->status = SMBDIRECT_SOCKET_NEGOTIATE_RUNNING;
|
||||
wake_up(&sc->status_wait);
|
||||
}
|
||||
|
||||
static int smb_direct_post_recv(struct smbdirect_socket *sc,
|
||||
struct smbdirect_recv_io *recvmsg)
|
||||
{
|
||||
@ -758,7 +861,6 @@ static int smb_direct_post_recv(struct smbdirect_socket *sc,
|
||||
return ret;
|
||||
recvmsg->sge.length = sp->max_recv_size;
|
||||
recvmsg->sge.lkey = sc->ib.pd->local_dma_lkey;
|
||||
recvmsg->cqe.done = recv_done;
|
||||
|
||||
wr.wr_cqe = &recvmsg->cqe;
|
||||
wr.next = NULL;
|
||||
@ -1732,6 +1834,7 @@ static int smb_direct_cm_handler(struct rdma_cm_id *cm_id,
|
||||
struct rdma_cm_event *event)
|
||||
{
|
||||
struct smbdirect_socket *sc = cm_id->context;
|
||||
unsigned long flags;
|
||||
|
||||
ksmbd_debug(RDMA, "RDMA CM event. cm_id=%p event=%s (%d)\n",
|
||||
cm_id, rdma_event_msg(event->event), event->event);
|
||||
@ -1739,18 +1842,27 @@ static int smb_direct_cm_handler(struct rdma_cm_id *cm_id,
|
||||
switch (event->event) {
|
||||
case RDMA_CM_EVENT_ESTABLISHED: {
|
||||
/*
|
||||
* Some drivers (at least mlx5_ib) might post a
|
||||
* recv completion before RDMA_CM_EVENT_ESTABLISHED,
|
||||
* Some drivers (at least mlx5_ib and irdma in roce mode)
|
||||
* might post a recv completion before RDMA_CM_EVENT_ESTABLISHED,
|
||||
* we need to adjust our expectation in that case.
|
||||
*
|
||||
* As we already started the negotiation, we just
|
||||
* ignore RDMA_CM_EVENT_ESTABLISHED here.
|
||||
* If smb_direct_negotiate_recv_done was called first
|
||||
* it initialized sc->connect.work only for us to
|
||||
* start, so that we turned into
|
||||
* SMBDIRECT_SOCKET_NEGOTIATE_NEEDED, before
|
||||
* smb_direct_negotiate_recv_work() runs.
|
||||
*
|
||||
* If smb_direct_negotiate_recv_done didn't happen
|
||||
* yet. sc->connect.work is still be disabled and
|
||||
* queue_work() is a no-op.
|
||||
*/
|
||||
if (!sc->first_error && sc->status > SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING)
|
||||
break;
|
||||
if (SMBDIRECT_CHECK_STATUS_DISCONNECT(sc, SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING))
|
||||
break;
|
||||
sc->status = SMBDIRECT_SOCKET_NEGOTIATE_NEEDED;
|
||||
spin_lock_irqsave(&sc->connect.lock, flags);
|
||||
if (!sc->first_error)
|
||||
queue_work(sc->workqueue, &sc->connect.work);
|
||||
spin_unlock_irqrestore(&sc->connect.lock, flags);
|
||||
wake_up(&sc->status_wait);
|
||||
break;
|
||||
}
|
||||
@ -1921,6 +2033,7 @@ static int smb_direct_prepare_negotiation(struct smbdirect_socket *sc)
|
||||
recvmsg = get_free_recvmsg(sc);
|
||||
if (!recvmsg)
|
||||
return -ENOMEM;
|
||||
recvmsg->cqe.done = smb_direct_negotiate_recv_done;
|
||||
|
||||
ret = smb_direct_post_recv(sc, recvmsg);
|
||||
if (ret) {
|
||||
@ -2339,6 +2452,7 @@ respond:
|
||||
|
||||
static int smb_direct_connect(struct smbdirect_socket *sc)
|
||||
{
|
||||
struct smbdirect_recv_io *recv_io;
|
||||
int ret;
|
||||
|
||||
ret = smb_direct_init_params(sc);
|
||||
@ -2353,6 +2467,9 @@ static int smb_direct_connect(struct smbdirect_socket *sc)
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_for_each_entry(recv_io, &sc->recv_io.free.list, list)
|
||||
recv_io->cqe.done = recv_done;
|
||||
|
||||
ret = smb_direct_create_qpair(sc);
|
||||
if (ret) {
|
||||
pr_err("Can't accept RDMA client: %d\n", ret);
|
||||
|
||||
@ -702,7 +702,7 @@ retry:
|
||||
rd.old_parent = NULL;
|
||||
rd.new_parent = new_path.dentry;
|
||||
rd.flags = flags;
|
||||
rd.delegated_inode = NULL,
|
||||
rd.delegated_inode = NULL;
|
||||
err = start_renaming_dentry(&rd, lookup_flags, old_child, &new_last);
|
||||
if (err)
|
||||
goto out_drop_write;
|
||||
|
||||
@ -3247,7 +3247,7 @@ struct offset_ctx {
|
||||
void simple_offset_init(struct offset_ctx *octx);
|
||||
int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry);
|
||||
void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry);
|
||||
int simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
void simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry);
|
||||
int simple_offset_rename_exchange(struct inode *old_dir,
|
||||
struct dentry *old_dentry,
|
||||
|
||||
@ -71,7 +71,6 @@ __bpf_kfunc void css_rstat_updated(struct cgroup_subsys_state *css, int cpu)
|
||||
{
|
||||
struct llist_head *lhead;
|
||||
struct css_rstat_cpu *rstatc;
|
||||
struct css_rstat_cpu __percpu *rstatc_pcpu;
|
||||
struct llist_node *self;
|
||||
|
||||
/*
|
||||
@ -104,18 +103,22 @@ __bpf_kfunc void css_rstat_updated(struct cgroup_subsys_state *css, int cpu)
|
||||
/*
|
||||
* This function can be renentered by irqs and nmis for the same cgroup
|
||||
* and may try to insert the same per-cpu lnode into the llist. Note
|
||||
* that llist_add() does not protect against such scenarios.
|
||||
* that llist_add() does not protect against such scenarios. In addition
|
||||
* this same per-cpu lnode can be modified through init_llist_node()
|
||||
* from css_rstat_flush() running on a different CPU.
|
||||
*
|
||||
* To protect against such stacked contexts of irqs/nmis, we use the
|
||||
* fact that lnode points to itself when not on a list and then use
|
||||
* this_cpu_cmpxchg() to atomically set to NULL to select the winner
|
||||
* try_cmpxchg() to atomically set to NULL to select the winner
|
||||
* which will call llist_add(). The losers can assume the insertion is
|
||||
* successful and the winner will eventually add the per-cpu lnode to
|
||||
* the llist.
|
||||
*
|
||||
* Please note that we can not use this_cpu_cmpxchg() here as on some
|
||||
* archs it is not safe against modifications from multiple CPUs.
|
||||
*/
|
||||
self = &rstatc->lnode;
|
||||
rstatc_pcpu = css->rstat_cpu;
|
||||
if (this_cpu_cmpxchg(rstatc_pcpu->lnode.next, self, NULL) != self)
|
||||
if (!try_cmpxchg(&rstatc->lnode.next, &self, NULL))
|
||||
return;
|
||||
|
||||
lhead = ss_lhead_cpu(css->ss, cpu);
|
||||
|
||||
@ -41,6 +41,13 @@ static bool scx_init_task_enabled;
|
||||
static bool scx_switching_all;
|
||||
DEFINE_STATIC_KEY_FALSE(__scx_switched_all);
|
||||
|
||||
/*
|
||||
* Tracks whether scx_enable() called scx_bypass(true). Used to balance bypass
|
||||
* depth on enable failure. Will be removed when bypass depth is moved into the
|
||||
* sched instance.
|
||||
*/
|
||||
static bool scx_bypassed_for_enable;
|
||||
|
||||
static atomic_long_t scx_nr_rejected = ATOMIC_LONG_INIT(0);
|
||||
static atomic_long_t scx_hotplug_seq = ATOMIC_LONG_INIT(0);
|
||||
|
||||
@ -975,6 +982,30 @@ static void refill_task_slice_dfl(struct scx_sched *sch, struct task_struct *p)
|
||||
__scx_add_event(sch, SCX_EV_REFILL_SLICE_DFL, 1);
|
||||
}
|
||||
|
||||
static void local_dsq_post_enq(struct scx_dispatch_q *dsq, struct task_struct *p,
|
||||
u64 enq_flags)
|
||||
{
|
||||
struct rq *rq = container_of(dsq, struct rq, scx.local_dsq);
|
||||
bool preempt = false;
|
||||
|
||||
/*
|
||||
* If @rq is in balance, the CPU is already vacant and looking for the
|
||||
* next task to run. No need to preempt or trigger resched after moving
|
||||
* @p into its local DSQ.
|
||||
*/
|
||||
if (rq->scx.flags & SCX_RQ_IN_BALANCE)
|
||||
return;
|
||||
|
||||
if ((enq_flags & SCX_ENQ_PREEMPT) && p != rq->curr &&
|
||||
rq->curr->sched_class == &ext_sched_class) {
|
||||
rq->curr->scx.slice = 0;
|
||||
preempt = true;
|
||||
}
|
||||
|
||||
if (preempt || sched_class_above(&ext_sched_class, rq->curr->sched_class))
|
||||
resched_curr(rq);
|
||||
}
|
||||
|
||||
static void dispatch_enqueue(struct scx_sched *sch, struct scx_dispatch_q *dsq,
|
||||
struct task_struct *p, u64 enq_flags)
|
||||
{
|
||||
@ -1086,22 +1117,10 @@ static void dispatch_enqueue(struct scx_sched *sch, struct scx_dispatch_q *dsq,
|
||||
if (enq_flags & SCX_ENQ_CLEAR_OPSS)
|
||||
atomic_long_set_release(&p->scx.ops_state, SCX_OPSS_NONE);
|
||||
|
||||
if (is_local) {
|
||||
struct rq *rq = container_of(dsq, struct rq, scx.local_dsq);
|
||||
bool preempt = false;
|
||||
|
||||
if ((enq_flags & SCX_ENQ_PREEMPT) && p != rq->curr &&
|
||||
rq->curr->sched_class == &ext_sched_class) {
|
||||
rq->curr->scx.slice = 0;
|
||||
preempt = true;
|
||||
}
|
||||
|
||||
if (preempt || sched_class_above(&ext_sched_class,
|
||||
rq->curr->sched_class))
|
||||
resched_curr(rq);
|
||||
} else {
|
||||
if (is_local)
|
||||
local_dsq_post_enq(dsq, p, enq_flags);
|
||||
else
|
||||
raw_spin_unlock(&dsq->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void task_unlink_from_dsq(struct task_struct *p,
|
||||
@ -1625,6 +1644,8 @@ static void move_local_task_to_local_dsq(struct task_struct *p, u64 enq_flags,
|
||||
|
||||
dsq_mod_nr(dst_dsq, 1);
|
||||
p->scx.dsq = dst_dsq;
|
||||
|
||||
local_dsq_post_enq(dst_dsq, p, enq_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2402,7 +2423,7 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p,
|
||||
* ops.enqueue() that @p is the only one available for this cpu,
|
||||
* which should trigger an explicit follow-up scheduling event.
|
||||
*/
|
||||
if (sched_class_above(&ext_sched_class, next->sched_class)) {
|
||||
if (next && sched_class_above(&ext_sched_class, next->sched_class)) {
|
||||
WARN_ON_ONCE(!(sch->ops.flags & SCX_OPS_ENQ_LAST));
|
||||
do_enqueue_task(rq, p, SCX_ENQ_LAST, -1);
|
||||
} else {
|
||||
@ -2425,7 +2446,7 @@ static struct task_struct *
|
||||
do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)
|
||||
{
|
||||
struct task_struct *prev = rq->curr;
|
||||
bool keep_prev, kick_idle = false;
|
||||
bool keep_prev;
|
||||
struct task_struct *p;
|
||||
|
||||
/* see kick_cpus_irq_workfn() */
|
||||
@ -2467,12 +2488,8 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)
|
||||
refill_task_slice_dfl(rcu_dereference_sched(scx_root), p);
|
||||
} else {
|
||||
p = first_local_task(rq);
|
||||
if (!p) {
|
||||
if (kick_idle)
|
||||
scx_kick_cpu(rcu_dereference_sched(scx_root),
|
||||
cpu_of(rq), SCX_KICK_IDLE);
|
||||
if (!p)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (unlikely(!p->scx.slice)) {
|
||||
struct scx_sched *sch = rcu_dereference_sched(scx_root);
|
||||
@ -3575,7 +3592,7 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
|
||||
int node;
|
||||
|
||||
irq_work_sync(&sch->error_irq_work);
|
||||
kthread_stop(sch->helper->task);
|
||||
kthread_destroy_worker(sch->helper);
|
||||
|
||||
free_percpu(sch->pcpu);
|
||||
|
||||
@ -4318,6 +4335,11 @@ static void scx_disable_workfn(struct kthread_work *work)
|
||||
scx_dsp_max_batch = 0;
|
||||
free_kick_syncs();
|
||||
|
||||
if (scx_bypassed_for_enable) {
|
||||
scx_bypassed_for_enable = false;
|
||||
scx_bypass(false);
|
||||
}
|
||||
|
||||
mutex_unlock(&scx_enable_mutex);
|
||||
|
||||
WARN_ON_ONCE(scx_set_enable_state(SCX_DISABLED) != SCX_DISABLING);
|
||||
@ -4786,7 +4808,7 @@ static struct scx_sched *scx_alloc_and_add_sched(struct sched_ext_ops *ops)
|
||||
return sch;
|
||||
|
||||
err_stop_helper:
|
||||
kthread_stop(sch->helper->task);
|
||||
kthread_destroy_worker(sch->helper);
|
||||
err_free_pcpu:
|
||||
free_percpu(sch->pcpu);
|
||||
err_free_gdsqs:
|
||||
@ -4970,6 +4992,7 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)
|
||||
* Init in bypass mode to guarantee forward progress.
|
||||
*/
|
||||
scx_bypass(true);
|
||||
scx_bypassed_for_enable = true;
|
||||
|
||||
for (i = SCX_OPI_NORMAL_BEGIN; i < SCX_OPI_NORMAL_END; i++)
|
||||
if (((void (**)(void))ops)[i])
|
||||
@ -5067,6 +5090,7 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)
|
||||
scx_task_iter_stop(&sti);
|
||||
percpu_up_write(&scx_fork_rwsem);
|
||||
|
||||
scx_bypassed_for_enable = false;
|
||||
scx_bypass(false);
|
||||
|
||||
if (!scx_tryset_enable_state(SCX_ENABLED, SCX_ENABLING)) {
|
||||
|
||||
38
mm/shmem.c
38
mm/shmem.c
@ -4019,22 +4019,10 @@ static int shmem_whiteout(struct mnt_idmap *idmap,
|
||||
whiteout = d_alloc(old_dentry->d_parent, &old_dentry->d_name);
|
||||
if (!whiteout)
|
||||
return -ENOMEM;
|
||||
|
||||
error = shmem_mknod(idmap, old_dir, whiteout,
|
||||
S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV);
|
||||
dput(whiteout);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* Cheat and hash the whiteout while the old dentry is still in
|
||||
* place, instead of playing games with FS_RENAME_DOES_D_MOVE.
|
||||
*
|
||||
* d_lookup() will consistently find one of them at this point,
|
||||
* not sure which one, but that isn't even important.
|
||||
*/
|
||||
d_rehash(whiteout);
|
||||
return 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4050,6 +4038,7 @@ static int shmem_rename2(struct mnt_idmap *idmap,
|
||||
{
|
||||
struct inode *inode = d_inode(old_dentry);
|
||||
int they_are_dirs = S_ISDIR(inode->i_mode);
|
||||
bool had_offset = false;
|
||||
int error;
|
||||
|
||||
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
|
||||
@ -4062,16 +4051,23 @@ static int shmem_rename2(struct mnt_idmap *idmap,
|
||||
if (!simple_empty(new_dentry))
|
||||
return -ENOTEMPTY;
|
||||
|
||||
if (flags & RENAME_WHITEOUT) {
|
||||
error = shmem_whiteout(idmap, old_dir, old_dentry);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
error = simple_offset_rename(old_dir, old_dentry, new_dir, new_dentry);
|
||||
if (error)
|
||||
error = simple_offset_add(shmem_get_offset_ctx(new_dir), new_dentry);
|
||||
if (error == -EBUSY)
|
||||
had_offset = true;
|
||||
else if (unlikely(error))
|
||||
return error;
|
||||
|
||||
if (flags & RENAME_WHITEOUT) {
|
||||
error = shmem_whiteout(idmap, old_dir, old_dentry);
|
||||
if (error) {
|
||||
if (!had_offset)
|
||||
simple_offset_remove(shmem_get_offset_ctx(new_dir),
|
||||
new_dentry);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
simple_offset_rename(old_dir, old_dentry, new_dir, new_dentry);
|
||||
if (d_really_is_positive(new_dentry)) {
|
||||
(void) shmem_unlink(new_dir, new_dentry);
|
||||
if (they_are_dirs) {
|
||||
|
||||
@ -46,6 +46,14 @@ static void print_test_preamble(const struct scx_test *test, bool quiet)
|
||||
if (!quiet)
|
||||
printf("DESCRIPTION: %s\n", test->description);
|
||||
printf("OUTPUT:\n");
|
||||
|
||||
/*
|
||||
* The tests may fork with the preamble buffered
|
||||
* in the children's stdout. Flush before the test
|
||||
* to avoid printing the message multiple times.
|
||||
*/
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
static const char *status_to_result(enum scx_test_status status)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user