Skip to content

Commit c9fb7b4

Browse files
committed
Improve concurrent sequential access speed by per-file file pointer.
This commit extends the caching of cluster information introduced by commit 6c332e1 so that multiple open file handles accessing the same node each have their own cache. That prevents dramatic slowdowns when multiple processes are reading from a large file. * Replace the fptr_index and fptr_cluster members of struct exfat_node introduced by 6c332e1 with a list of exfat_fptr structs, each of which holds one index and cluster. * In fuse_exfat_open, add a new exfat_fptr to the exfat_node, and store a pointer to both in the fh member of the fuse_file_info. * In fuse_exfat_read and fuse_exfat_write, pass the exfat_fptr from the fh through to exfat_advance_cluster, so that multiple file handles open on the same file can independently track their most recently used cluster. For all other uses of exfat_advance_cluster, we continue to user the "shared" exfat_fptr stored in the node. * Adjust grow_file and shrink_file to detect when multiple exfat_fptr instances are stored in the node and update them all as needed.
1 parent 11e4f03 commit c9fb7b4

File tree

7 files changed

+121
-40
lines changed

7 files changed

+121
-40
lines changed

fuse/main.c

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,69 @@
4444

4545
struct exfat ef;
4646

47-
static struct exfat_node* get_node(const struct fuse_file_info* fi)
47+
struct exfat_fh
4848
{
49-
return (struct exfat_node*) (size_t) fi->fh;
49+
struct exfat_node *node;
50+
struct exfat_fptr fptr;
51+
};
52+
53+
static struct exfat_fh *get_fh(const struct fuse_file_info *fi) {
54+
return (struct exfat_fh *)(size_t)fi->fh;
5055
}
5156

52-
static void set_node(struct fuse_file_info* fi, struct exfat_node* node)
53-
{
54-
fi->fh = (uint64_t) (size_t) node;
57+
static struct exfat_node *get_node(const struct fuse_file_info *fi) {
58+
return get_fh(fi)->node;
59+
}
60+
61+
static struct exfat_fptr *get_fptr(const struct fuse_file_info *fi) {
62+
return &get_fh(fi)->fptr;
63+
}
64+
65+
static int add_fh(struct fuse_file_info *fi, struct exfat_node *node) {
66+
/* allocate and init new file handle */
67+
struct exfat_fh *fh = malloc(sizeof(struct exfat_fh));
68+
if (fh == NULL) {
69+
exfat_error("failed to allocate file handle");
70+
return -ENOMEM;
71+
}
72+
memset(fh, 0, sizeof(struct exfat_fh));
73+
fh->node = node;
74+
fh->fptr.cluster = node->start_cluster;
75+
76+
/* add the file handle's fptr to the node */
77+
struct exfat_fptr *p = &node->fptr;
78+
int count = 1;
79+
while(p->next != NULL) {
80+
p = p->next;
81+
++count;
82+
}
83+
p->next = &fh->fptr;
84+
exfat_debug("added file handle %d to node", count);
85+
86+
/* hand the file handle to fuse */
87+
fi->fh = (uint64_t)(size_t)fh;
5588
fi->keep_cache = 1;
89+
return 0;
90+
}
91+
92+
static void remove_fh(struct fuse_file_info *fi) {
93+
struct exfat_fh *fh = get_fh(fi);
94+
95+
struct exfat_fptr *p = &fh->node->fptr;
96+
while (p->next != &fh->fptr) {
97+
p = p->next;
98+
if (p == NULL)
99+
exfat_bug("freeing a file handle that wasn't listed in the node");
100+
}
101+
p->next = p->next->next;
102+
103+
exfat_debug("removed a file handle from node");
104+
if(fh->node->fptr.next == NULL)
105+
exfat_debug("no more file handles open on this node");
106+
107+
free(fh);
108+
109+
fi->fh = 0;
56110
}
57111

58112
static int fuse_exfat_getattr(const char* path, struct stat* stbuf)
@@ -152,7 +206,9 @@ static int fuse_exfat_open(const char* path, struct fuse_file_info* fi)
152206
rc = exfat_lookup(&ef, &node, path);
153207
if (rc != 0)
154208
return rc;
155-
set_node(fi, node);
209+
rc = add_fh(fi, node);
210+
if (rc != 0)
211+
return rc;
156212
return 0;
157213
}
158214

