Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
49cad57
fuse: fine-grained request ftraces
bsbernd Apr 2, 2025
c8f3d45
fuse: {uring} Pin the user buffer
bsbernd Jan 8, 2025
8a20f0b
fuse: {io-uring] Avoid complete-in-task if pinned pages are used
bsbernd Jan 17, 2025
1cb8f2d
fuse: Use fuser-server provided read-ahead for CAP_SYS_ADMIN
bsbernd May 7, 2025
7c5216b
fuse: Increase the default max pages limit to 8182
bsbernd Apr 8, 2025
9e44162
fuse: add DLM_LOCK opcode
bsbernd Jun 20, 2025
ec05a1c
fuse: Renumber FUSE_DLM_WB_LOCK to 100
cding-ddn Jul 17, 2025
d25d13a
fuse: invalidate inode aliases when doing inode invalidation
yongzech Jul 8, 2025
440ccd8
fuse: Send DLM_WB_LOCK request in page_mkwrite handler
cding-ddn Jul 16, 2025
dbfa78c
fuse: Allow read_folio to retry page fault and read operations
cding-ddn Jul 16, 2025
a6dc0c6
fuse: flush pending fuse events before aborting the connection
Jul 17, 2025
1f531ed
fuse: Refactor io-uring bg queue flush and queue abort
bsbernd Jul 18, 2025
0744737
fuse: Flush the io-uring bg queue from fuse_uring_flush_bg
bsbernd Jul 18, 2025
8c810fa
fuse: fix unnecessary connection abort in dlm lock acquiring
hbirth Jul 21, 2025
1d4a8b3
fuse: fix connection abort on mmap when fuse server returns ENOSYS
hbirth Jul 21, 2025
3ce2031
fuse: change FUSE DLM_LOCK to request start and end of area
hbirth Aug 20, 2025
13eef80
fuse: fix memory leak in fuse-over-io-uring argument copies
cding-ddn Sep 24, 2025
cdd444e
fuse: {io-uring} Add queue length counters
bsbernd Jun 2, 2025
9f6de8b
fuse: {io-uring} Rename ring->nr_queues to max_nr_queues
bsbernd Jun 13, 2025
5a0f4bf
fuse: {io-uring} Use bitmaps to track registered queues
bsbernd Jun 10, 2025
a7f6de4
fuse: {io-uring} Allow reduced number of ring queues
bsbernd Jun 4, 2025
1fe1b00
fuse: {io-uring} Queue background requests on a different core
bsbernd Sep 24, 2025
489b160
fuse: Add retry attempts for numa local queues for load distribution
bsbernd Oct 24, 2025
7f65f76
fuse: Fetch a queued fuse request on command registration
bsbernd Nov 10, 2025
73c9855
fuse: add compound command to combine multiple requests
hbirth Sep 16, 2025
901deb5
RED-34640: Fix a startup teardown race
bsbernd Dec 12, 2025
9bb2806
fuse: Move ring queues_refs decrement
bsbernd Oct 20, 2025
c915d7f
fs/fuse: fix potential memory leak from fuse_uring_cancel
jianhuangli Oct 20, 2025
9b49db7
fuse: Fix missing numa_q_map free in dev_uring
bsbernd Nov 23, 2025
e32fd25
fuse: fix includes
hbirth Dec 19, 2025
1ae938a
Create workflow the create pr for redfs in each branch
openunix Sep 29, 2025
c4d8037
Fix the github actions PR trigger
openunix Dec 30, 2025
27baabd
Remove the pull_request_target from actions
openunix Dec 30, 2025
b226346
fuse: Make compounds a module option
bsbernd Jan 13, 2026
026345b
fuse: Fix the reduced queue assignment
bsbernd Feb 4, 2026
8e7e5b9
Fix the compiling error on aarch64
openunix Dec 26, 2025
ce5b16a
fuse: {io-uring} Prefer the current core over mapping
bsbernd Feb 11, 2026
2e4905a
fuse: enable large folios in inode initialization
hbirth Feb 23, 2026
0b7039a
fuse: Remove double define of 'enable_large_folios' module param
bsbernd Mar 6, 2026
9ebffb0
fuse: Remove unlock_request/lock_request
bsbernd Mar 9, 2026
09e3c7b
fuse: fix inode initialization race
hbirth Jan 16, 2026
f65445e
fuse: debug print requests when we hang in fuse_wait_aborted()
hbirth Mar 26, 2026
39d2998
fuse: fix io_uring connection abort leaving requests stuck
hbirth Apr 2, 2026
17fd845
fuse: invalidate page cache after sync and async direct writes
cding-ddn Mar 6, 2026
4f790a1
Invalidate selinux security label during inode invalidation.
kchen-ddn Apr 21, 2026
b321885
ubuntu: fix makefile compiler warning option to support LLVM
hbirth Apr 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions .github/workflows/create-redfs-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Automatially run copy-from-linux-branch.sh on branches and create PR for redfs.
name: Sync to redfs repo
on:
# Triggers the workflow on pull request merged.
pull_request:
branches: [ "redfs-*" ]
types: [ "closed" ]

