Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
0b91d06
[RFC] fuse: Set request unique on allocation
bsbernd Apr 2, 2025
dbdeb21
fuse: {io-uring} Avoid _send code dup
bsbernd Apr 2, 2025
e3a1dbc
fuse: fine-grained request ftraces
bsbernd Apr 2, 2025
3bfca20
fuse: {uring} Pin the user buffer
bsbernd Jan 8, 2025
c211946
fuse: {io-uring] Avoid complete-in-task if pinned pages are used
bsbernd Jan 17, 2025
358d674
fuse: Use fuser-server provided read-ahead for CAP_SYS_ADMIN
bsbernd May 7, 2025
eb4e2d4
fuse: Increase the default max pages limit to 8182
bsbernd Apr 8, 2025
d4a733c
fuse: add DLM_LOCK opcode
bsbernd Jun 20, 2025
a2e7c1b
fuse: invalidate inode aliases when doing inode invalidation
yongzech Jul 8, 2025
f598715
fuse: Renumber FUSE_DLM_WB_LOCK to 100
cding-ddn Jul 17, 2025
c779a86
fuse: Send DLM_WB_LOCK request in page_mkwrite handler
cding-ddn Jul 16, 2025
1954b0e
fuse: Allow read_folio to retry page fault and read operations
cding-ddn Jul 16, 2025
eaab38b
fuse: flush pending fuse events before aborting the connection
Jul 17, 2025
feb65a0
fuse: Refactor io-uring bg queue flush and queue abort
bsbernd Jul 18, 2025
b48d099
fuse: Flush the io-uring bg queue from fuse_uring_flush_bg
bsbernd Jul 18, 2025
d033650
fuse: fix unnecessary connection abort in dlm lock acquiring
hbirth Jul 21, 2025
f2c85b3
fuse: fix connection abort on mmap when fuse server returns ENOSYS
hbirth Jul 21, 2025
751660f
fuse: make foffset alignment opt-in for optimum backend performance
lostjeffle Jul 5, 2024
e41c4d9
fuse: change FUSE DLM_LOCK to request start and end of area
hbirth Aug 20, 2025
cf94d3c
fuse: fix memory leak in fuse-over-io-uring argument copies
cding-ddn Sep 24, 2025
005f3dd
fuse: {io-uring} Add queue length counters
bsbernd Jun 2, 2025
0a2127d
fuse: {io-uring} Rename ring->nr_queues to max_nr_queues
bsbernd Jun 13, 2025
3766d58
fuse: {io-uring} Use bitmaps to track registered queues
bsbernd Jun 10, 2025
2b1fec0
fuse: {io-uring} Allow reduced number of ring queues
bsbernd Jun 4, 2025
da27244
fuse: {io-uring} Queue background requests on a different core
bsbernd Sep 24, 2025
e56d798
fuse: Add retry attempts for numa local queues for load distribution
bsbernd Oct 24, 2025
592d083
fuse: Invalidate the page cache after FOPEN_DIRECT_IO write
bsbernd Oct 22, 2025
0dc14f0
fuse: Always flush the page cache before FOPEN_DIRECT_IO write
bsbernd Oct 22, 2025
7e7ef51
fuse: Fetch a queued fuse request on command registration
bsbernd Nov 10, 2025
c36239b
fuse: add compound command to combine multiple requests
hbirth Sep 16, 2025
35caf14
fuse: simplify compound commands
hbirth Jan 8, 2026
d132097
RED-34640: Fix a startup teardown race
bsbernd Dec 12, 2025
23e29f8
fuse: Move ring queues_refs decrement
bsbernd Oct 20, 2025
8451259
fs/fuse: fix potential memory leak from fuse_uring_cancel
jianhuangli Oct 20, 2025
4defc52
fuse: fix io-uring list corruption for terminated non-committed requests
joannekoong Nov 25, 2025
ae1ce13
fuse: Fix missing numa_q_map free in dev_uring
bsbernd Nov 23, 2025
a8f31aa
fuse: allow synchronous FUSE_INIT
Aug 22, 2025
cbd2732
fuse: compounds fix includes
hbirth Dec 19, 2025
bf04ff5
Create workflow the create pr for redfs in each branch
openunix Sep 29, 2025
99d2382
Fix the github actions PR trigger
openunix Dec 30, 2025
f6c808e
Remove the pull_request_target from actions
openunix Dec 30, 2025
abffbbe
fuse: Make compounds a module option
bsbernd Jan 13, 2026
4ff6d60
fuse: Fix the reduced queue assignment
bsbernd Feb 4, 2026
9bf3ed0
Fix the compiling error on aarch64
openunix Dec 26, 2025
d9dcafe
fuse: {io-uring} Prefer the current core over mapping
bsbernd Feb 11, 2026
43ca84c
fuse: invalidate the page cache after direct write
lostjeffle Jan 11, 2026
69ecae4
fuse: enable large folios in inode initialization
hbirth Feb 23, 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 @@ -10,7 +10,7 @@ obj-$(CONFIG_FUSE_FS) += fuse.o
obj-$(CONFIG_CUSE) += cuse.o
obj-$(CONFIG_VIRTIO_FS) += virtiofs.o

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
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;
}
3 changes: 2 additions & 1 deletion fs/fuse/cuse.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#include <linux/user_namespace.h>

#include "fuse_i.h"
#include "fuse_dev_i.h"

#define CUSE_CONNTBL_LEN 64

Expand Down Expand Up @@ -547,7 +548,7 @@ static int cuse_channel_open(struct inode *inode, struct file *file)
*/
static int cuse_channel_release(struct inode *inode, struct file *file)
{
struct fuse_dev *fud = file->private_data;
struct fuse_dev *fud = __fuse_get_dev(file);
struct cuse_conn *cc = fc_to_cc(fud->fc);

/* remove from the conntbl, no more access from this point on */
Expand Down
Loading