whiterose

linux unikernel
Log | Files | Refs | README | LICENSE | git clone https://git.ne02ptzero.me/git/whiterose

commit 5f1ca5c619c2af5e03114b0276642cdc15d11a94
parent 49afe6614321e78727729680b97e96998695249a
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Fri, 30 Nov 2018 10:47:50 -0800

Merge branch 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull vfs fixes from Al Viro:
 "Assorted fixes all over the place.

  The iov_iter one is this cycle regression (splice from UDP triggering
  WARN_ON()), the rest is older"

* 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  afs: Use d_instantiate() rather than d_add() and don't d_drop()
  afs: Fix missing net error handling
  afs: Fix validation/callback interaction
  iov_iter: teach csum_and_copy_to_iter() to handle pipe-backed ones
  exportfs: do not read dentry after free
  exportfs: fix 'passing zero to ERR_PTR()' warning
  aio: fix failure to put the file pointer
  sysv: return 'err' instead of 0 in __sysv_write_inode

Diffstat:
Mfs/afs/dir.c | 4+---
Mfs/afs/fs_probe.c | 39++++++++++++++++++++++++---------------
Mfs/afs/inode.c | 18++++++++++++------
Mfs/afs/internal.h | 9+++++++++
Mfs/afs/misc.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Mfs/afs/rotate.c | 53+++++++++++++----------------------------------------
Mfs/afs/vl_probe.c | 45+++++++++++++++++++++++++++------------------
Mfs/afs/vl_rotate.c | 50++++++++++----------------------------------------
Mfs/aio.c | 1+
Mfs/exportfs/expfs.c | 3++-
Mfs/sysv/inode.c | 2+-
Mlib/iov_iter.c | 38+++++++++++++++++++++++++++++++++++++-
12 files changed, 189 insertions(+), 125 deletions(-)