jobs:
create-redfs-pr:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
# Checks-out to a different directory to avoid following checkout removing it.
- uses: actions/checkout@v4
with:
path: linux

- name: Try to checkout sync-${{ github.ref_name }} if it exists
uses: actions/checkout@v4
id: try-checkout
continue-on-error: true
with:
repository: DDNStorage/redfs
ref: sync-${{ github.ref_name }}
fetch-depth: 0
path: redfs
token: ${{ secrets.REDFS_TOKEN }}

- name: Fallback to checkout main
if: steps.try-checkout.outcome == 'failure'
uses: actions/checkout@v4
with:
repository: DDNStorage/redfs
ref: main
fetch-depth: 0
path: redfs
token: ${{ secrets.REDFS_TOKEN }}

- name: Initialize git
run: |
git config --global user.name "DDNStorage RED Workflow"
git config --global user.email "red@ddn.com"

- name: Create tracking branch based on main
if: steps.try-checkout.outcome == 'failure'
run: |
pushd redfs
git checkout -b sync-${{ github.ref_name }}
popd

- name: Generate PR for redfs
run: |
declare -A MAP
MAP["redfs-rhel9_4-427.42.1"]="5.14.0-427.42.1.el9_4"
MAP["redfs-rhel9_5-503.40.1"]="5.14.0-503.40.1.el9_5"
MAP["redfs-rhel9_6-570.12.1"]="5.14.0-570.12.1.el9_6"
MAP["redfs-ubuntu-noble-6.8.0-58.60"]="6.8.0-58.60.ubuntu"
kerver=${MAP["${{ github.ref_name }}"]}
if [ -z ${kerver} ]; then
echo "Cannot find target kernel version"
exit 1
fi
pushd redfs
./copy-from-linux-branch.sh $GITHUB_WORKSPACE/linux ${kerver}
git add src/$kerver
echo -e "Sync with ${{ github.repository }} branch ${{ github.ref_name }}\n" > ../commit.msg
echo -e "Sync with ${{ github.repository }} branch ${{ github.ref_name }} by commit" >> ../commit.msg
echo -e "${{ github.sha }}" >> ../commit.msg
RET=0
git commit -F ../commit.msg 2> ../commit.log || RET=$?;
if [ -s ../commit.log ]; then
echo "Error detcted in commit:"
cat ../commit.log
exit 1
elif [ $RET -eq 0 ]; then
echo "Done. Push the code to remote:"
git push origin sync-${{ github.ref_name }} 2> ../push.log ||:
else
echo "No changes to existed codes. Still try with PR."
fi
if [ -s ../push.log ]; then
echo "Message detected in push:"
cat ../push.log
fi
gh pr create --base main --fill || RET=$?
if [ $RET -eq 1 ]; then
echo "No pending changes for PR, returning $RET."
fi
popd
env:
GH_TOKEN: ${{ secrets.REDFS_TOKEN }}

