Commit 7ce4fab8 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'fuse-update-5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse

Pull fuse update from Miklos Szeredi:

 - Fix a regression introduced in the last release

 - Fix a number of issues with validating data coming from userspace

 - Some cleanups in virtiofs

* tag 'fuse-update-5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: fix Kconfig indentation
  fuse: fix leak of fuse_io_priv
  virtiofs: Use completions while waiting for queue to be drained
  virtiofs: Do not send forget request "struct list_head" element
  virtiofs: Use a common function to send forget
  virtiofs: Fix old-style declaration
  fuse: verify nlink
  fuse: verify write return
  fuse: verify attributes
parents 0f137416 8d66fcb7
...@@ -248,7 +248,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) ...@@ -248,7 +248,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
kfree(forget); kfree(forget);
if (ret == -ENOMEM) if (ret == -ENOMEM)
goto out; goto out;
if (ret || (outarg.attr.mode ^ inode->i_mode) & S_IFMT) if (ret || fuse_invalid_attr(&outarg.attr) ||
(outarg.attr.mode ^ inode->i_mode) & S_IFMT)
goto invalid; goto invalid;
forget_all_cached_acls(inode); forget_all_cached_acls(inode);
...@@ -319,6 +320,12 @@ int fuse_valid_type(int m) ...@@ -319,6 +320,12 @@ int fuse_valid_type(int m)
S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m); S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m);
} }
bool fuse_invalid_attr(struct fuse_attr *attr)
{
return !fuse_valid_type(attr->mode) ||
attr->size > LLONG_MAX;
}
int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name, int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name,
struct fuse_entry_out *outarg, struct inode **inode) struct fuse_entry_out *outarg, struct inode **inode)
{ {
...@@ -350,7 +357,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name ...@@ -350,7 +357,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
err = -EIO; err = -EIO;
if (!outarg->nodeid) if (!outarg->nodeid)
goto out_put_forget; goto out_put_forget;
if (!fuse_valid_type(outarg->attr.mode)) if (fuse_invalid_attr(&outarg->attr))
goto out_put_forget; goto out_put_forget;
*inode = fuse_iget(sb, outarg->nodeid, outarg->generation, *inode = fuse_iget(sb, outarg->nodeid, outarg->generation,
...@@ -475,7 +482,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, ...@@ -475,7 +482,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
goto out_free_ff; goto out_free_ff;
err = -EIO; err = -EIO;
if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid)) if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid) ||
fuse_invalid_attr(&outentry.attr))
goto out_free_ff; goto out_free_ff;
ff->fh = outopen.fh; ff->fh = outopen.fh;
...@@ -583,7 +591,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_args *args, ...@@ -583,7 +591,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_args *args,
goto out_put_forget_req; goto out_put_forget_req;
err = -EIO; err = -EIO;
if (invalid_nodeid(outarg.nodeid)) if (invalid_nodeid(outarg.nodeid) || fuse_invalid_attr(&outarg.attr))
goto out_put_forget_req; goto out_put_forget_req;
if ((outarg.attr.mode ^ mode) & S_IFMT) if ((outarg.attr.mode ^ mode) & S_IFMT)
...@@ -862,6 +870,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, ...@@ -862,6 +870,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
spin_lock(&fi->lock); spin_lock(&fi->lock);
fi->attr_version = atomic64_inc_return(&fc->attr_version); fi->attr_version = atomic64_inc_return(&fc->attr_version);
if (likely(inode->i_nlink < UINT_MAX))
inc_nlink(inode); inc_nlink(inode);
spin_unlock(&fi->lock); spin_unlock(&fi->lock);
fuse_invalidate_attr(inode); fuse_invalidate_attr(inode);
...@@ -942,7 +951,8 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat, ...@@ -942,7 +951,8 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
args.out_args[0].value = &outarg; args.out_args[0].value = &outarg;
err = fuse_simple_request(fc, &args); err = fuse_simple_request(fc, &args);
if (!err) { if (!err) {
if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) { if (fuse_invalid_attr(&outarg.attr) ||
(inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
make_bad_inode(inode); make_bad_inode(inode);
err = -EIO; err = -EIO;
} else { } else {
...@@ -1563,7 +1573,8 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, ...@@ -1563,7 +1573,8 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
goto error; goto error;
} }
if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) { if (fuse_invalid_attr(&outarg.attr) ||
(inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
make_bad_inode(inode); make_bad_inode(inode);
err = -EIO; err = -EIO;
goto error; goto error;
......
...@@ -713,8 +713,10 @@ static ssize_t fuse_async_req_send(struct fuse_conn *fc, ...@@ -713,8 +713,10 @@ static ssize_t fuse_async_req_send(struct fuse_conn *fc,
ia->ap.args.end = fuse_aio_complete_req; ia->ap.args.end = fuse_aio_complete_req;
err = fuse_simple_background(fc, &ia->ap.args, GFP_KERNEL); err = fuse_simple_background(fc, &ia->ap.args, GFP_KERNEL);
if (err)
fuse_aio_complete_req(fc, &ia->ap.args, err);
return err ?: num_bytes; return num_bytes;
} }
static ssize_t fuse_send_read(struct fuse_io_args *ia, loff_t pos, size_t count, static ssize_t fuse_send_read(struct fuse_io_args *ia, loff_t pos, size_t count,
...@@ -1096,6 +1098,8 @@ static ssize_t fuse_send_write_pages(struct fuse_io_args *ia, ...@@ -1096,6 +1098,8 @@ static ssize_t fuse_send_write_pages(struct fuse_io_args *ia,
ia->write.in.flags = fuse_write_flags(iocb); ia->write.in.flags = fuse_write_flags(iocb);
err = fuse_simple_request(fc, &ap->args); err = fuse_simple_request(fc, &ap->args);
if (!err && ia->write.out.size > count)
err = -EIO;
offset = ap->descs[0].offset; offset = ap->descs[0].offset;
count = ia->write.out.size; count = ia->write.out.size;
......
...@@ -989,6 +989,8 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc); ...@@ -989,6 +989,8 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc);
*/ */
int fuse_valid_type(int m); int fuse_valid_type(int m);
bool fuse_invalid_attr(struct fuse_attr *attr);
/** /**
* Is current process allowed to perform filesystem operation? * Is current process allowed to perform filesystem operation?
*/ */
......
...@@ -184,7 +184,7 @@ static int fuse_direntplus_link(struct file *file, ...@@ -184,7 +184,7 @@ static int fuse_direntplus_link(struct file *file,
if (invalid_nodeid(o->nodeid)) if (invalid_nodeid(o->nodeid))
return -EIO; return -EIO;
if (!fuse_valid_type(o->attr.mode)) if (fuse_invalid_attr(&o->attr))
return -EIO; return -EIO;
fc = get_fuse_conn(dir); fc = get_fuse_conn(dir);
......
...@@ -35,6 +35,7 @@ struct virtio_fs_vq { ...@@ -35,6 +35,7 @@ struct virtio_fs_vq {
struct fuse_dev *fud; struct fuse_dev *fud;
bool connected; bool connected;
long in_flight; long in_flight;
struct completion in_flight_zero; /* No inflight requests */
char name[24]; char name[24];
} ____cacheline_aligned_in_smp; } ____cacheline_aligned_in_smp;
...@@ -48,11 +49,15 @@ struct virtio_fs { ...@@ -48,11 +49,15 @@ struct virtio_fs {
unsigned int num_request_queues; /* number of request queues */ unsigned int num_request_queues; /* number of request queues */
}; };
struct virtio_fs_forget { struct virtio_fs_forget_req {
struct fuse_in_header ih; struct fuse_in_header ih;
struct fuse_forget_in arg; struct fuse_forget_in arg;
};
struct virtio_fs_forget {
/* This request can be temporarily queued on virt queue */ /* This request can be temporarily queued on virt queue */
struct list_head list; struct list_head list;
struct virtio_fs_forget_req req;
}; };
static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq, static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
...@@ -81,6 +86,8 @@ static inline void dec_in_flight_req(struct virtio_fs_vq *fsvq) ...@@ -81,6 +86,8 @@ static inline void dec_in_flight_req(struct virtio_fs_vq *fsvq)
{ {
WARN_ON(fsvq->in_flight <= 0); WARN_ON(fsvq->in_flight <= 0);
fsvq->in_flight--; fsvq->in_flight--;
if (!fsvq->in_flight)
complete(&fsvq->in_flight_zero);
} }
static void release_virtio_fs_obj(struct kref *ref) static void release_virtio_fs_obj(struct kref *ref)
...@@ -111,22 +118,23 @@ static void virtio_fs_drain_queue(struct virtio_fs_vq *fsvq) ...@@ -111,22 +118,23 @@ static void virtio_fs_drain_queue(struct virtio_fs_vq *fsvq)
WARN_ON(fsvq->in_flight < 0); WARN_ON(fsvq->in_flight < 0);
/* Wait for in flight requests to finish.*/ /* Wait for in flight requests to finish.*/
while (1) {
spin_lock(&fsvq->lock); spin_lock(&fsvq->lock);
if (!fsvq->in_flight) { if (fsvq->in_flight) {
/* We are holding virtio_fs_mutex. There should not be any
* waiters waiting for completion.
*/
reinit_completion(&fsvq->in_flight_zero);
spin_unlock(&fsvq->lock); spin_unlock(&fsvq->lock);
break; wait_for_completion(&fsvq->in_flight_zero);
} } else {
spin_unlock(&fsvq->lock); spin_unlock(&fsvq->lock);
/* TODO use completion instead of timeout */
usleep_range(1000, 2000);
} }
flush_work(&fsvq->done_work); flush_work(&fsvq->done_work);
flush_delayed_work(&fsvq->dispatch_work); flush_delayed_work(&fsvq->dispatch_work);
} }
static void virtio_fs_drain_all_queues(struct virtio_fs *fs) static void virtio_fs_drain_all_queues_locked(struct virtio_fs *fs)
{ {
struct virtio_fs_vq *fsvq; struct virtio_fs_vq *fsvq;
int i; int i;
...@@ -137,6 +145,19 @@ static void virtio_fs_drain_all_queues(struct virtio_fs *fs) ...@@ -137,6 +145,19 @@ static void virtio_fs_drain_all_queues(struct virtio_fs *fs)
} }
} }
static void virtio_fs_drain_all_queues(struct virtio_fs *fs)
{
/* Provides mutual exclusion between ->remove and ->kill_sb
* paths. We don't want both of these draining queue at the
* same time. Current completion logic reinits completion
* and that means there should not be any other thread
* doing reinit or waiting for completion already.
*/
mutex_lock(&virtio_fs_mutex);
virtio_fs_drain_all_queues_locked(fs);
mutex_unlock(&virtio_fs_mutex);
}
static void virtio_fs_start_all_queues(struct virtio_fs *fs) static void virtio_fs_start_all_queues(struct virtio_fs *fs)
{ {
struct virtio_fs_vq *fsvq; struct virtio_fs_vq *fsvq;
...@@ -313,65 +334,86 @@ static void virtio_fs_request_dispatch_work(struct work_struct *work) ...@@ -313,65 +334,86 @@ static void virtio_fs_request_dispatch_work(struct work_struct *work)
} }
} }
static void virtio_fs_hiprio_dispatch_work(struct work_struct *work) /*
* Returns 1 if queue is full and sender should wait a bit before sending
* next request, 0 otherwise.
*/
static int send_forget_request(struct virtio_fs_vq *fsvq,
struct virtio_fs_forget *forget,
bool in_flight)
{ {
struct virtio_fs_forget *forget;
struct virtio_fs_vq *fsvq = container_of(work, struct virtio_fs_vq,
dispatch_work.work);
struct virtqueue *vq = fsvq->vq;
struct scatterlist sg; struct scatterlist sg;
struct scatterlist *sgs[] = {&sg}; struct virtqueue *vq;
int ret = 0;
bool notify; bool notify;
int ret; struct virtio_fs_forget_req *req = &forget->req;
pr_debug("virtio-fs: worker %s called.\n", __func__);
while (1) {
spin_lock(&fsvq->lock); spin_lock(&fsvq->lock);
forget = list_first_entry_or_null(&fsvq->queued_reqs,
struct virtio_fs_forget, list);
if (!forget) {
spin_unlock(&fsvq->lock);
return;
}
list_del(&forget->list);
if (!fsvq->connected) { if (!fsvq->connected) {
if (in_flight)
dec_in_flight_req(fsvq); dec_in_flight_req(fsvq);
spin_unlock(&fsvq->lock);
kfree(forget); kfree(forget);
continue; goto out;
} }
sg_init_one(&sg, forget, sizeof(*forget)); sg_init_one(&sg, req, sizeof(*req));
vq = fsvq->vq;
/* Enqueue the request */
dev_dbg(&vq->vdev->dev, "%s\n", __func__); dev_dbg(&vq->vdev->dev, "%s\n", __func__);
ret = virtqueue_add_sgs(vq, sgs, 1, 0, forget, GFP_ATOMIC);
ret = virtqueue_add_outbuf(vq, &sg, 1, forget, GFP_ATOMIC);
if (ret < 0) { if (ret < 0) {
if (ret == -ENOMEM || ret == -ENOSPC) { if (ret == -ENOMEM || ret == -ENOSPC) {
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Will try later\n", pr_debug("virtio-fs: Could not queue FORGET: err=%d. Will try later\n",
ret); ret);
list_add_tail(&forget->list, list_add_tail(&forget->list, &fsvq->queued_reqs);
&fsvq->queued_reqs);
schedule_delayed_work(&fsvq->dispatch_work, schedule_delayed_work(&fsvq->dispatch_work,
msecs_to_jiffies(1)); msecs_to_jiffies(1));
if (!in_flight)
inc_in_flight_req(fsvq);
/* Queue is full */
ret = 1;
} else { } else {
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Dropping it.\n", pr_debug("virtio-fs: Could not queue FORGET: err=%d. Dropping it.\n",
ret); ret);
dec_in_flight_req(fsvq);
kfree(forget); kfree(forget);
if (in_flight)
dec_in_flight_req(fsvq);
} }
spin_unlock(&fsvq->lock); goto out;
return;
} }
if (!in_flight)
inc_in_flight_req(fsvq);
notify = virtqueue_kick_prepare(vq); notify = virtqueue_kick_prepare(vq);
spin_unlock(&fsvq->lock); spin_unlock(&fsvq->lock);
if (notify) if (notify)
virtqueue_notify(vq); virtqueue_notify(vq);
pr_debug("virtio-fs: worker %s dispatched one forget request.\n", return ret;
__func__); out:
spin_unlock(&fsvq->lock);
return ret;
}
static void virtio_fs_hiprio_dispatch_work(struct work_struct *work)
{
struct virtio_fs_forget *forget;
struct virtio_fs_vq *fsvq = container_of(work, struct virtio_fs_vq,
dispatch_work.work);
pr_debug("virtio-fs: worker %s called.\n", __func__);
while (1) {
spin_lock(&fsvq->lock);
forget = list_first_entry_or_null(&fsvq->queued_reqs,
struct virtio_fs_forget, list);
if (!forget) {
spin_unlock(&fsvq->lock);
return;
}
list_del(&forget->list);
spin_unlock(&fsvq->lock);
if (send_forget_request(fsvq, forget, true))
return;
} }
} }
...@@ -556,6 +598,7 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev, ...@@ -556,6 +598,7 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
INIT_LIST_HEAD(&fs->vqs[VQ_HIPRIO].end_reqs); INIT_LIST_HEAD(&fs->vqs[VQ_HIPRIO].end_reqs);
INIT_DELAYED_WORK(&fs->vqs[VQ_HIPRIO].dispatch_work, INIT_DELAYED_WORK(&fs->vqs[VQ_HIPRIO].dispatch_work,
virtio_fs_hiprio_dispatch_work); virtio_fs_hiprio_dispatch_work);
init_completion(&fs->vqs[VQ_HIPRIO].in_flight_zero);
spin_lock_init(&fs->vqs[VQ_HIPRIO].lock); spin_lock_init(&fs->vqs[VQ_HIPRIO].lock);
/* Initialize the requests virtqueues */ /* Initialize the requests virtqueues */
...@@ -566,6 +609,7 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev, ...@@ -566,6 +609,7 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
virtio_fs_request_dispatch_work); virtio_fs_request_dispatch_work);
INIT_LIST_HEAD(&fs->vqs[i].queued_reqs); INIT_LIST_HEAD(&fs->vqs[i].queued_reqs);
INIT_LIST_HEAD(&fs->vqs[i].end_reqs); INIT_LIST_HEAD(&fs->vqs[i].end_reqs);
init_completion(&fs->vqs[i].in_flight_zero);
snprintf(fs->vqs[i].name, sizeof(fs->vqs[i].name), snprintf(fs->vqs[i].name, sizeof(fs->vqs[i].name),
"requests.%u", i - VQ_REQUEST); "requests.%u", i - VQ_REQUEST);
callbacks[i] = virtio_fs_vq_done; callbacks[i] = virtio_fs_vq_done;
...@@ -659,7 +703,7 @@ static void virtio_fs_remove(struct virtio_device *vdev) ...@@ -659,7 +703,7 @@ static void virtio_fs_remove(struct virtio_device *vdev)
/* This device is going away. No one should get new reference */ /* This device is going away. No one should get new reference */
list_del_init(&fs->list); list_del_init(&fs->list);
virtio_fs_stop_all_queues(fs); virtio_fs_stop_all_queues(fs);
virtio_fs_drain_all_queues(fs); virtio_fs_drain_all_queues_locked(fs);
vdev->config->reset(vdev); vdev->config->reset(vdev);
virtio_fs_cleanup_vqs(vdev, fs); virtio_fs_cleanup_vqs(vdev, fs);
...@@ -684,12 +728,12 @@ static int virtio_fs_restore(struct virtio_device *vdev) ...@@ -684,12 +728,12 @@ static int virtio_fs_restore(struct virtio_device *vdev)
} }
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
const static struct virtio_device_id id_table[] = { static const struct virtio_device_id id_table[] = {
{ VIRTIO_ID_FS, VIRTIO_DEV_ANY_ID }, { VIRTIO_ID_FS, VIRTIO_DEV_ANY_ID },
{}, {},
}; };
const static unsigned int feature_table[] = {}; static const unsigned int feature_table[] = {};
static struct virtio_driver virtio_fs_driver = { static struct virtio_driver virtio_fs_driver = {
.driver.name = KBUILD_MODNAME, .driver.name = KBUILD_MODNAME,
...@@ -710,14 +754,10 @@ __releases(fiq->lock) ...@@ -710,14 +754,10 @@ __releases(fiq->lock)
{ {
struct fuse_forget_link *link; struct fuse_forget_link *link;
struct virtio_fs_forget *forget; struct virtio_fs_forget *forget;
struct scatterlist sg; struct virtio_fs_forget_req *req;
struct scatterlist *sgs[] = {&sg};
struct virtio_fs *fs; struct virtio_fs *fs;
struct virtqueue *vq;
struct virtio_fs_vq *fsvq; struct virtio_fs_vq *fsvq;
bool notify;
u64 unique; u64 unique;
int ret;
link = fuse_dequeue_forget(fiq, 1, NULL); link = fuse_dequeue_forget(fiq, 1, NULL);
unique = fuse_get_unique(fiq); unique = fuse_get_unique(fiq);
...@@ -728,57 +768,19 @@ __releases(fiq->lock) ...@@ -728,57 +768,19 @@ __releases(fiq->lock)
/* Allocate a buffer for the request */ /* Allocate a buffer for the request */
forget = kmalloc(sizeof(*forget), GFP_NOFS | __GFP_NOFAIL); forget = kmalloc(sizeof(*forget), GFP_NOFS | __GFP_NOFAIL);
req = &forget->req;
forget->ih = (struct fuse_in_header){ req->ih = (struct fuse_in_header){
.opcode = FUSE_FORGET, .opcode = FUSE_FORGET,
.nodeid = link->forget_one.nodeid, .nodeid = link->forget_one.nodeid,
.unique = unique, .unique = unique,
.len = sizeof(*forget), .len = sizeof(*req),
}; };
forget->arg = (struct fuse_forget_in){ req->arg = (struct fuse_forget_in){
.nlookup = link->forget_one.nlookup, .nlookup = link->forget_one.nlookup,
}; };
sg_init_one(&sg, forget, sizeof(*forget)); send_forget_request(fsvq, forget, false);
/* Enqueue the request */
spin_lock(&fsvq->lock);
if (!fsvq->connected) {
kfree(forget);
spin_unlock(&fsvq->lock);
goto out;
}
vq = fsvq->vq;
dev_dbg(&vq->vdev->dev, "%s\n", __func__);
ret = virtqueue_add_sgs(vq, sgs, 1, 0, forget, GFP_ATOMIC);
if (ret < 0) {
if (ret == -ENOMEM || ret == -ENOSPC) {
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Will try later.\n",
ret);
list_add_tail(&forget->list, &fsvq->queued_reqs);
schedule_delayed_work(&fsvq->dispatch_work,
msecs_to_jiffies(1));
inc_in_flight_req(fsvq);
} else {
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Dropping it.\n",
ret);
kfree(forget);
}
spin_unlock(&fsvq->lock);
goto out;
}
inc_in_flight_req(fsvq);
notify = virtqueue_kick_prepare(vq);
spin_unlock(&fsvq->lock);
if (notify)
virtqueue_notify(vq);
out:
kfree(link); kfree(link);
} }
...@@ -1026,7 +1028,7 @@ __releases(fiq->lock) ...@@ -1026,7 +1028,7 @@ __releases(fiq->lock)
} }
} }
const static struct fuse_iqueue_ops virtio_fs_fiq_ops = { static const struct fuse_iqueue_ops virtio_fs_fiq_ops = {
.wake_forget_and_unlock = virtio_fs_wake_forget_and_unlock, .wake_forget_and_unlock = virtio_fs_wake_forget_and_unlock,
.wake_interrupt_and_unlock = virtio_fs_wake_interrupt_and_unlock, .wake_interrupt_and_unlock = virtio_fs_wake_interrupt_and_unlock,
.wake_pending_and_unlock = virtio_fs_wake_pending_and_unlock, .wake_pending_and_unlock = virtio_fs_wake_pending_and_unlock,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment