| @@ -9,6 +9,7 @@ · 8 unmodified lines |
| 9 | 9 | #include "dev_uring_i.h" |
| 10 | 10 | #include "fuse_trace.h" |
| 11 | 11 | |
| 12 | +#include <linux/bitmap.h> |
| 12 | 13 | #include <linux/fs.h> |
| 13 | 14 | #include <linux/io_uring/cmd.h> |
| 14 | 15 | |
| @@ -41,6 +42,11 @@ enum fuse_uring_header_type { · 26 unmodified lines |
| 41 | 42 | FUSE_URING_HEADER_RING_ENT, |
| 42 | 43 | }; |
| 43 | 44 | |
| 45 | +static inline bool bufpool_enabled(struct fuse_ring_queue *queue) |
| 46 | +{ |
| 47 | + return queue->payload_mode == FUSE_PAYLOAD_BUFPOOL; |
| 48 | +} |
| 49 | + |
| 44 | 50 | static void uring_cmd_set_ring_ent(struct io_uring_cmd *cmd, |
| 45 | 51 | struct fuse_ring_ent *ring_ent) |
| 46 | 52 | { |
| @@ -222,6 +228,7 @@ void fuse_uring_destruct(struct fuse_chan *fch) · 175 unmodified lines |
| 222 | 228 | } |
| 223 | 229 | |
| 224 | 230 | kfree(queue->fpq.processing); |
| 231 | + kfree(queue->bufpool); |
| 225 | 232 | kfree(queue); |
| 226 | 233 | WRITE_ONCE(ring->queues[qid], NULL); |
| 227 | 234 | } |
| @@ -316,6 +323,7 @@ static struct fuse_ring_queue *fuse_uring_create_queue(struct fuse_ring *ring, · 88 unmodified lines |
| 316 | 323 | if (ring->queues[qid]) { |
| 317 | 324 | spin_unlock(&fch->lock); |
| 318 | 325 | kfree(queue->fpq.processing); |
| 326 | + kfree(queue->bufpool); |
| 319 | 327 | kfree(queue); |
| 320 | 328 | return fail_if_exists ? ERR_PTR(-EEXIST) : ring->queues[qid]; |
| 321 | 329 | } |
| @@ -646,13 +654,14 @@ static int copy_header_from_ring(struct fuse_ring_ent *ent, · 324 unmodified lines |
| 646 | 654 | } |
| 647 | 655 | |
| 648 | 656 | static int setup_fuse_copy_state(struct fuse_copy_state *cs, |
| 649 | | − struct fuse_ring *ring, struct fuse_req *req, |
| 657 | + struct fuse_req *req, |
| 650 | 658 | struct fuse_ring_ent *ent, int dir, |
| 651 | 659 | struct iov_iter *iter) |
| 652 | 660 | { |
| 653 | 661 | int err; |
| 654 | 662 | |
| 655 | | − err = import_ubuf(dir, ent->payload, ring->max_payload_sz, iter); |
| 663 | + err = import_ubuf(dir, ent->payload.iov_base, ent->payload.iov_len, |
| 664 | + iter); |
| 656 | 665 | if (err) { |
| 657 | 666 | pr_info_ratelimited("fuse: Import of user buffer failed\n"); |
| 658 | 667 | return err; |
| @@ -666,8 +675,7 @@ static int setup_fuse_copy_state(struct fuse_copy_state *cs, · 7 unmodified lines |
| 666 | 675 | return 0; |
| 667 | 676 | } |
| 668 | 677 | |
| 669 | | −static int fuse_uring_copy_from_ring(struct fuse_ring *ring, |
| 670 | | − struct fuse_req *req, |
| 678 | +static int fuse_uring_copy_from_ring(struct fuse_req *req, |
| 671 | 679 | struct fuse_ring_ent *ent) |
| 672 | 680 | { |
| 673 | 681 | struct fuse_copy_state cs; |
| @@ -681,7 +689,7 @@ static int fuse_uring_copy_from_ring(struct fuse_ring *ring, · 7 unmodified lines |
| 681 | 689 | if (err) |
| 682 | 690 | return err; |
| 683 | 691 | |
| 684 | | − err = setup_fuse_copy_state(&cs, ring, req, ent, ITER_SOURCE, &iter); |
| 692 | + err = setup_fuse_copy_state(&cs, req, ent, ITER_SOURCE, &iter); |
| 685 | 693 | if (err) |
| 686 | 694 | return err; |
| 687 | 695 | |
| @@ -693,7 +701,7 @@ static int fuse_uring_copy_from_ring(struct fuse_ring *ring, · 5 unmodified lines |
| 693 | 701 | /* |
| 694 | 702 | * Copy data from the req to the ring buffer |
| 695 | 703 | */ |
| 696 | | −static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req, |
| 704 | +static int fuse_uring_args_to_ring(struct fuse_req *req, |
| 697 | 705 | struct fuse_ring_ent *ent) |
| 698 | 706 | { |
| 699 | 707 | struct fuse_copy_state cs; |
| @@ -707,7 +715,7 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req, · 7 unmodified lines |
| 707 | 715 | .commit_id = req->in.h.unique, |
| 708 | 716 | }; |
| 709 | 717 | |
| 710 | | − err = setup_fuse_copy_state(&cs, ring, req, ent, ITER_DEST, &iter); |
| 718 | + err = setup_fuse_copy_state(&cs, req, ent, ITER_DEST, &iter); |
| 711 | 719 | if (err) |
| 712 | 720 | return err; |
| 713 | 721 | |
| @@ -737,6 +745,10 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req, · 23 unmodified lines |
| 737 | 745 | } |
| 738 | 746 | |
| 739 | 747 | ent_in_out.payload_sz = cs.ring.copied_sz; |
| 748 | + if (bufpool_enabled(ent->queue) && ent->payload.iov_base) |
| 749 | + ent_in_out.offset = |
| 750 | + (uintptr_t)ent->payload.iov_base - ent->queue->bufpool->base_uaddr; |
| 751 | + |
| 740 | 752 | return copy_header_to_ring(ent, FUSE_URING_HEADER_RING_ENT, |
| 741 | 753 | &ent_in_out, sizeof(ent_in_out)); |
| 742 | 754 | } |
| @@ -745,7 +757,6 @@ static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent, · 2 unmodified lines |
| 745 | 757 | struct fuse_req *req) |
| 746 | 758 | { |
| 747 | 759 | struct fuse_ring_queue *queue = ent->queue; |
| 748 | | − struct fuse_ring *ring = queue->ring; |
| 749 | 760 | int err; |
| 750 | 761 | |
| 751 | 762 | err = -EIO; |
| @@ -760,7 +771,7 @@ static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent, · 8 unmodified lines |
| 760 | 771 | return err; |
| 761 | 772 | |
| 762 | 773 | /* copy the request */ |
| 763 | | − err = fuse_uring_args_to_ring(ring, req, ent); |
| 774 | + err = fuse_uring_args_to_ring(req, ent); |
| 764 | 775 | if (unlikely(err)) { |
| 765 | 776 | pr_info_ratelimited("Copy to ring failed: %d\n", err); |
| 766 | 777 | return err; |
| @@ -771,6 +782,91 @@ static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent, · 4 unmodified lines |
| 771 | 782 | sizeof(req->in.h)); |
| 772 | 783 | } |
| 773 | 784 | |
| 785 | +static bool fuse_uring_req_has_payload(struct fuse_req *req) |
| 786 | +{ |
| 787 | + struct fuse_args *args = req->args; |
| 788 | + |
| 789 | + return args->in_numargs > 1 || args->out_numargs; |
| 790 | +} |
| 791 | + |
| 792 | +static int fuse_uring_select_buffer(struct fuse_ring_ent *ent) |
| 793 | +{ |
| 794 | + struct fuse_ring_queue *queue = ent->queue; |
| 795 | + struct fuse_bufpool *pool = queue->bufpool; |
| 796 | + unsigned int id; |
| 797 | + |
| 798 | + lockdep_assert_held(&queue->lock); |
| 799 | + |
| 800 | + id = find_first_bit(pool->free_map, pool->nr_bufs); |
| 801 | + if (id >= pool->nr_bufs) |
| 802 | + return -ENOBUFS; |
| 803 | + |
| 804 | + WARN_ON_ONCE(ent->payload.iov_base); |
| 805 | + __clear_bit(id, pool->free_map); |
| 806 | + |
| 807 | + ent->buf_id = id; |
| 808 | + ent->payload.iov_base = |
| 809 | + (void __user *)(pool->base_uaddr + id * pool->buf_size); |
| 810 | + ent->payload.iov_len = pool->buf_size; |
| 811 | + |
| 812 | + return 0; |
| 813 | +} |
| 814 | + |
| 815 | +static void fuse_uring_recycle_buffer(struct fuse_ring_ent *ent) |
| 816 | +{ |
| 817 | + struct iovec *ent_payload = &ent->payload; |
| 818 | + struct fuse_ring_queue *queue = ent->queue; |
| 819 | + struct fuse_bufpool *pool; |
| 820 | + |
| 821 | + lockdep_assert_held(&queue->lock); |
| 822 | + |
| 823 | + if (!bufpool_enabled(queue) || !ent_payload->iov_base) |
| 824 | + return; |
| 825 | + |
| 826 | + pool = queue->bufpool; |
| 827 | + |
| 828 | + /* a buffer should never be recycled twice */ |
| 829 | + WARN_ON_ONCE(test_bit(ent->buf_id, pool->free_map)); |
| 830 | + __set_bit(ent->buf_id, pool->free_map); |
| 831 | + |
| 832 | + memset(ent_payload, 0, sizeof(*ent_payload)); |
| 833 | + ent->buf_id = 0; |
| 834 | +} |
| 835 | + |
| 836 | +static int fuse_uring_next_req_update_buffer(struct fuse_ring_ent *ent, |
| 837 | + struct fuse_req *req) |
| 838 | +{ |
| 839 | + bool buffer_selected; |
| 840 | + bool has_payload; |
| 841 | + |
| 842 | + if (!bufpool_enabled(ent->queue)) |
| 843 | + return 0; |
| 844 | + |
| 845 | + buffer_selected = !!ent->payload.iov_base; |
| 846 | + has_payload = fuse_uring_req_has_payload(req); |
| 847 | + |
| 848 | + if (has_payload && !buffer_selected) |
| 849 | + return fuse_uring_select_buffer(ent); |
| 850 | + |
| 851 | + if (!has_payload && buffer_selected) |
| 852 | + fuse_uring_recycle_buffer(ent); |
| 853 | + |
| 854 | + return 0; |
| 855 | +} |
| 856 | + |
| 857 | +static int fuse_uring_prep_buffer(struct fuse_ring_ent *ent, |
| 858 | + struct fuse_req *req) |
| 859 | +{ |
| 860 | + if (!bufpool_enabled(ent->queue)) |
| 861 | + return 0; |
| 862 | + |
| 863 | + /* no payload to copy, can skip selecting a buffer */ |
| 864 | + if (!fuse_uring_req_has_payload(req)) |
| 865 | + return 0; |
| 866 | + |
| 867 | + return fuse_uring_select_buffer(ent); |
| 868 | +} |
| 869 | + |
| 774 | 870 | static int fuse_uring_prepare_send(struct fuse_ring_ent *ent, |
| 775 | 871 | struct fuse_req *req) |
| 776 | 872 | { |
| @@ -858,9 +954,12 @@ static struct fuse_req *fuse_uring_ent_assign_req(struct fuse_ring_ent *ent) · 81 unmodified lines |
| 858 | 954 | |
| 859 | 955 | /* get and assign the next entry while it is still holding the lock */ |
| 860 | 956 | req = list_first_entry_or_null(req_queue, struct fuse_req, list); |
| 861 | | − if (req) |
| 862 | | − fuse_uring_add_req_to_ring_ent(ent, req); |
| 957 | + if (!req || fuse_uring_next_req_update_buffer(ent, req)) { |
| 958 | + fuse_uring_recycle_buffer(ent); |
| 959 | + return NULL; |
| 960 | + } |
| 863 | 961 | |
| 962 | + fuse_uring_add_req_to_ring_ent(ent, req); |
| 864 | 963 | return req; |
| 865 | 964 | } |
| 866 | 965 | |
| @@ -872,7 +971,6 @@ static struct fuse_req *fuse_uring_ent_assign_req(struct fuse_ring_ent *ent) · 5 unmodified lines |
| 872 | 971 | static void fuse_uring_commit(struct fuse_ring_ent *ent, struct fuse_req *req, |
| 873 | 972 | unsigned int issue_flags) |
| 874 | 973 | { |
| 875 | | − struct fuse_ring *ring = ent->queue->ring; |
| 876 | 974 | ssize_t err = -EFAULT; |
| 877 | 975 | |
| 878 | 976 | if (copy_header_from_ring(ent, FUSE_URING_HEADER_IN_OUT, &req->out.h, |
| @@ -885,7 +983,7 @@ static void fuse_uring_commit(struct fuse_ring_ent *ent, struct fuse_req *req, · 6 unmodified lines |
| 885 | 983 | goto out; |
| 886 | 984 | } |
| 887 | 985 | |
| 888 | | − err = fuse_uring_copy_from_ring(ring, req, ent); |
| 986 | + err = fuse_uring_copy_from_ring(req, ent); |
| 889 | 987 | out: |
| 890 | 988 | fuse_uring_req_end(ent, req, err); |
| 891 | 989 | } |
| @@ -1004,6 +1102,7 @@ static int fuse_uring_commit_fetch(struct io_uring_cmd *cmd, int issue_flags, · 112 unmodified lines |
| 1004 | 1102 | if (err != 0) { |
| 1005 | 1103 | pr_info_ratelimited("qid=%d commit_id %llu state %d", |
| 1006 | 1104 | queue->qid, commit_id, ent->state); |
| 1105 | + fuse_uring_recycle_buffer(ent); |
| 1007 | 1106 | spin_unlock(&queue->lock); |
| 1008 | 1107 | fuse_uring_req_end(ent, req, err); |
| 1009 | 1108 | return err; |
| @@ -1021,6 +1120,11 @@ static int fuse_uring_commit_fetch(struct io_uring_cmd *cmd, int issue_flags, · 11 unmodified lines |
| 1021 | 1120 | * fuse requests would otherwise not get processed - committing |
| 1022 | 1121 | * and fetching is done in one step vs legacy fuse, which has separated |
| 1023 | 1122 | * read (fetch request) and write (commit result). |
| 1123 | + * |
| 1124 | + * If there is no next request or if all buffers are busy (if using a |
| 1125 | + * bufpool), the cmd is not returned to userspace. The entry is left |
| 1126 | + * available and the cmd only returns to userspace when there's a |
| 1127 | + * next request and an available buffer. |
| 1024 | 1128 | */ |
| 1025 | 1129 | if (fuse_uring_get_next_fuse_req(ent, queue)) |
| 1026 | 1130 | fuse_uring_send(ent, cmd, 0, issue_flags); |
| @@ -1145,11 +1249,23 @@ fuse_uring_create_ring_ent(struct io_uring_cmd *cmd, · 118 unmodified lines |
| 1145 | 1249 | } |
| 1146 | 1250 | |
| 1147 | 1251 | payload = &iov[FUSE_URING_IOV_PAYLOAD]; |
| 1148 | | − if (payload->iov_len < ring->max_payload_sz) { |
| 1149 | | − pr_info_ratelimited("Invalid req payload len %zu\n", |
| 1150 | | − payload->iov_len); |
| 1151 | | − return ERR_PTR(err); |
| 1252 | + |
| 1253 | + spin_lock(&queue->lock); |
| 1254 | + if (bufpool_enabled(queue)) { |
| 1255 | + if (payload->iov_base || payload->iov_len) { |
| 1256 | + spin_unlock(&queue->lock); |
| 1257 | + return ERR_PTR(err); |
| 1258 | + } |
| 1259 | + } else { |
| 1260 | + if (payload->iov_len < ring->max_payload_sz) { |
| 1261 | + pr_info_ratelimited("Invalid req payload len %zu\n", |
| 1262 | + payload->iov_len); |
| 1263 | + spin_unlock(&queue->lock); |
| 1264 | + return ERR_PTR(err); |
| 1265 | + } |
| 1266 | + queue->payload_mode = FUSE_PAYLOAD_PER_ENT; |
| 1152 | 1267 | } |
| 1268 | + spin_unlock(&queue->lock); |
| 1153 | 1269 | |
| 1154 | 1270 | err = -ENOMEM; |
| 1155 | 1271 | ent = kzalloc_obj(*ent, GFP_KERNEL_ACCOUNT); |
| @@ -1160,7 +1276,8 @@ fuse_uring_create_ring_ent(struct io_uring_cmd *cmd, · 4 unmodified lines |
| 1160 | 1276 | |
| 1161 | 1277 | ent->queue = queue; |
| 1162 | 1278 | ent->headers = headers->iov_base; |
| 1163 | | − ent->payload = payload->iov_base; |
| 1279 | + if (queue->payload_mode == FUSE_PAYLOAD_PER_ENT) |
| 1280 | + ent->payload = *payload; |
| 1164 | 1281 | |
| 1165 | 1282 | atomic_inc(&ring->queue_refs); |
| 1166 | 1283 | return ent; |
| @@ -1231,6 +1348,67 @@ static int fuse_uring_add_queue(struct io_uring_cmd *cmd, struct fuse_chan *fch) · 64 unmodified lines |
| 1231 | 1348 | return 0; |
| 1232 | 1349 | } |
| 1233 | 1350 | |
| 1351 | +static int fuse_uring_add_bufpool(struct io_uring_cmd *cmd, |
| 1352 | + struct fuse_chan *fch) |
| 1353 | +{ |
| 1354 | + const struct fuse_uring_cmd_req *cmd_req = |
| 1355 | + io_uring_sqe128_cmd(cmd->sqe, struct fuse_uring_cmd_req); |
| 1356 | + unsigned int qid = READ_ONCE(cmd_req->qid); |
| 1357 | + uint64_t flags = READ_ONCE(cmd_req->flags); |
| 1358 | + /* paired with the smp_store_release() in fuse_uring_create */ |
| 1359 | + struct fuse_ring *ring = smp_load_acquire(&fch->ring); |
| 1360 | + struct fuse_ring_queue *queue; |
| 1361 | + struct fuse_bufpool *pool; |
| 1362 | + uintptr_t pool_uaddr; |
| 1363 | + unsigned int pool_len, nr_bufs; |
| 1364 | + size_t pool_size, buf_size; |
| 1365 | + |
| 1366 | + if (!ring || qid >= ring->nr_queues || flags) |
| 1367 | + return -EINVAL; |
| 1368 | + |
| 1369 | + /* reserved for future use, must be zero */ |
| 1370 | + if (READ_ONCE(cmd_req->bufpool.reserved)) |
| 1371 | + return -EINVAL; |
| 1372 | + |
| 1373 | + /* Pairs with smp_store_release() in fuse_uring_create_queue() */ |
| 1374 | + queue = smp_load_acquire(&ring->queues[qid]); |
| 1375 | + if (!queue) |
| 1376 | + return -EINVAL; |
| 1377 | + |
| 1378 | + pool_uaddr = READ_ONCE(cmd_req->bufpool.uaddr); |
| 1379 | + pool_len = READ_ONCE(cmd_req->bufpool.len); |
| 1380 | + |
| 1381 | + /* each buffer holds the max payload size */ |
| 1382 | + buf_size = queue->ring->max_payload_sz; |
| 1383 | + |
| 1384 | + nr_bufs = pool_len / buf_size; |
| 1385 | + if (!nr_bufs) |
| 1386 | + return -EINVAL; |
| 1387 | + |
| 1388 | + pool_size = struct_size(pool, free_map, BITS_TO_LONGS(nr_bufs)); |
| 1389 | + pool = kzalloc(pool_size, GFP_KERNEL_ACCOUNT); |
| 1390 | + if (!pool) |
| 1391 | + return -ENOMEM; |
| 1392 | + |
| 1393 | + pool->base_uaddr = pool_uaddr; |
| 1394 | + pool->buf_size = buf_size; |
| 1395 | + pool->nr_bufs = nr_bufs; |
| 1396 | + /* all buffers are free */ |
| 1397 | + bitmap_set(pool->free_map, 0, nr_bufs); |
| 1398 | + |
| 1399 | + spin_lock(&queue->lock); |
| 1400 | + if (queue->payload_mode != FUSE_PAYLOAD_UNSET) { |
| 1401 | + spin_unlock(&queue->lock); |
| 1402 | + kfree(pool); |
| 1403 | + return -EINVAL; |
| 1404 | + } |
| 1405 | + queue->bufpool = pool; |
| 1406 | + queue->payload_mode = FUSE_PAYLOAD_BUFPOOL; |
| 1407 | + spin_unlock(&queue->lock); |
| 1408 | + |
| 1409 | + return 0; |
| 1410 | +} |
| 1411 | + |
| 1234 | 1412 | /* |
| 1235 | 1413 | * Entry function from io_uring to handle the given passthrough command |
| 1236 | 1414 | * (op code IORING_OP_URING_CMD) |
| @@ -1303,6 +1481,12 @@ int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags) · 66 unmodified lines |
| 1303 | 1481 | pr_info_once("FUSE_IO_URING_CMD_ADD_QUEUE failed err=%d\n", |
| 1304 | 1482 | err); |
| 1305 | 1483 | return err; |
| 1484 | + case FUSE_IO_URING_CMD_ADD_BUFPOOL: |
| 1485 | + err = fuse_uring_add_bufpool(cmd, fch); |
| 1486 | + if (err) |
| 1487 | + pr_info_once("FUSE_IO_URING_ADD_BUFPOOL failed err=%d\n", |
| 1488 | + err); |
| 1489 | + return err; |
| 1306 | 1490 | default: |
| 1307 | 1491 | return -EINVAL; |
| 1308 | 1492 | } |
| @@ -1336,6 +1520,7 @@ static void fuse_uring_send_in_task(struct io_tw_req tw_req, io_tw_token_t tw) · 27 unmodified lines |
| 1336 | 1520 | |
| 1337 | 1521 | spin_lock(&queue->lock); |
| 1338 | 1522 | list_del_init(&ent->list); |
| 1523 | + fuse_uring_recycle_buffer(ent); |
| 1339 | 1524 | spin_unlock(&queue->lock); |
| 1340 | 1525 | |
| 1341 | 1526 | io_uring_cmd_done(cmd, err, issue_flags); |
| @@ -1397,15 +1582,16 @@ void fuse_uring_queue_fuse_req(struct fuse_iqueue *fiq, struct fuse_req *req) · 55 unmodified lines |
| 1397 | 1582 | req->ring_queue = queue; |
| 1398 | 1583 | ent = list_first_entry_or_null(&queue->ent_avail_queue, |
| 1399 | 1584 | struct fuse_ring_ent, list); |
| 1400 | | − if (ent) |
| 1401 | | − fuse_uring_add_req_to_ring_ent(ent, req); |
| 1402 | | − else |
| 1403 | | − list_add_tail(&req->list, &queue->fuse_req_queue); |
| 1404 | | − spin_unlock(&queue->lock); |
| 1405 | 1585 | |
| 1406 | | − if (ent) |
| 1407 | | − fuse_uring_dispatch_ent(ent); |
| 1586 | + if (!ent || fuse_uring_prep_buffer(ent, req)) { |
| 1587 | + list_add_tail(&req->list, &queue->fuse_req_queue); |
| 1588 | + spin_unlock(&queue->lock); |
| 1589 | + return; |
| 1590 | + } |
| 1408 | 1591 | |
| 1592 | + fuse_uring_add_req_to_ring_ent(ent, req); |
| 1593 | + spin_unlock(&queue->lock); |
| 1594 | + fuse_uring_dispatch_ent(ent); |
| 1409 | 1595 | return; |
| 1410 | 1596 | |
| 1411 | 1597 | err_unlock: |
| @@ -1453,10 +1639,9 @@ bool fuse_uring_queue_bq_req(struct fuse_req *req) · 41 unmodified lines |
| 1453 | 1639 | */ |
| 1454 | 1640 | req = list_first_entry_or_null(&queue->fuse_req_queue, struct fuse_req, |
| 1455 | 1641 | list); |
| 1456 | | − if (ent && req) { |
| 1642 | + if (ent && req && !fuse_uring_prep_buffer(ent, req)) { |
| 1457 | 1643 | fuse_uring_add_req_to_ring_ent(ent, req); |
| 1458 | 1644 | spin_unlock(&queue->lock); |
| 1459 | | − |
| 1460 | 1645 | fuse_uring_dispatch_ent(ent); |
| 1461 | 1646 | } else { |
| 1462 | 1647 | spin_unlock(&queue->lock); |