Empty file.
2 changes: 1 addition & 1 deletion fs/fuse/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ obj-$(CONFIG_CUSE) += cuse.o
obj-$(CONFIG_VIRTIO_FS) += virtiofs.o

fuse-y := trace.o # put trace.o first so we see ftrace errors sooner
fuse-y += dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o ioctl.o
fuse-y += dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o ioctl.o fuse_dlm_cache.o compound.o
fuse-y += iomode.o
fuse-$(CONFIG_FUSE_DAX) += dax.o
fuse-$(CONFIG_FUSE_PASSTHROUGH) += passthrough.o backing.o
Expand Down
251 changes: 251 additions & 0 deletions fs/fuse/compound.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
// SPDX-License-Identifier: GPL-2.0
/*
* FUSE: Filesystem in Userspace
* Copyright (C) 2025
*
* This file implements compound operations for FUSE, allowing multiple
* operations to be batched into a single request to reduce round trips
* between kernel and userspace.
*/

#include "fuse_i.h"

/*
* Compound request builder and state tracker and args pointer storage
*/
struct fuse_compound_req {
struct fuse_mount *fm;
struct fuse_compound_in compound_header;
struct fuse_compound_out result_header;

/* Per-operation error codes */
int op_errors[FUSE_MAX_COMPOUND_OPS];
struct fuse_args *op_args[FUSE_MAX_COMPOUND_OPS];
};

struct fuse_compound_req *fuse_compound_alloc(struct fuse_mount *fm, u32 flags)
{
struct fuse_compound_req *compound;

compound = kzalloc(sizeof(*compound), GFP_KERNEL);
if (!compound)
return ERR_PTR(-ENOMEM);

compound->fm = fm;
compound->compound_header.flags = flags;

return compound;
}

int fuse_compound_add(struct fuse_compound_req *compound,
struct fuse_args *args)
{
if (!compound ||
compound->compound_header.count >= FUSE_MAX_COMPOUND_OPS)
return -EINVAL;

if (args->in_pages)
return -EINVAL;

compound->op_args[compound->compound_header.count] = args;
compound->compound_header.count++;
return 0;
}

static void *fuse_copy_response_per_req(struct fuse_args *args,
char *resp)
{
int i;
size_t copied = 0;

for (i = 0; i < args->out_numargs; i++) {
struct fuse_arg current_arg = args->out_args[i];
size_t arg_size = current_arg.size;

if (current_arg.value && arg_size > 0) {
memcpy(current_arg.value,
(char *)resp + copied, arg_size);
copied += arg_size;
}
}

return (char *)resp + copied;
}

int fuse_compound_get_error(struct fuse_compound_req *compound, int op_idx)
{
return compound->op_errors[op_idx];
}

static void *fuse_compound_parse_one_op(struct fuse_compound_req *compound,
int op_index, void *op_out_data,
void *response_end)
{
struct fuse_out_header *op_hdr = op_out_data;
struct fuse_args *args = compound->op_args[op_index];

if (op_hdr->len < sizeof(struct fuse_out_header))
return NULL;

/* Check if the entire operation response fits in the buffer */
if ((char *)op_out_data + op_hdr->len > (char *)response_end)
return NULL;

if (op_hdr->error != 0)
compound->op_errors[op_index] = op_hdr->error;

if (args && op_hdr->len > sizeof(struct fuse_out_header))
return fuse_copy_response_per_req(args, op_out_data +
sizeof(struct fuse_out_header));

/* No response data, just advance past the header */
return (char *)op_out_data + op_hdr->len;
}

static int fuse_compound_parse_resp(struct fuse_compound_req *compound,
u32 count, void *response,
size_t response_size)
{
void *op_out_data = response;
void *response_end = (char *)response + response_size;
int i;

if (!response || response_size < sizeof(struct fuse_out_header))
return -EIO;

for (i = 0; i < count && i < compound->result_header.count; i++) {
op_out_data = fuse_compound_parse_one_op(compound, i,
op_out_data,
response_end);
if (!op_out_data)
return -EIO;
}

return 0;
}