diff --git a/fs/afs/dir.c b/fs/afs/dir.c @@ -1075,8 +1075,6 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc, if (fc->ac.error < 0) return; - d_drop(new_dentry); - inode = afs_iget(fc->vnode->vfs_inode.i_sb, fc->key, newfid, newstatus, newcb, fc->cbi); if (IS_ERR(inode)) { @@ -1090,7 +1088,7 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc, vnode = AFS_FS_I(inode); set_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags); afs_vnode_commit_status(fc, vnode, 0); - d_add(new_dentry, inode); + d_instantiate(new_dentry, inode); } /* diff --git a/fs/afs/fs_probe.c b/fs/afs/fs_probe.c @@ -61,8 +61,11 @@ void afs_fileserver_probe_result(struct afs_call *call) afs_io_error(call, afs_io_error_fs_probe_fail); goto out; case -ECONNRESET: /* Responded, but call expired. */ + case -ERFKILL: + case -EADDRNOTAVAIL: case -ENETUNREACH: case -EHOSTUNREACH: + case -EHOSTDOWN: case -ECONNREFUSED: case -ETIMEDOUT: case -ETIME: @@ -132,12 +135,14 @@ out: static int afs_do_probe_fileserver(struct afs_net *net, struct afs_server *server, struct key *key, - unsigned int server_index) + unsigned int server_index, + struct afs_error *_e) { struct afs_addr_cursor ac = { .index = 0, }; - int ret; + bool in_progress = false; + int err; _enter("%pU", &server->uuid); @@ -151,15 +156,17 @@ static int afs_do_probe_fileserver(struct afs_net *net, server->probe.rtt = UINT_MAX; for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) { - ret = afs_fs_get_capabilities(net, server, &ac, key, server_index, + err = afs_fs_get_capabilities(net, server, &ac, key, server_index, true); - if (ret != -EINPROGRESS) { - afs_fs_probe_done(server); - return ret; - } + if (err == -EINPROGRESS) + in_progress = true; + else + afs_prioritise_error(_e, err, ac.abort_code); } - return 0; + if (!in_progress) + afs_fs_probe_done(server); + return in_progress; } /* @@ -169,21 +176,23 @@ int afs_probe_fileservers(struct afs_net *net, struct key *key, struct afs_server_list *list) { struct afs_server *server; - int i, ret; + struct afs_error e; + bool in_progress = false; + int i; + e.error = 0; + e.responded = false; for (i = 0; i < list->nr_servers; i++) { server = list->servers[i].server; if (test_bit(AFS_SERVER_FL_PROBED, &server->flags)) continue; - if (!test_and_set_bit_lock(AFS_SERVER_FL_PROBING, &server->flags)) { - ret = afs_do_probe_fileserver(net, server, key, i); - if (ret) - return ret; - } + if (!test_and_set_bit_lock(AFS_SERVER_FL_PROBING, &server->flags) && + afs_do_probe_fileserver(net, server, key, i, &e)) + in_progress = true; } - return 0; + return in_progress ? 0 : e.error; } /* diff --git a/fs/afs/inode.c b/fs/afs/inode.c @@ -382,7 +382,7 @@ void afs_zap_data(struct afs_vnode *vnode) int afs_validate(struct afs_vnode *vnode, struct key *key) { time64_t now = ktime_get_real_seconds(); - bool valid = false; + bool valid; int ret; _enter("{v={%llx:%llu} fl=%lx},%x", @@ -402,15 +402,21 @@ int afs_validate(struct afs_vnode *vnode, struct key *key) vnode->cb_v_break = vnode->volume->cb_v_break; valid = false; } else if (vnode->status.type == AFS_FTYPE_DIR && - test_bit(AFS_VNODE_DIR_VALID, &vnode->flags) && - vnode->cb_expires_at - 10 > now) { - valid = true; - } else if (!test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) && - vnode->cb_expires_at - 10 > now) { + (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags) || + vnode->cb_expires_at - 10 <= now)) { + valid = false; + } else if (test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) || + vnode->cb_expires_at - 10 <= now) { + valid = false; + } else { valid = true; } } else if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { valid = true; + } else { + vnode->cb_s_break = vnode->cb_interest->server->cb_s_break; + vnode->cb_v_break = vnode->volume->cb_v_break; + valid = false; } read_sequnlock_excl(&vnode->cb_lock); diff --git a/fs/afs/internal.h b/fs/afs/internal.h @@ -696,6 +696,14 @@ struct afs_interface { }; /* + * Error prioritisation and accumulation. + */ +struct afs_error { + short error; /* Accumulated error */ + bool responded; /* T if server responded */ +}; + +/* * Cursor for iterating over a server's address list. */ struct afs_addr_cursor { @@ -1015,6 +1023,7 @@ static inline void __afs_stat(atomic_t *s) * misc.c */ extern int afs_abort_to_error(u32); +extern void afs_prioritise_error(struct afs_error *, int, u32); /* * mntpt.c diff --git a/fs/afs/misc.c b/fs/afs/misc.c @@ -118,3 +118,55 @@ int afs_abort_to_error(u32 abort_code) default: return -EREMOTEIO; } } + +/* + * Select the error to report from a set of errors. + */ +void afs_prioritise_error(struct afs_error *e, int error, u32 abort_code) +{ + switch (error) { + case 0: + return; + default: + if (e->error == -ETIMEDOUT || + e->error == -ETIME) + return; + case -ETIMEDOUT: + case -ETIME: + if (e->error == -ENOMEM || + e->error == -ENONET) + return; + case -ENOMEM: + case -ENONET: + if (e->error == -ERFKILL) + return; + case -ERFKILL: + if (e->error == -EADDRNOTAVAIL) + return; + case -EADDRNOTAVAIL: + if (e->error == -ENETUNREACH) + return; + case -ENETUNREACH: + if (e->error == -EHOSTUNREACH) + return; + case -EHOSTUNREACH: + if (e->error == -EHOSTDOWN) + return; + case -EHOSTDOWN: + if (e->error == -ECONNREFUSED) + return; + case -ECONNREFUSED: + if (e->error == -ECONNRESET) + return; + case -ECONNRESET: /* Responded, but call expired. */ + if (e->responded) + return; + e->error = error; + return; + + case -ECONNABORTED: + e->responded = true; + e->error = afs_abort_to_error(abort_code); + return; + } +} diff --git a/fs/afs/rotate.c b/fs/afs/rotate.c @@ -136,7 +136,8 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc) struct afs_addr_list *alist; struct afs_server *server; struct afs_vnode *vnode = fc->vnode; - u32 rtt, abort_code; + struct afs_error e; + u32 rtt; int error = fc->ac.error, i; _enter("%lx[%d],%lx[%d],%d,%d", @@ -306,8 +307,11 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc) if (fc->error != -EDESTADDRREQ) goto iterate_address; /* Fall through */ + case -ERFKILL: + case -EADDRNOTAVAIL: case -ENETUNREACH: case -EHOSTUNREACH: + case -EHOSTDOWN: case -ECONNREFUSED: _debug("no conn"); fc->error = error; @@ -446,50 +450,15 @@ no_more_servers: if (fc->flags & AFS_FS_CURSOR_VBUSY) goto restart_from_beginning; - abort_code = 0; - error = -EDESTADDRREQ; + e.error = -EDESTADDRREQ; + e.responded = false; for (i = 0; i < fc->server_list->nr_servers; i++) { struct afs_server *s = fc->server_list->servers[i].server; - int probe_error = READ_ONCE(s->probe.error); - switch (probe_error) { - case 0: - continue; - default: - if (error == -ETIMEDOUT || - error == -ETIME) - continue; - case -ETIMEDOUT: - case -ETIME: - if (error == -ENOMEM || - error == -ENONET) - continue; - case -ENOMEM: - case -ENONET: - if (error == -ENETUNREACH) - continue; - case -ENETUNREACH: - if (error == -EHOSTUNREACH) - continue; - case -EHOSTUNREACH: - if (error == -ECONNREFUSED) - continue; - case -ECONNREFUSED: - if (error == -ECONNRESET) - continue; - case -ECONNRESET: /* Responded, but call expired. */ - if (error == -ECONNABORTED) - continue; - case -ECONNABORTED: - abort_code = s->probe.abort_code; - error = probe_error; - continue; - } + afs_prioritise_error(&e, READ_ONCE(s->probe.error), + s->probe.abort_code); } - if (error == -ECONNABORTED) - error = afs_abort_to_error(abort_code); - failed_set_error: fc->error = error; failed: @@ -553,8 +522,11 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc) _leave(" = f [abort]"); return false; + case -ERFKILL: + case -EADDRNOTAVAIL: case -ENETUNREACH: case -EHOSTUNREACH: + case -EHOSTDOWN: case -ECONNREFUSED: case -ETIMEDOUT: case -ETIME: @@ -633,6 +605,7 @@ int afs_end_vnode_operation(struct afs_fs_cursor *fc) struct afs_net *net = afs_v2net(fc->vnode); if (fc->error == -EDESTADDRREQ || + fc->error == -EADDRNOTAVAIL || fc->error == -ENETUNREACH || fc->error == -EHOSTUNREACH) afs_dump_edestaddrreq(fc); diff --git a/fs/afs/vl_probe.c b/fs/afs/vl_probe.c @@ -61,8 +61,11 @@ void afs_vlserver_probe_result(struct afs_call *call) afs_io_error(call, afs_io_error_vl_probe_fail); goto out; case -ECONNRESET: /* Responded, but call expired. */ + case -ERFKILL: + case -EADDRNOTAVAIL: case -ENETUNREACH: case -EHOSTUNREACH: + case -EHOSTDOWN: case -ECONNREFUSED: case -ETIMEDOUT: case -ETIME: @@ -129,15 +132,17 @@ out: * Probe all of a vlserver's addresses to find out the best route and to * query its capabilities. */ -static int afs_do_probe_vlserver(struct afs_net *net, - struct afs_vlserver *server, - struct key *key, - unsigned int server_index) +static bool afs_do_probe_vlserver(struct afs_net *net, + struct afs_vlserver *server, + struct key *key, + unsigned int server_index, + struct afs_error *_e) { struct afs_addr_cursor ac = { .index = 0, }; - int ret; + bool in_progress = false; + int err; _enter("%s", server->name); @@ -151,15 +156,17 @@ static int afs_do_probe_vlserver(struct afs_net *net, server->probe.rtt = UINT_MAX; for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) { - ret = afs_vl_get_capabilities(net, &ac, key, server, + err = afs_vl_get_capabilities(net, &ac, key, server, server_index, true); - if (ret != -EINPROGRESS) { - afs_vl_probe_done(server); - return ret; - } + if (err == -EINPROGRESS) + in_progress = true; + else + afs_prioritise_error(_e, err, ac.abort_code); } - return 0; + if (!in_progress) + afs_vl_probe_done(server); + return in_progress; } /* @@ -169,21 +176,23 @@ int afs_send_vl_probes(struct afs_net *net, struct key *key, struct afs_vlserver_list *vllist) { struct afs_vlserver *server; - int i, ret; + struct afs_error e; + bool in_progress = false; + int i; + e.error = 0; + e.responded = false; for (i = 0; i < vllist->nr_servers; i++) { server = vllist->servers[i].server; if (test_bit(AFS_VLSERVER_FL_PROBED, &server->flags)) continue; - if (!test_and_set_bit_lock(AFS_VLSERVER_FL_PROBING, &server->flags)) { - ret = afs_do_probe_vlserver(net, server, key, i); - if (ret) - return ret; - } + if (!test_and_set_bit_lock(AFS_VLSERVER_FL_PROBING, &server->flags) && + afs_do_probe_vlserver(net, server, key, i, &e)) + in_progress = true; } - return 0; + return in_progress ? 0 : e.error; } /* diff --git a/fs/afs/vl_rotate.c b/fs/afs/vl_rotate.c @@ -71,8 +71,9 @@ bool afs_select_vlserver(struct afs_vl_cursor *vc) { struct afs_addr_list *alist; struct afs_vlserver *vlserver; + struct afs_error e; u32 rtt; - int error = vc->ac.error, abort_code, i; + int error = vc->ac.error, i; _enter("%lx[%d],%lx[%d],%d,%d", vc->untried, vc->index, @@ -119,8 +120,11 @@ bool afs_select_vlserver(struct afs_vl_cursor *vc) goto failed; } + case -ERFKILL: + case -EADDRNOTAVAIL: case -ENETUNREACH: case -EHOSTUNREACH: + case -EHOSTDOWN: case -ECONNREFUSED: case -ETIMEDOUT: case -ETIME: @@ -235,50 +239,15 @@ no_more_servers: if (vc->flags & AFS_VL_CURSOR_RETRY) goto restart_from_beginning; - abort_code = 0; - error = -EDESTADDRREQ; + e.error = -EDESTADDRREQ; + e.responded = false; for (i = 0; i < vc->server_list->nr_servers; i++) { struct afs_vlserver *s = vc->server_list->servers[i].server; - int probe_error = READ_ONCE(s->probe.error); - switch (probe_error) { - case 0: - continue; - default: - if (error == -ETIMEDOUT || - error == -ETIME) - continue; - case -ETIMEDOUT: - case -ETIME: - if (error == -ENOMEM || - error == -ENONET) - continue; - case -ENOMEM: - case -ENONET: - if (error == -ENETUNREACH) - continue; - case -ENETUNREACH: - if (error == -EHOSTUNREACH) - continue; - case -EHOSTUNREACH: - if (error == -ECONNREFUSED) - continue; - case -ECONNREFUSED: - if (error == -ECONNRESET) - continue; - case -ECONNRESET: /* Responded, but call expired. */ - if (error == -ECONNABORTED) - continue; - case -ECONNABORTED: - abort_code = s->probe.abort_code; - error = probe_error; - continue; - } + afs_prioritise_error(&e, READ_ONCE(s->probe.error), + s->probe.abort_code); } - if (error == -ECONNABORTED) - error = afs_abort_to_error(abort_code); - failed_set_error: vc->error = error; failed: @@ -341,6 +310,7 @@ int afs_end_vlserver_operation(struct afs_vl_cursor *vc) struct afs_net *net = vc->cell->net; if (vc->error == -EDESTADDRREQ || + vc->error == -EADDRNOTAVAIL || vc->error == -ENETUNREACH || vc->error == -EHOSTUNREACH) afs_vl_dump_edestaddrreq(vc); diff --git a/fs/aio.c b/fs/aio.c @@ -1436,6 +1436,7 @@ static int aio_prep_rw(struct kiocb *req, struct iocb *iocb) ret = ioprio_check_cap(iocb->aio_reqprio); if (ret) { pr_debug("aio ioprio check cap error: %d\n", ret); + fput(req->ki_filp); return ret; } diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c @@ -77,7 +77,7 @@ static bool dentry_connected(struct dentry *dentry) struct dentry *parent = dget_parent(dentry); dput(dentry); - if (IS_ROOT(dentry)) { + if (dentry == parent) { dput(parent); return false; } @@ -147,6 +147,7 @@ static struct dentry *reconnect_one(struct vfsmount *mnt, tmp = lookup_one_len_unlocked(nbuf, parent, strlen(nbuf)); if (IS_ERR(tmp)) { dprintk("%s: lookup failed: %d\n", __func__, PTR_ERR(tmp)); + err = PTR_ERR(tmp); goto out_err; } if (tmp != dentry) { diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c @@ -275,7 +275,7 @@ static int __sysv_write_inode(struct inode *inode, int wait) } } brelse(bh); - return 0; + return err; } int sysv_write_inode(struct inode *inode, struct writeback_control *wbc) diff --git a/lib/iov_iter.c b/lib/iov_iter.c @@ -560,6 +560,38 @@ static size_t copy_pipe_to_iter(const void *addr, size_t bytes, return bytes; } +static size_t csum_and_copy_to_pipe_iter(const void *addr, size_t bytes, + __wsum *csum, struct iov_iter *i) +{ + struct pipe_inode_info *pipe = i->pipe; + size_t n, r; + size_t off = 0; + __wsum sum = *csum, next; + int idx; + + if (!sanity(i)) + return 0; + + bytes = n = push_pipe(i, bytes, &idx, &r); + if (unlikely(!n)) + return 0; + for ( ; n; idx = next_idx(idx, pipe), r = 0) { + size_t chunk = min_t(size_t, n, PAGE_SIZE - r); + char *p = kmap_atomic(pipe->bufs[idx].page); + next = csum_partial_copy_nocheck(addr, p + r, chunk, 0); + sum = csum_block_add(sum, next, off); + kunmap_atomic(p); + i->idx = idx; + i->iov_offset = r + chunk; + n -= chunk; + off += chunk; + addr += chunk; + } + i->count -= bytes; + *csum = sum; + return bytes; +} + size_t _copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i) { const char *from = addr; @@ -1438,8 +1470,12 @@ size_t csum_and_copy_to_iter(const void *addr, size_t bytes, __wsum *csum, const char *from = addr; __wsum sum, next; size_t off = 0; + + if (unlikely(iov_iter_is_pipe(i))) + return csum_and_copy_to_pipe_iter(addr, bytes, csum, i); + sum = *csum; - if (unlikely(iov_iter_is_pipe(i) || iov_iter_is_discard(i))) { + if (unlikely(iov_iter_is_discard(i))) { WARN_ON(1); /* for now */ return 0; }