@@ -170,7 +226,9 @@ static int fuse_exfat_create(const char* path, UNUSED mode_t mode,
170226
rc = exfat_lookup(&ef, &node, path);
171227
if (rc != 0)
172228
return rc;
173-
set_node(fi, node);
229+
rc = add_fh(fi, node);
230+
if (rc != 0)
231+
return rc;
174232
return 0;
175233
}
176234

@@ -186,6 +244,7 @@ static int fuse_exfat_release(UNUSED const char* path,
186244
exfat_debug("[%s] %s", __func__, path);
187245
exfat_flush_node(&ef, get_node(fi));
188246
exfat_put_node(&ef, get_node(fi));
247+
remove_fh(fi);
189248
return 0; /* FUSE ignores this return value */
190249
}
191250

@@ -220,14 +279,14 @@ static int fuse_exfat_read(UNUSED const char* path, char* buffer,
220279
size_t size, off_t offset, struct fuse_file_info* fi)
221280
{
222281
exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
223-
return exfat_generic_pread(&ef, get_node(fi), buffer, size, offset);
282+
return exfat_generic_pread(&ef, get_node(fi), buffer, size, offset, get_fptr(fi));
224283
}
225284

226285
static int fuse_exfat_write(UNUSED const char* path, const char* buffer,
227286
size_t size, off_t offset, struct fuse_file_info* fi)
228287
{
229288
exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
230-
return exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset);
289+
return exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset, get_fptr(fi));
231290
}
232291

233292
static int fuse_exfat_unlink(const char* path)

libexfat/cluster.c

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -90,25 +90,33 @@ cluster_t exfat_next_cluster(const struct exfat* ef,
9090
}
9191

9292
cluster_t exfat_advance_cluster(const struct exfat* ef,
93-
struct exfat_node* node, uint32_t count)
93+
struct exfat_node* node, uint32_t count,
94+
struct exfat_fptr* fptr)
9495
{
9596
uint32_t i;
97+
uint32_t *index;
98+
cluster_t *cluster;
9699

97-
if (node->fptr_index > count)
100+
if(fptr == NULL)
101+
/* Fallback for cases where don't have a per-file-handle file
102+
* pointer. */
103+
fptr = &node->fptr;
104+
105+
if (fptr->index > count)
98106
{
99-
node->fptr_index = 0;
100-
node->fptr_cluster = node->start_cluster;
107+
fptr->index = 0;
108+
fptr->cluster = node->start_cluster;
101109
}
102110

103-
for (i = node->fptr_index; i < count; i++)
111+
for (i = fptr->index; i < count; i++)
104112
{
105-
node->fptr_cluster = exfat_next_cluster(ef, node, node->fptr_cluster);
106-
if (CLUSTER_INVALID(*ef->sb, node->fptr_cluster))
113+
fptr->cluster = exfat_next_cluster(ef, node, fptr->cluster);
114+
if (CLUSTER_INVALID(*ef->sb, fptr->cluster))
107115
break; /* the caller should handle this and print appropriate
108116
error message */
109117
}
110-
node->fptr_index = count;
111-
return node->fptr_cluster;
118+
fptr->index = count;
119+
return fptr->cluster;
112120
}
113121