ssize_t fuse_compound_send(struct fuse_compound_req *compound)
{
struct fuse_args args = {
.opcode = FUSE_COMPOUND,
.nodeid = 0,
.in_numargs = 2,
.out_numargs = 2,
.out_argvar = true,
};
size_t resp_buffer_size;
size_t actual_response_size;
size_t buffer_pos;
size_t total_expected_out_size;
void *buffer = NULL;
void *resp_payload;
ssize_t ret;
int i;

if (!compound) {
pr_info_ratelimited("FUSE: compound request is NULL in %s\n",
__func__);
return -EINVAL;
}

if (compound->compound_header.count == 0) {
pr_info_ratelimited("FUSE: compound request contains no operations\n");
return -EINVAL;
}

buffer_pos = 0;
total_expected_out_size = 0;

for (i = 0; i < compound->compound_header.count; i++) {
struct fuse_args *op_args = compound->op_args[i];
size_t needed_size = sizeof(struct fuse_in_header);
int j;

for (j = 0; j < op_args->in_numargs; j++)
needed_size += op_args->in_args[j].size;

buffer_pos += needed_size;

for (j = 0; j < op_args->out_numargs; j++)
total_expected_out_size += op_args->out_args[j].size;
}

buffer = kvmalloc(buffer_pos, GFP_KERNEL);
if (!buffer)
return -ENOMEM;

buffer_pos = 0;
for (i = 0; i < compound->compound_header.count; i++) {
struct fuse_args *op_args = compound->op_args[i];
struct fuse_in_header *hdr;
size_t needed_size = sizeof(struct fuse_in_header);
int j;

for (j = 0; j < op_args->in_numargs; j++)
needed_size += op_args->in_args[j].size;

hdr = (struct fuse_in_header *)(buffer + buffer_pos);
memset(hdr, 0, sizeof(*hdr));
hdr->len = needed_size;
hdr->opcode = op_args->opcode;
hdr->nodeid = op_args->nodeid;
hdr->uid = from_kuid(compound->fm->fc->user_ns,
current_fsuid());
hdr->gid = from_kgid(compound->fm->fc->user_ns,
current_fsgid());
hdr->pid = pid_nr_ns(task_pid(current),
compound->fm->fc->pid_ns);
buffer_pos += sizeof(*hdr);

for (j = 0; j < op_args->in_numargs; j++) {
memcpy(buffer + buffer_pos, op_args->in_args[j].value,
op_args->in_args[j].size);
buffer_pos += op_args->in_args[j].size;
}
}

resp_buffer_size = total_expected_out_size +
(compound->compound_header.count *
sizeof(struct fuse_out_header));

resp_payload = kvmalloc(resp_buffer_size, GFP_KERNEL | __GFP_ZERO);
if (!resp_payload) {
ret = -ENOMEM;
goto out_free_buffer;
}

compound->compound_header.result_size = total_expected_out_size;

args.in_args[0].size = sizeof(compound->compound_header);
args.in_args[0].value = &compound->compound_header;
args.in_args[1].size = buffer_pos;
args.in_args[1].value = buffer;

args.out_args[0].size = sizeof(compound->result_header);
args.out_args[0].value = &compound->result_header;
args.out_args[1].size = resp_buffer_size;
args.out_args[1].value = resp_payload;

ret = fuse_simple_request(compound->fm, &args);
if (ret < 0)
goto out;

actual_response_size = args.out_args[1].size;

if (actual_response_size < sizeof(struct fuse_compound_out)) {
pr_info_ratelimited("FUSE: compound response too small (%zu bytes, minimum %zu bytes)\n",
actual_response_size,
sizeof(struct fuse_compound_out));
ret = -EINVAL;
goto out;
}

ret = fuse_compound_parse_resp(compound, compound->result_header.count,
(char *)resp_payload,
actual_response_size);
out:
kvfree(resp_payload);
out_free_buffer:
kvfree(buffer);
return ret;
}
Loading