114122
static cluster_t find_bit_and_set(bitmap_t* bitmap, size_t start, size_t end)
@@ -249,7 +257,7 @@ static int grow_file(struct exfat* ef, struct exfat_node* node,
249257
if (node->start_cluster != EXFAT_CLUSTER_FREE)
250258
{
251259
/* get the last cluster of the file */
252-
previous = exfat_advance_cluster(ef, node, current - 1);
260+
previous = exfat_advance_cluster(ef, node, current - 1, NULL);
253261
if (CLUSTER_INVALID(*ef->sb, previous))
254262
{
255263
exfat_error("invalid cluster 0x%x while growing", previous);
@@ -258,14 +266,17 @@ static int grow_file(struct exfat* ef, struct exfat_node* node,
258266
}
259267
else
260268
{
261-
if (node->fptr_index != 0)
262-
exfat_bug("non-zero pointer index (%u)", node->fptr_index);
263269
/* file does not have clusters (i.e. is empty), allocate
264270
the first one for it */
265271
previous = allocate_cluster(ef, 0);
266272
if (CLUSTER_INVALID(*ef->sb, previous))
267273
return -ENOSPC;
268-
node->fptr_cluster = node->start_cluster = previous;
274+
for(struct exfat_fptr* fptr=&node->fptr; fptr != NULL; fptr=fptr->next) {
275+
if (fptr->index != 0)
276+
exfat_bug("non-zero pointer index (%u)", fptr->index);
277+
fptr->cluster = previous;
278+
}
279+
node->start_cluster = previous;
269280
allocated = 1;
270281
/* file consists of only one cluster, so it's contiguous */
271282
node->is_contiguous = true;
@@ -318,7 +329,7 @@ static int shrink_file(struct exfat* ef, struct exfat_node* node,
318329
if (current > difference)
319330
{
320331
cluster_t last = exfat_advance_cluster(ef, node,
321-
current - difference - 1);
332+
current - difference - 1, NULL);
322333
if (CLUSTER_INVALID(*ef->sb, last))
323334
{
324335
exfat_error("invalid cluster 0x%x while shrinking", last);
@@ -335,8 +346,10 @@ static int shrink_file(struct exfat* ef, struct exfat_node* node,
335346
node->start_cluster = EXFAT_CLUSTER_FREE;
336347
node->is_dirty = true;
337348
}
338-
node->fptr_index = 0;
339-
node->fptr_cluster = node->start_cluster;
349+
for(struct exfat_fptr* fptr=&node->fptr; fptr != NULL; fptr=fptr->next) {
350+
fptr->index = 0;
351+
fptr->cluster = node->start_cluster;
352+
}
340353

341354
/* free remaining clusters */
342355
while (difference--)
@@ -379,7 +392,7 @@ static int erase_range(struct exfat* ef, struct exfat_node* node,
379392

380393
cluster_boundary = (begin | (CLUSTER_SIZE(*ef->sb) - 1)) + 1;
381394
cluster = exfat_advance_cluster(ef, node,
382-
begin / CLUSTER_SIZE(*ef->sb));
395+
begin / CLUSTER_SIZE(*ef->sb), NULL);
383396
if (CLUSTER_INVALID(*ef->sb, cluster))
384397
{
385398
exfat_error("invalid cluster 0x%x while erasing", cluster);

libexfat/exfat.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@
7171
be corrupted with 32-bit off_t. */
7272
STATIC_ASSERT(sizeof(off_t) == 8);
7373

74+
struct exfat_fptr
75+
{
76+
uint32_t index;
77+
cluster_t cluster;
78+
struct exfat_fptr* next;
79+
};
80+
7481
struct exfat_node
7582
{
7683
struct exfat_node* parent;
@@ -79,8 +86,7 @@ struct exfat_node
7986
struct exfat_node* prev;
8087

8188
int references;
82-
uint32_t fptr_index;
83-
cluster_t fptr_cluster;
89+
struct exfat_fptr fptr;
8490
off_t entry_offset;
8591
cluster_t start_cluster;
8692
uint16_t attrib;
@@ -162,9 +168,9 @@ ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
162168
ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
163169
off_t offset);
164170
ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
165-
void* buffer, size_t size, off_t offset);
171+
void* buffer, size_t size, off_t offset, struct exfat_fptr* fh);
166172
ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
167-
const void* buffer, size_t size, off_t offset);
173+
const void* buffer, size_t size, off_t offset, struct exfat_fptr* fh);
168174

169175
int exfat_opendir(struct exfat* ef, struct exfat_node* dir,
170176
struct exfat_iterator* it);
@@ -179,7 +185,7 @@ off_t exfat_c2o(const struct exfat* ef, cluster_t cluster);
179185
cluster_t exfat_next_cluster(const struct exfat* ef,
180186
const struct exfat_node* node, cluster_t cluster);
181187
cluster_t exfat_advance_cluster(const struct exfat* ef,
182-
struct exfat_node* node, uint32_t count);
188+
struct exfat_node* node, uint32_t count, struct exfat_fptr* fptr);
183189
int exfat_flush_nodes(struct exfat* ef);
184190
int exfat_flush(struct exfat* ef);
185191
int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size,

libexfat/io.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
388388
}
389389

390390
ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
391-
void* buffer, size_t size, off_t offset)
391+
void* buffer, size_t size, off_t offset, struct exfat_fptr* fptr)
392392
{
393393
uint64_t newsize = offset;
394394
cluster_t cluster;
@@ -402,7 +402,7 @@ ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
402402
if (size == 0)
403403
return 0;
404404

405-
cluster = exfat_advance_cluster(ef, node, newsize / CLUSTER_SIZE(*ef->sb));
405+
cluster = exfat_advance_cluster(ef, node, newsize / CLUSTER_SIZE(*ef->sb), fptr);
406406
if (CLUSTER_INVALID(*ef->sb, cluster))
407407
{
408408
exfat_error("invalid cluster 0x%x while reading", cluster);
@@ -436,7 +436,7 @@ ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
436436
}
437437

438438
ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
439-
const void* buffer, size_t size, off_t offset)
439+
const void* buffer, size_t size, off_t offset, struct exfat_fptr* fptr)
440440
{
441441
uint64_t newsize = offset;
442442
int rc;
@@ -461,7 +461,7 @@ ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
461461
if (size == 0)
462462
return 0;
463463

464-
cluster = exfat_advance_cluster(ef, node, newsize / CLUSTER_SIZE(*ef->sb));
464+
cluster = exfat_advance_cluster(ef, node, newsize / CLUSTER_SIZE(*ef->sb), fptr);
465465
if (CLUSTER_INVALID(*ef->sb, cluster))
466466
{
467467
exfat_error("invalid cluster 0x%x while writing", cluster);

libexfat/mount.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ int exfat_mount(struct exfat* ef, const char* spec, const char* options)
303303
memset(ef->root, 0, sizeof(struct exfat_node));
304304
ef->root->attrib = EXFAT_ATTRIB_DIR;
305305
ef->root->start_cluster = le32_to_cpu(ef->sb->rootdir_cluster);
306-
ef->root->fptr_cluster = ef->root->start_cluster;
306+
ef->root->fptr.cluster = ef->root->start_cluster;
307307
ef->root->name[0] = cpu_to_le16('\0');
308308
ef->root->size = rootdir_size(ef);
309309
if (ef->root->size == 0)

libexfat/node.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ int exfat_cleanup_node(struct exfat* ef, struct exfat_node* node)
6767
exfat_bug("unable to cleanup a node with %d references",
6868
node->references);
6969

70+
if (node->fptr.next != NULL)
71+
exfat_bug("unable to cleanup a node with an open file handle");
72+
7073
if (node->is_unlinked)
7174
{
7275
/* free all clusters and node structure itself */
@@ -86,7 +89,7 @@ static int read_entries(struct exfat* ef, struct exfat_node* dir,
8689
exfat_bug("attempted to read entries from a file");
8790

8891
size = exfat_generic_pread(ef, dir, entries,
89-
sizeof(struct exfat_entry[n]), offset);
92+
sizeof(struct exfat_entry[n]), offset, NULL);
9093
if (size == (ssize_t) sizeof(struct exfat_entry) * n)
9194
return 0; /* success */
9295
if (size == 0)
@@ -107,7 +110,7 @@ static int write_entries(struct exfat* ef, struct exfat_node* dir,
107110
exfat_bug("attempted to write entries into a file");
108111

109112
size = exfat_generic_pwrite(ef, dir, entries,
110-
sizeof(struct exfat_entry[n]), offset);
113+
sizeof(struct exfat_entry[n]), offset, NULL);
111114
if (size == (ssize_t) sizeof(struct exfat_entry) * n)
112115
return 0; /* success */
113116
if (size < 0)
@@ -146,7 +149,7 @@ static void init_node_meta2(struct exfat_node* node,
146149
{
147150
node->size = le64_to_cpu(meta2->size);
148151
node->start_cluster = le32_to_cpu(meta2->start_cluster);
149-
node->fptr_cluster = node->start_cluster;
152+
node->fptr.cluster = node->start_cluster;
150153
node->is_contiguous = ((meta2->flags & EXFAT_FLAG_CONTIGUOUS) != 0);
151154
}
152155

libexfat/repair.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ bool exfat_fix_unknown_entry(struct exfat* ef, struct exfat_node* dir,
9494

9595
deleted.type &= ~EXFAT_ENTRY_VALID;
9696
if (exfat_generic_pwrite(ef, dir, &deleted, sizeof(struct exfat_entry),
97-
offset) != sizeof(struct exfat_entry))
97+
offset, NULL) != sizeof(struct exfat_entry))
9898
return false;
9999

100100
exfat_errors_fixed++;

0 commit comments

Comments
 (0)