Merge tag 'ceph-for-5.20-rc1' of https://github.com/ceph/ceph-client
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 11 Aug 2022 19:41:07 +0000 (12:41 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 11 Aug 2022 19:41:07 +0000 (12:41 -0700)
Pull ceph updates from Ilya Dryomov:
 "We have a good pile of various fixes and cleanups from Xiubo, Jeff,
  Luis and others, almost exclusively in the filesystem.

  Several patches touch files outside of our normal purview to set the
  stage for bringing in Jeff's long awaited ceph+fscrypt series in the
  near future. All of them have appropriate acks and sat in linux-next
  for a while"

* tag 'ceph-for-5.20-rc1' of https://github.com/ceph/ceph-client: (27 commits)
  libceph: clean up ceph_osdc_start_request prototype
  libceph: fix ceph_pagelist_reserve() comment typo
  ceph: remove useless check for the folio
  ceph: don't truncate file in atomic_open
  ceph: make f_bsize always equal to f_frsize
  ceph: flush the dirty caps immediatelly when quota is approaching
  libceph: print fsid and epoch with osd id
  libceph: check pointer before assigned to "c->rules[]"
  ceph: don't get the inline data for new creating files
  ceph: update the auth cap when the async create req is forwarded
  ceph: make change_auth_cap_ses a global symbol
  ceph: fix incorrect old_size length in ceph_mds_request_args
  ceph: switch back to testing for NULL folio->private in ceph_dirty_folio
  ceph: call netfs_subreq_terminated with was_async == false
  ceph: convert to generic_file_llseek
  ceph: fix the incorrect comment for the ceph_mds_caps struct
  ceph: don't leak snap_rwsem in handle_cap_grant
  ceph: prevent a client from exceeding the MDS maximum xattr size
  ceph: choose auth MDS for getxattr with the Xs caps
  ceph: add session already open notify support
  ...

1  2 
drivers/block/rbd.c
fs/ceph/addr.c
fs/ceph/file.c
fs/crypto/fscrypt_private.h
fs/crypto/policy.c
fs/dcache.c
fs/inode.c
include/linux/dcache.h

diff --combined drivers/block/rbd.c
index 0d8ec2fe574001532dd0346588a5ac1a805df776,b4580b73479fe0a026d9125adf86d48633e3ecaf..f9e39301c4afa3f78b93298d4d27a7ef0f3cd2b5
@@@ -1297,7 -1297,7 +1297,7 @@@ static void rbd_osd_submit(struct ceph_
        dout("%s osd_req %p for obj_req %p objno %llu %llu~%llu\n",
             __func__, osd_req, obj_req, obj_req->ex.oe_objno,
             obj_req->ex.oe_off, obj_req->ex.oe_len);
-       ceph_osdc_start_request(osd_req->r_osdc, osd_req, false);
+       ceph_osdc_start_request(osd_req->r_osdc, osd_req);
  }
  
  /*
@@@ -2081,7 -2081,7 +2081,7 @@@ static int rbd_object_map_update(struc
        if (ret)
                return ret;
  
-       ceph_osdc_start_request(osdc, req, false);
+       ceph_osdc_start_request(osdc, req);
        return 0;
  }
  
@@@ -4729,7 -4729,7 +4729,7 @@@ static blk_status_t rbd_queue_rq(struc
  
  static void rbd_free_disk(struct rbd_device *rbd_dev)
  {
 -      blk_cleanup_disk(rbd_dev->disk);
 +      put_disk(rbd_dev->disk);
        blk_mq_free_tag_set(&rbd_dev->tag_set);
        rbd_dev->disk = NULL;
  }
@@@ -4768,7 -4768,7 +4768,7 @@@ static int rbd_obj_read_sync(struct rbd
        if (ret)
                goto out_req;
  
-       ceph_osdc_start_request(osdc, req, false);
+       ceph_osdc_start_request(osdc, req);
        ret = ceph_osdc_wait_request(osdc, req);
        if (ret >= 0)
                ceph_copy_from_page_vector(pages, buf, 0, ret);
diff --combined fs/ceph/addr.c
index 2c3a9b5b4b745f0a0adfad3e319d839bd6f6a2b2,ec76e77f8d4bfedf58d7c9e8020d5db891ad6d15..dcf701b05cc1c0e9b4a633bc2b3eb74f7148a32a
@@@ -122,7 -122,7 +122,7 @@@ static bool ceph_dirty_folio(struct add
         * Reference snap context in folio->private.  Also set
         * PagePrivate so that we get invalidate_folio callback.
         */
-       VM_BUG_ON_FOLIO(folio_test_private(folio), folio);
+       VM_WARN_ON_FOLIO(folio->private, folio);
        folio_attach_private(folio, snapc);
  
        return ceph_fscache_dirty_folio(mapping, folio);
@@@ -237,7 -237,7 +237,7 @@@ static void finish_netfs_read(struct ce
        if (err >= 0 && err < subreq->len)
                __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
  
-       netfs_subreq_terminated(subreq, err, true);
+       netfs_subreq_terminated(subreq, err, false);
  
        num_pages = calc_pages_for(osd_data->alignment, osd_data->length);
        ceph_put_page_vector(osd_data->pages, num_pages, false);
@@@ -313,8 -313,7 +313,7 @@@ static void ceph_netfs_issue_read(struc
        int err = 0;
        u64 len = subreq->len;
  
-       if (ci->i_inline_version != CEPH_INLINE_NONE &&
-           ceph_netfs_issue_op_inline(subreq))
+       if (ceph_has_inline_data(ci) && ceph_netfs_issue_op_inline(subreq))
                return;
  
        req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, vino, subreq->start, &len,
  
        dout("%s: pos=%llu orig_len=%zu len=%llu\n", __func__, subreq->start, subreq->len, len);
        iov_iter_xarray(&iter, READ, &rreq->mapping->i_pages, subreq->start, len);
 -      err = iov_iter_get_pages_alloc(&iter, &pages, len, &page_off);
 +      err = iov_iter_get_pages_alloc2(&iter, &pages, len, &page_off);
        if (err < 0) {
                dout("%s: iov_ter_get_pages_alloc returned %d\n", __func__, err);
                goto out;
        /* should always give us a page-aligned read */
        WARN_ON_ONCE(page_off);
        len = err;
+       err = 0;
  
        osd_req_op_extent_osd_data_pages(req, 0, pages, len, 0, false, false);
        req->r_callback = finish_netfs_read;
        req->r_inode = inode;
        ihold(inode);
  
-       err = ceph_osdc_start_request(req->r_osdc, req, false);
-       if (err)
-               iput(inode);
+       ceph_osdc_start_request(req->r_osdc, req);
  out:
        ceph_osdc_put_request(req);
        if (err)
@@@ -621,9 -619,8 +619,8 @@@ static int writepage_nounlock(struct pa
        dout("writepage %llu~%llu (%llu bytes)\n", page_off, len, len);
  
        req->r_mtime = inode->i_mtime;
-       err = ceph_osdc_start_request(osdc, req, true);
-       if (!err)
-               err = ceph_osdc_wait_request(osdc, req);
+       ceph_osdc_start_request(osdc, req);
+       err = ceph_osdc_wait_request(osdc, req);
  
        ceph_update_write_metrics(&fsc->mdsc->metric, req->r_start_latency,
                                  req->r_end_latency, len, err);
@@@ -1151,8 -1148,7 +1148,7 @@@ new_request
                }
  
                req->r_mtime = inode->i_mtime;
-               rc = ceph_osdc_start_request(&fsc->client->osdc, req, true);
-               BUG_ON(rc);
+               ceph_osdc_start_request(&fsc->client->osdc, req);
                req = NULL;
  
                wbc->nr_to_write -= i;
@@@ -1327,16 -1323,13 +1323,13 @@@ static int ceph_write_begin(struct fil
        int r;
  
        r = netfs_write_begin(&ci->netfs, file, inode->i_mapping, pos, len, &folio, NULL);
-       if (r == 0)
-               folio_wait_fscache(folio);
-       if (r < 0) {
-               if (folio)
-                       folio_put(folio);
-       } else {
-               WARN_ON_ONCE(!folio_test_locked(folio));
-               *pagep = &folio->page;
-       }
-       return r;
+       if (r < 0)
+               return r;
+       folio_wait_fscache(folio);
+       WARN_ON_ONCE(!folio_test_locked(folio));
+       *pagep = &folio->page;
+       return 0;
  }
  
  /*
@@@ -1439,7 -1432,7 +1432,7 @@@ static vm_fault_t ceph_filemap_fault(st
             inode, off, ceph_cap_string(got));
  
        if ((got & (CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_LAZYIO)) ||
-           ci->i_inline_version == CEPH_INLINE_NONE) {
+           !ceph_has_inline_data(ci)) {
                CEPH_DEFINE_RW_CONTEXT(rw_ctx, got);
                ceph_add_rw_context(fi, &rw_ctx);
                ret = filemap_fault(vmf);
@@@ -1696,9 -1689,8 +1689,8 @@@ int ceph_uninline_data(struct file *fil
        }
  
        req->r_mtime = inode->i_mtime;
-       err = ceph_osdc_start_request(&fsc->client->osdc, req, false);
-       if (!err)
-               err = ceph_osdc_wait_request(&fsc->client->osdc, req);
+       ceph_osdc_start_request(&fsc->client->osdc, req);
+       err = ceph_osdc_wait_request(&fsc->client->osdc, req);
        ceph_osdc_put_request(req);
        if (err < 0)
                goto out_unlock;
        }
  
        req->r_mtime = inode->i_mtime;
-       err = ceph_osdc_start_request(&fsc->client->osdc, req, false);
-       if (!err)
-               err = ceph_osdc_wait_request(&fsc->client->osdc, req);
+       ceph_osdc_start_request(&fsc->client->osdc, req);
+       err = ceph_osdc_wait_request(&fsc->client->osdc, req);
  
        ceph_update_write_metrics(&fsc->mdsc->metric, req->r_start_latency,
                                  req->r_end_latency, len, err);
@@@ -1912,15 -1903,13 +1903,13 @@@ static int __ceph_pool_perm_get(struct 
  
        osd_req_op_raw_data_in_pages(rd_req, 0, pages, PAGE_SIZE,
                                     0, false, true);
-       err = ceph_osdc_start_request(&fsc->client->osdc, rd_req, false);
+       ceph_osdc_start_request(&fsc->client->osdc, rd_req);
  
        wr_req->r_mtime = ci->netfs.inode.i_mtime;
-       err2 = ceph_osdc_start_request(&fsc->client->osdc, wr_req, false);
+       ceph_osdc_start_request(&fsc->client->osdc, wr_req);
  
-       if (!err)
-               err = ceph_osdc_wait_request(&fsc->client->osdc, rd_req);
-       if (!err2)
-               err2 = ceph_osdc_wait_request(&fsc->client->osdc, wr_req);
+       err = ceph_osdc_wait_request(&fsc->client->osdc, rd_req);
+       err2 = ceph_osdc_wait_request(&fsc->client->osdc, wr_req);
  
        if (err >= 0 || err == -ENOENT)
                have |= POOL_READ;
diff --combined fs/ceph/file.c
index 284d2fda663deb8b4a20c9edcfb03d2dd78bd7c8,c3caa9bf97557408373b2477bc82145189475491..04fd34557de84b49612dd94f5cbd1d748ee9921a
@@@ -95,11 -95,12 +95,11 @@@ static ssize_t __iter_get_bvecs(struct 
                size_t start;
                int idx = 0;
  
 -              bytes = iov_iter_get_pages(iter, pages, maxsize - size,
 +              bytes = iov_iter_get_pages2(iter, pages, maxsize - size,
                                           ITER_GET_BVECS_PAGES, &start);
                if (bytes < 0)
                        return size ?: bytes;
  
 -              iov_iter_advance(iter, bytes);
                size += bytes;
  
                for ( ; bytes; idx++, bvec_idx++) {
@@@ -240,8 -241,7 +240,7 @@@ static int ceph_init_file_info(struct i
        INIT_LIST_HEAD(&fi->rw_contexts);
        fi->filp_gen = READ_ONCE(ceph_inode_to_client(inode)->filp_gen);
  
-       if ((file->f_mode & FMODE_WRITE) &&
-           ci->i_inline_version != CEPH_INLINE_NONE) {
+       if ((file->f_mode & FMODE_WRITE) && ceph_has_inline_data(ci)) {
                ret = ceph_uninline_data(file);
                if (ret < 0)
                        goto error;
@@@ -568,7 -568,7 +567,7 @@@ static void ceph_async_create_cb(struc
                char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen,
                                                  &base, 0);
  
-               pr_warn("ceph: async create failure path=(%llx)%s result=%d!\n",
+               pr_warn("async create failure path=(%llx)%s result=%d!\n",
                        base, IS_ERR(path) ? "<<bad>>" : path, result);
                ceph_mdsc_free_path(path, pathlen);
  
@@@ -611,6 -611,7 +610,7 @@@ static int ceph_finish_async_create(str
        struct ceph_mds_reply_inode in = { };
        struct ceph_mds_reply_info_in iinfo = { .in = &in };
        struct ceph_inode_info *ci = ceph_inode(dir);
+       struct ceph_dentry_info *di = ceph_dentry(dentry);
        struct inode *inode;
        struct timespec64 now;
        struct ceph_string *pool_ns;
                /* Directories always inherit the setgid bit. */
                if (S_ISDIR(mode))
                        mode |= S_ISGID;
 -              else if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP) &&
 -                       !in_group_p(dir->i_gid) &&
 -                       !capable_wrt_inode_uidgid(&init_user_ns, dir, CAP_FSETID))
 -                      mode &= ~S_ISGID;
        } else {
                in.gid = cpu_to_le32(from_kgid(&init_user_ns, current_fsgid()));
        }
                file->f_mode |= FMODE_CREATED;
                ret = finish_open(file, dentry, ceph_open);
        }
+       spin_lock(&dentry->d_lock);
+       di->flags &= ~CEPH_DENTRY_ASYNC_CREATE;
+       wake_up_bit(&di->flags, CEPH_DENTRY_ASYNC_CREATE_BIT);
+       spin_unlock(&dentry->d_lock);
        return ret;
  }
  
@@@ -735,6 -746,15 +741,15 @@@ int ceph_atomic_open(struct inode *dir
        if (dentry->d_name.len > NAME_MAX)
                return -ENAMETOOLONG;
  
+       err = ceph_wait_on_conflict_unlink(dentry);
+       if (err)
+               return err;
+       /*
+        * Do not truncate the file, since atomic_open is called before the
+        * permission check. The caller will do the truncation afterward.
+        */
+       flags &= ~O_TRUNC;
        if (flags & O_CREAT) {
                if (ceph_quota_is_max_files_exceeded(dir))
                        return -EDQUOT;
@@@ -781,9 -801,16 +796,16 @@@ retry
                    (req->r_dir_caps =
                      try_prep_async_create(dir, dentry, &lo,
                                            &req->r_deleg_ino))) {
+                       struct ceph_dentry_info *di = ceph_dentry(dentry);
                        set_bit(CEPH_MDS_R_ASYNC, &req->r_req_flags);
                        req->r_args.open.flags |= cpu_to_le32(CEPH_O_EXCL);
                        req->r_callback = ceph_async_create_cb;
+                       spin_lock(&dentry->d_lock);
+                       di->flags |= CEPH_DENTRY_ASYNC_CREATE;
+                       spin_unlock(&dentry->d_lock);
                        err = ceph_mdsc_submit_request(mdsc, dir, req);
                        if (!err) {
                                err = ceph_finish_async_create(dir, dentry,
        }
  
        set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
-       err = ceph_mdsc_do_request(mdsc,
-                                  (flags & (O_CREAT|O_TRUNC)) ? dir : NULL,
-                                  req);
+       err = ceph_mdsc_do_request(mdsc, (flags & O_CREAT) ? dir : NULL, req);
        if (err == -ENOENT) {
                dentry = ceph_handle_snapdir(req, dentry);
                if (IS_ERR(dentry)) {
@@@ -960,9 -985,8 +980,8 @@@ static ssize_t ceph_sync_read(struct ki
  
                osd_req_op_extent_osd_data_pages(req, 0, pages, len, page_off,
                                                 false, false);
-               ret = ceph_osdc_start_request(osdc, req, false);
-               if (!ret)
-                       ret = ceph_osdc_wait_request(osdc, req);
+               ceph_osdc_start_request(osdc, req);
+               ret = ceph_osdc_wait_request(osdc, req);
  
                ceph_update_read_metrics(&fsc->mdsc->metric,
                                         req->r_start_latency,
@@@ -1225,7 -1249,7 +1244,7 @@@ static void ceph_aio_retry_work(struct 
        req->r_inode = inode;
        req->r_priv = aio_req;
  
-       ret = ceph_osdc_start_request(req->r_osdc, req, false);
+       ceph_osdc_start_request(req->r_osdc, req);
  out:
        if (ret < 0) {
                req->r_result = ret;
@@@ -1257,7 -1281,7 +1276,7 @@@ ceph_direct_read_write(struct kiocb *io
        size_t count = iov_iter_count(iter);
        loff_t pos = iocb->ki_pos;
        bool write = iov_iter_rw(iter) == WRITE;
 -      bool should_dirty = !write && iter_is_iovec(iter);
 +      bool should_dirty = !write && user_backed_iter(iter);
  
        if (write && ceph_snap(file_inode(file)) != CEPH_NOSNAP)
                return -EROFS;
                        continue;
                }
  
-               ret = ceph_osdc_start_request(req->r_osdc, req, false);
-               if (!ret)
-                       ret = ceph_osdc_wait_request(&fsc->client->osdc, req);
+               ceph_osdc_start_request(req->r_osdc, req);
+               ret = ceph_osdc_wait_request(&fsc->client->osdc, req);
  
                if (write)
                        ceph_update_write_metrics(metric, req->r_start_latency,
                                               r_private_item);
                        list_del_init(&req->r_private_item);
                        if (ret >= 0)
-                               ret = ceph_osdc_start_request(req->r_osdc,
-                                                             req, false);
+                               ceph_osdc_start_request(req->r_osdc, req);
                        if (ret < 0) {
                                req->r_result = ret;
                                ceph_aio_complete_req(req);
@@@ -1541,9 -1563,8 +1558,8 @@@ ceph_sync_write(struct kiocb *iocb, str
                                                false, true);
  
                req->r_mtime = mtime;
-               ret = ceph_osdc_start_request(&fsc->client->osdc, req, false);
-               if (!ret)
-                       ret = ceph_osdc_wait_request(&fsc->client->osdc, req);
+               ceph_osdc_start_request(&fsc->client->osdc, req);
+               ret = ceph_osdc_wait_request(&fsc->client->osdc, req);
  
                ceph_update_write_metrics(&fsc->mdsc->metric, req->r_start_latency,
                                          req->r_end_latency, len, ret);
@@@ -1627,7 -1648,7 +1643,7 @@@ again
                     inode, ceph_vinop(inode), iocb->ki_pos, (unsigned)len,
                     ceph_cap_string(got));
  
-               if (ci->i_inline_version == CEPH_INLINE_NONE) {
+               if (!ceph_has_inline_data(ci)) {
                        if (!retry_op && (iocb->ki_flags & IOCB_DIRECT)) {
                                ret = ceph_direct_read_write(iocb, to,
                                                             NULL, NULL);
@@@ -1890,7 -1911,7 +1906,7 @@@ retry_snap
                if (dirty)
                        __mark_inode_dirty(inode, dirty);
                if (ceph_quota_is_max_bytes_approaching(inode, iocb->ki_pos))
-                       ceph_check_caps(ci, 0, NULL);
+                       ceph_check_caps(ci, CHECK_CAPS_FLUSH, NULL);
        }
  
        dout("aio_write %p %llx.%llx %llu~%u  dropping cap refs on %s\n",
@@@ -1930,57 -1951,15 +1946,15 @@@ out_unlocked
   */
  static loff_t ceph_llseek(struct file *file, loff_t offset, int whence)
  {
-       struct inode *inode = file->f_mapping->host;
-       struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
-       loff_t i_size;
-       loff_t ret;
-       inode_lock(inode);
        if (whence == SEEK_END || whence == SEEK_DATA || whence == SEEK_HOLE) {
+               struct inode *inode = file_inode(file);
+               int ret;
                ret = ceph_do_getattr(inode, CEPH_STAT_CAP_SIZE, false);
                if (ret < 0)
-                       goto out;
-       }
-       i_size = i_size_read(inode);
-       switch (whence) {
-       case SEEK_END:
-               offset += i_size;
-               break;
-       case SEEK_CUR:
-               /*
-                * Here we special-case the lseek(fd, 0, SEEK_CUR)
-                * position-querying operation.  Avoid rewriting the "same"
-                * f_pos value back to the file because a concurrent read(),
-                * write() or lseek() might have altered it
-                */
-               if (offset == 0) {
-                       ret = file->f_pos;
-                       goto out;
-               }
-               offset += file->f_pos;
-               break;
-       case SEEK_DATA:
-               if (offset < 0 || offset >= i_size) {
-                       ret = -ENXIO;
-                       goto out;
-               }
-               break;
-       case SEEK_HOLE:
-               if (offset < 0 || offset >= i_size) {
-                       ret = -ENXIO;
-                       goto out;
-               }
-               offset = i_size;
-               break;
+                       return ret;
        }
-       ret = vfs_setpos(file, offset, max(i_size, fsc->max_file_size));
- out:
-       inode_unlock(inode);
-       return ret;
+       return generic_file_llseek(file, offset, whence);
  }
  
  static inline void ceph_zero_partial_page(
@@@ -2049,12 -2028,10 +2023,10 @@@ static int ceph_zero_partial_object(str
        }
  
        req->r_mtime = inode->i_mtime;
-       ret = ceph_osdc_start_request(&fsc->client->osdc, req, false);
-       if (!ret) {
-               ret = ceph_osdc_wait_request(&fsc->client->osdc, req);
-               if (ret == -ENOENT)
-                       ret = 0;
-       }
+       ceph_osdc_start_request(&fsc->client->osdc, req);
+       ret = ceph_osdc_wait_request(&fsc->client->osdc, req);
+       if (ret == -ENOENT)
+               ret = 0;
        ceph_osdc_put_request(req);
  
  out:
@@@ -2356,7 -2333,7 +2328,7 @@@ static ssize_t ceph_do_objects_copy(str
                if (IS_ERR(req))
                        ret = PTR_ERR(req);
                else {
-                       ceph_osdc_start_request(osdc, req, false);
+                       ceph_osdc_start_request(osdc, req);
                        ret = ceph_osdc_wait_request(osdc, req);
                        ceph_update_copyfrom_metrics(&fsc->mdsc->metric,
                                                     req->r_start_latency,
@@@ -2549,7 -2526,8 +2521,8 @@@ static ssize_t __ceph_copy_file_range(s
                /* Let the MDS know about dst file size change */
                if (ceph_inode_set_size(dst_inode, dst_off) ||
                    ceph_quota_is_max_bytes_approaching(dst_inode, dst_off))
-                       ceph_check_caps(dst_ci, CHECK_CAPS_AUTHONLY, NULL);
+                       ceph_check_caps(dst_ci, CHECK_CAPS_AUTHONLY | CHECK_CAPS_FLUSH,
+                                       NULL);
        }
        /* Mark Fw dirty */
        spin_lock(&dst_ci->i_ceph_lock);
index f5be777d82795afd32ad69be183339750cf2e91d,11fe9d213ae14dee5150106fb4d9b24720bc1125..3afdaa0847736b1d28b50cc4417db864c787d4ca
@@@ -31,7 -31,7 +31,7 @@@
  #define FSCRYPT_CONTEXT_V2    2
  
  /* Keep this in sync with include/uapi/linux/fscrypt.h */
 -#define FSCRYPT_MODE_MAX      FSCRYPT_MODE_ADIANTUM
 +#define FSCRYPT_MODE_MAX      FSCRYPT_MODE_AES_256_HCTR2
  
  struct fscrypt_context_v1 {
        u8 version; /* FSCRYPT_CONTEXT_V1 */
@@@ -297,14 -297,11 +297,11 @@@ void fscrypt_generate_iv(union fscrypt_
                         const struct fscrypt_info *ci);
  
  /* fname.c */
- int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
-                         u8 *out, unsigned int olen);
- bool fscrypt_fname_encrypted_size(const union fscrypt_policy *policy,
-                                 u32 orig_len, u32 max_len,
-                                 u32 *encrypted_len_ret);
+ bool __fscrypt_fname_encrypted_size(const union fscrypt_policy *policy,
+                                   u32 orig_len, u32 max_len,
+                                   u32 *encrypted_len_ret);
  
  /* hkdf.c */
  struct fscrypt_hkdf {
        struct crypto_shash *hmac_tfm;
  };
diff --combined fs/crypto/policy.c
index 8a054e6d1e68774a6dbe17938e1ec33aae46b71b,a450189565e3291ae59bd58f5e0e2edee715475b..80b8ca0f340b29db71f62cbb2fec85b96618e451
@@@ -61,7 -61,7 +61,7 @@@ fscrypt_get_dummy_policy(struct super_b
        return sb->s_cop->get_dummy_policy(sb);
  }
  
 -static bool fscrypt_valid_enc_modes(u32 contents_mode, u32 filenames_mode)
 +static bool fscrypt_valid_enc_modes_v1(u32 contents_mode, u32 filenames_mode)
  {
        if (contents_mode == FSCRYPT_MODE_AES_256_XTS &&
            filenames_mode == FSCRYPT_MODE_AES_256_CTS)
        return false;
  }
  
 +static bool fscrypt_valid_enc_modes_v2(u32 contents_mode, u32 filenames_mode)
 +{
 +      if (contents_mode == FSCRYPT_MODE_AES_256_XTS &&
 +          filenames_mode == FSCRYPT_MODE_AES_256_HCTR2)
 +              return true;
 +      return fscrypt_valid_enc_modes_v1(contents_mode, filenames_mode);
 +}
 +
  static bool supported_direct_key_modes(const struct inode *inode,
                                       u32 contents_mode, u32 filenames_mode)
  {
@@@ -159,7 -151,7 +159,7 @@@ static bool supported_iv_ino_lblk_polic
  static bool fscrypt_supported_v1_policy(const struct fscrypt_policy_v1 *policy,
                                        const struct inode *inode)
  {
 -      if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode,
 +      if (!fscrypt_valid_enc_modes_v1(policy->contents_encryption_mode,
                                     policy->filenames_encryption_mode)) {
                fscrypt_warn(inode,
                             "Unsupported encryption modes (contents %d, filenames %d)",
@@@ -195,7 -187,7 +195,7 @@@ static bool fscrypt_supported_v2_policy
  {
        int count = 0;
  
 -      if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode,
 +      if (!fscrypt_valid_enc_modes_v2(policy->contents_encryption_mode,
                                     policy->filenames_encryption_mode)) {
                fscrypt_warn(inode,
                             "Unsupported encryption modes (contents %d, filenames %d)",
@@@ -693,6 -685,32 +693,32 @@@ const union fscrypt_policy *fscrypt_pol
        return fscrypt_get_dummy_policy(dir->i_sb);
  }
  
+ /**
+  * fscrypt_context_for_new_inode() - create an encryption context for a new inode
+  * @ctx: where context should be written
+  * @inode: inode from which to fetch policy and nonce
+  *
+  * Given an in-core "prepared" (via fscrypt_prepare_new_inode) inode,
+  * generate a new context and write it to ctx. ctx _must_ be at least
+  * FSCRYPT_SET_CONTEXT_MAX_SIZE bytes.
+  *
+  * Return: size of the resulting context or a negative error code.
+  */
+ int fscrypt_context_for_new_inode(void *ctx, struct inode *inode)
+ {
+       struct fscrypt_info *ci = inode->i_crypt_info;
+       BUILD_BUG_ON(sizeof(union fscrypt_context) !=
+                       FSCRYPT_SET_CONTEXT_MAX_SIZE);
+       /* fscrypt_prepare_new_inode() should have set up the key already. */
+       if (WARN_ON_ONCE(!ci))
+               return -ENOKEY;
+       return fscrypt_new_context(ctx, &ci->ci_policy, ci->ci_nonce);
+ }
+ EXPORT_SYMBOL_GPL(fscrypt_context_for_new_inode);
  /**
   * fscrypt_set_context() - Set the fscrypt context of a new inode
   * @inode: a new inode
@@@ -709,12 -727,9 +735,9 @@@ int fscrypt_set_context(struct inode *i
        union fscrypt_context ctx;
        int ctxsize;
  
-       /* fscrypt_prepare_new_inode() should have set up the key already. */
-       if (WARN_ON_ONCE(!ci))
-               return -ENOKEY;
-       BUILD_BUG_ON(sizeof(ctx) != FSCRYPT_SET_CONTEXT_MAX_SIZE);
-       ctxsize = fscrypt_new_context(&ctx, &ci->ci_policy, ci->ci_nonce);
+       ctxsize = fscrypt_context_for_new_inode(&ctx, inode);
+       if (ctxsize < 0)
+               return ctxsize;
  
        /*
         * This may be the first time the inode number is available, so do any
diff --combined fs/dcache.c
index ea5cdec24ea75f831d918c8f74beeed847c4adfe,a409312ee0dfa8e376636c3b9ecc60a023659e12..c5dc32a59c7699107c233034254d890e8b806dde
@@@ -2240,7 -2240,6 +2240,7 @@@ struct dentry *d_add_ci(struct dentry *
        }
        res = d_splice_alias(inode, found);
        if (res) {
 +              d_lookup_done(found);
                dput(found);
                return res;
        }
  }
  EXPORT_SYMBOL(d_add_ci);
  
- static inline bool d_same_name(const struct dentry *dentry,
-                               const struct dentry *parent,
-                               const struct qstr *name)
+ /**
+  * d_same_name - compare dentry name with case-exact name
+  * @parent: parent dentry
+  * @dentry: the negative dentry that was passed to the parent's lookup func
+  * @name:   the case-exact name to be associated with the returned dentry
+  *
+  * Return: true if names are same, or false
+  */
+ bool d_same_name(const struct dentry *dentry, const struct dentry *parent,
+                const struct qstr *name)
  {
        if (likely(!(parent->d_flags & DCACHE_OP_COMPARE))) {
                if (dentry->d_name.len != name->len)
                                       dentry->d_name.len, dentry->d_name.name,
                                       name) == 0;
  }
+ EXPORT_SYMBOL_GPL(d_same_name);
  
  /**
   * __d_lookup_rcu - search for a dentry (racy, store-free)
@@@ -2564,15 -2570,7 +2571,15 @@@ EXPORT_SYMBOL(d_rehash)
  
  static inline unsigned start_dir_add(struct inode *dir)
  {
 -
 +      /*
 +       * The caller holds a spinlock (dentry::d_lock). On !PREEMPT_RT
 +       * kernels spin_lock() implicitly disables preemption, but not on
 +       * PREEMPT_RT.  So for RT it has to be done explicitly to protect
 +       * the sequence count write side critical section against a reader
 +       * or another writer preempting, which would result in a live lock.
 +       */
 +      if (IS_ENABLED(CONFIG_PREEMPT_RT))
 +              preempt_disable();
        for (;;) {
                unsigned n = dir->i_dir_seq;
                if (!(n & 1) && cmpxchg(&dir->i_dir_seq, n, n + 1) == n)
        }
  }
  
 -static inline void end_dir_add(struct inode *dir, unsigned n)
 +static inline void end_dir_add(struct inode *dir, unsigned int n,
 +                             wait_queue_head_t *d_wait)
  {
        smp_store_release(&dir->i_dir_seq, n + 2);
 +      if (IS_ENABLED(CONFIG_PREEMPT_RT))
 +              preempt_enable();
 +      wake_up_all(d_wait);
  }
  
  static void d_wait_lookup(struct dentry *dentry)
@@@ -2714,50 -2708,32 +2721,50 @@@ mismatch
  }
  EXPORT_SYMBOL(d_alloc_parallel);
  
 -void __d_lookup_done(struct dentry *dentry)
 +/*
 + * - Unhash the dentry
 + * - Retrieve and clear the waitqueue head in dentry
 + * - Return the waitqueue head
 + */
 +static wait_queue_head_t *__d_lookup_unhash(struct dentry *dentry)
  {
 -      struct hlist_bl_head *b = in_lookup_hash(dentry->d_parent,
 -                                               dentry->d_name.hash);
 +      wait_queue_head_t *d_wait;
 +      struct hlist_bl_head *b;
 +
 +      lockdep_assert_held(&dentry->d_lock);
 +
 +      b = in_lookup_hash(dentry->d_parent, dentry->d_name.hash);
        hlist_bl_lock(b);
        dentry->d_flags &= ~DCACHE_PAR_LOOKUP;
        __hlist_bl_del(&dentry->d_u.d_in_lookup_hash);
 -      wake_up_all(dentry->d_wait);
 +      d_wait = dentry->d_wait;
        dentry->d_wait = NULL;
        hlist_bl_unlock(b);
        INIT_HLIST_NODE(&dentry->d_u.d_alias);
        INIT_LIST_HEAD(&dentry->d_lru);
 +      return d_wait;
 +}
 +
 +void __d_lookup_unhash_wake(struct dentry *dentry)
 +{
 +      spin_lock(&dentry->d_lock);
 +      wake_up_all(__d_lookup_unhash(dentry));
 +      spin_unlock(&dentry->d_lock);
  }
 -EXPORT_SYMBOL(__d_lookup_done);
 +EXPORT_SYMBOL(__d_lookup_unhash_wake);
  
  /* inode->i_lock held if inode is non-NULL */
  
  static inline void __d_add(struct dentry *dentry, struct inode *inode)
  {
 +      wait_queue_head_t *d_wait;
        struct inode *dir = NULL;
        unsigned n;
        spin_lock(&dentry->d_lock);
        if (unlikely(d_in_lookup(dentry))) {
                dir = dentry->d_parent->d_inode;
                n = start_dir_add(dir);
 -              __d_lookup_done(dentry);
 +              d_wait = __d_lookup_unhash(dentry);
        }
        if (inode) {
                unsigned add_flags = d_flags_for_inode(inode);
        }
        __d_rehash(dentry);
        if (dir)
 -              end_dir_add(dir, n);
 +              end_dir_add(dir, n, d_wait);
        spin_unlock(&dentry->d_lock);
        if (inode)
                spin_unlock(&inode->i_lock);
@@@ -2916,7 -2892,6 +2923,7 @@@ static void __d_move(struct dentry *den
                     bool exchange)
  {
        struct dentry *old_parent, *p;
 +      wait_queue_head_t *d_wait;
        struct inode *dir = NULL;
        unsigned n;
  
        if (unlikely(d_in_lookup(target))) {
                dir = target->d_parent->d_inode;
                n = start_dir_add(dir);
 -              __d_lookup_done(target);
 +              d_wait = __d_lookup_unhash(target);
        }
  
        write_seqcount_begin(&dentry->d_seq);
        write_seqcount_end(&dentry->d_seq);
  
        if (dir)
 -              end_dir_add(dir, n);
 +              end_dir_add(dir, n, d_wait);
  
        if (dentry->d_parent != old_parent)
                spin_unlock(&dentry->d_parent->d_lock);
diff --combined fs/inode.c
index 9c3cd540c665c608a7318f02c30483ab9cfa5af8,d5db55df442b782340c9f5bf75bdab283b9b456d..6462276dfdf04d9ce648b7a2f4a06faa727d9de0
@@@ -422,6 -422,7 +422,7 @@@ void inode_init_once(struct inode *inod
        INIT_LIST_HEAD(&inode->i_io_list);
        INIT_LIST_HEAD(&inode->i_wb_list);
        INIT_LIST_HEAD(&inode->i_lru);
+       INIT_LIST_HEAD(&inode->i_sb_list);
        __address_space_init_once(&inode->i_data);
        i_size_ordered_init(inode);
  }
@@@ -604,7 -605,7 +605,7 @@@ void clear_inode(struct inode *inode
  {
        /*
         * We have to cycle the i_pages lock here because reclaim can be in the
 -       * process of removing the last page (in __delete_from_page_cache())
 +       * process of removing the last page (in __filemap_remove_folio())
         * and we must not free the mapping under it.
         */
        xa_lock_irq(&inode->i_data.i_pages);
@@@ -1021,7 -1022,6 +1022,6 @@@ struct inode *new_inode_pseudo(struct s
                spin_lock(&inode->i_lock);
                inode->i_state = 0;
                spin_unlock(&inode->i_lock);
-               INIT_LIST_HEAD(&inode->i_sb_list);
        }
        return inode;
  }
@@@ -1165,7 -1165,6 +1165,6 @@@ struct inode *inode_insert5(struct inod
  {
        struct hlist_head *head = inode_hashtable + hash(inode->i_sb, hashval);
        struct inode *old;
-       bool creating = inode->i_state & I_CREATING;
  
  again:
        spin_lock(&inode_hash_lock);
        inode->i_state |= I_NEW;
        hlist_add_head_rcu(&inode->i_hash, head);
        spin_unlock(&inode->i_lock);
-       if (!creating)
+       /*
+        * Add inode to the sb list if it's not already. It has I_NEW at this
+        * point, so it should be safe to test i_sb_list locklessly.
+        */
+       if (list_empty(&inode->i_sb_list))
                inode_sb_list_add(inode);
  unlock:
        spin_unlock(&inode_hash_lock);
@@@ -2010,57 -2014,67 +2014,57 @@@ static int __remove_privs(struct user_n
        return notify_change(mnt_userns, dentry, &newattrs, NULL);
  }
  
 -/*
 - * Remove special file priviledges (suid, capabilities) when file is written
 - * to or truncated.
 - */
 -int file_remove_privs(struct file *file)
 +static int __file_remove_privs(struct file *file, unsigned int flags)
  {
        struct dentry *dentry = file_dentry(file);
        struct inode *inode = file_inode(file);
 +      int error;
        int kill;
 -      int error = 0;
  
 -      /*
 -       * Fast path for nothing security related.
 -       * As well for non-regular files, e.g. blkdev inodes.
 -       * For example, blkdev_write_iter() might get here
 -       * trying to remove privs which it is not allowed to.
 -       */
        if (IS_NOSEC(inode) || !S_ISREG(inode->i_mode))
                return 0;
  
        kill = dentry_needs_remove_privs(dentry);
 -      if (kill < 0)
 +      if (kill <= 0)
                return kill;
 -      if (kill)
 -              error = __remove_privs(file_mnt_user_ns(file), dentry, kill);
 +
 +      if (flags & IOCB_NOWAIT)
 +              return -EAGAIN;
 +
 +      error = __remove_privs(file_mnt_user_ns(file), dentry, kill);
        if (!error)
                inode_has_no_xattr(inode);
  
        return error;
  }
 -EXPORT_SYMBOL(file_remove_privs);
  
  /**
 - *    file_update_time        -       update mtime and ctime time
 - *    @file: file accessed
 + * file_remove_privs - remove special file privileges (suid, capabilities)
 + * @file: file to remove privileges from
 + *
 + * When file is modified by a write or truncation ensure that special
 + * file privileges are removed.
   *
 - *    Update the mtime and ctime members of an inode and mark the inode
 - *    for writeback.  Note that this function is meant exclusively for
 - *    usage in the file write path of filesystems, and filesystems may
 - *    choose to explicitly ignore update via this function with the
 - *    S_NOCMTIME inode flag, e.g. for network filesystem where these
 - *    timestamps are handled by the server.  This can return an error for
 - *    file systems who need to allocate space in order to update an inode.
 + * Return: 0 on success, negative errno on failure.
   */
 +int file_remove_privs(struct file *file)
 +{
 +      return __file_remove_privs(file, 0);
 +}
 +EXPORT_SYMBOL(file_remove_privs);
  
 -int file_update_time(struct file *file)
 +static int inode_needs_update_time(struct inode *inode, struct timespec64 *now)
  {
 -      struct inode *inode = file_inode(file);
 -      struct timespec64 now;
        int sync_it = 0;
 -      int ret;
  
        /* First try to exhaust all avenues to not sync */
        if (IS_NOCMTIME(inode))
                return 0;
  
 -      now = current_time(inode);
 -      if (!timespec64_equal(&inode->i_mtime, &now))
 +      if (!timespec64_equal(&inode->i_mtime, now))
                sync_it = S_MTIME;
  
 -      if (!timespec64_equal(&inode->i_ctime, &now))
 +      if (!timespec64_equal(&inode->i_ctime, now))
                sync_it |= S_CTIME;
  
        if (IS_I_VERSION(inode) && inode_iversion_need_inc(inode))
        if (!sync_it)
                return 0;
  
 -      /* Finally allowed to write? Takes lock. */
 -      if (__mnt_want_write_file(file))
 -              return 0;
 +      return sync_it;
 +}
  
 -      ret = inode_update_time(inode, &now, sync_it);
 -      __mnt_drop_write_file(file);
 +static int __file_update_time(struct file *file, struct timespec64 *now,
 +                      int sync_mode)
 +{
 +      int ret = 0;
 +      struct inode *inode = file_inode(file);
 +
 +      /* try to update time settings */
 +      if (!__mnt_want_write_file(file)) {
 +              ret = inode_update_time(inode, now, sync_mode);
 +              __mnt_drop_write_file(file);
 +      }
  
        return ret;
  }
 +
 +/**
 + * file_update_time - update mtime and ctime time
 + * @file: file accessed
 + *
 + * Update the mtime and ctime members of an inode and mark the inode for
 + * writeback. Note that this function is meant exclusively for usage in
 + * the file write path of filesystems, and filesystems may choose to
 + * explicitly ignore updates via this function with the _NOCMTIME inode
 + * flag, e.g. for network filesystem where these imestamps are handled
 + * by the server. This can return an error for file systems who need to
 + * allocate space in order to update an inode.
 + *
 + * Return: 0 on success, negative errno on failure.
 + */
 +int file_update_time(struct file *file)
 +{
 +      int ret;
 +      struct inode *inode = file_inode(file);
 +      struct timespec64 now = current_time(inode);
 +
 +      ret = inode_needs_update_time(inode, &now);
 +      if (ret <= 0)
 +              return ret;
 +
 +      return __file_update_time(file, &now, ret);
 +}
  EXPORT_SYMBOL(file_update_time);
  
 -/* Caller must hold the file's inode lock */
 -int file_modified(struct file *file)
 +/**
 + * file_modified_flags - handle mandated vfs changes when modifying a file
 + * @file: file that was modified
 + * @flags: kiocb flags
 + *
 + * When file has been modified ensure that special
 + * file privileges are removed and time settings are updated.
 + *
 + * If IOCB_NOWAIT is set, special file privileges will not be removed and
 + * time settings will not be updated. It will return -EAGAIN.
 + *
 + * Context: Caller must hold the file's inode lock.
 + *
 + * Return: 0 on success, negative errno on failure.
 + */
 +static int file_modified_flags(struct file *file, int flags)
  {
 -      int err;
 +      int ret;
 +      struct inode *inode = file_inode(file);
 +      struct timespec64 now = current_time(inode);
  
        /*
         * Clear the security bits if the process is not being run by root.
         * This keeps people from modifying setuid and setgid binaries.
         */
 -      err = file_remove_privs(file);
 -      if (err)
 -              return err;
 +      ret = __file_remove_privs(file, flags);
 +      if (ret)
 +              return ret;
  
        if (unlikely(file->f_mode & FMODE_NOCMTIME))
                return 0;
  
 -      return file_update_time(file);
 +      ret = inode_needs_update_time(inode, &now);
 +      if (ret <= 0)
 +              return ret;
 +      if (flags & IOCB_NOWAIT)
 +              return -EAGAIN;
 +
 +      return __file_update_time(file, &now, ret);
 +}
 +
 +/**
 + * file_modified - handle mandated vfs changes when modifying a file
 + * @file: file that was modified
 + *
 + * When file has been modified ensure that special
 + * file privileges are removed and time settings are updated.
 + *
 + * Context: Caller must hold the file's inode lock.
 + *
 + * Return: 0 on success, negative errno on failure.
 + */
 +int file_modified(struct file *file)
 +{
 +      return file_modified_flags(file, 0);
  }
  EXPORT_SYMBOL(file_modified);
  
 +/**
 + * kiocb_modified - handle mandated vfs changes when modifying a file
 + * @iocb: iocb that was modified
 + *
 + * When file has been modified ensure that special
 + * file privileges are removed and time settings are updated.
 + *
 + * Context: Caller must hold the file's inode lock.
 + *
 + * Return: 0 on success, negative errno on failure.
 + */
 +int kiocb_modified(struct kiocb *iocb)
 +{
 +      return file_modified_flags(iocb->ki_filp, iocb->ki_flags);
 +}
 +EXPORT_SYMBOL_GPL(kiocb_modified);
 +
  int inode_needs_sync(struct inode *inode)
  {
        if (IS_SYNC(inode))
@@@ -2326,6 -2250,10 +2330,6 @@@ void inode_init_owner(struct user_names
                /* Directories are special, and always inherit S_ISGID */
                if (S_ISDIR(mode))
                        mode |= S_ISGID;
 -              else if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP) &&
 -                       !in_group_p(i_gid_into_mnt(mnt_userns, dir)) &&
 -                       !capable_wrt_inode_uidgid(mnt_userns, dir, CAP_FSETID))
 -                      mode &= ~S_ISGID;
        } else
                inode_fsgid_set(inode, mnt_userns);
        inode->i_mode = mode;
@@@ -2481,33 -2409,3 +2485,33 @@@ struct timespec64 current_time(struct i
        return timestamp_truncate(now, inode);
  }
  EXPORT_SYMBOL(current_time);
 +
 +/**
 + * mode_strip_sgid - handle the sgid bit for non-directories
 + * @mnt_userns: User namespace of the mount the inode was created from
 + * @dir: parent directory inode
 + * @mode: mode of the file to be created in @dir
 + *
 + * If the @mode of the new file has both the S_ISGID and S_IXGRP bit
 + * raised and @dir has the S_ISGID bit raised ensure that the caller is
 + * either in the group of the parent directory or they have CAP_FSETID
 + * in their user namespace and are privileged over the parent directory.
 + * In all other cases, strip the S_ISGID bit from @mode.
 + *
 + * Return: the new mode to use for the file
 + */
 +umode_t mode_strip_sgid(struct user_namespace *mnt_userns,
 +                      const struct inode *dir, umode_t mode)
 +{
 +      if ((mode & (S_ISGID | S_IXGRP)) != (S_ISGID | S_IXGRP))
 +              return mode;
 +      if (S_ISDIR(mode) || !dir || !(dir->i_mode & S_ISGID))
 +              return mode;
 +      if (in_group_p(i_gid_into_mnt(mnt_userns, dir)))
 +              return mode;
 +      if (capable_wrt_inode_uidgid(mnt_userns, dir, CAP_FSETID))
 +              return mode;
 +
 +      return mode & ~S_ISGID;
 +}
 +EXPORT_SYMBOL(mode_strip_sgid);
diff --combined include/linux/dcache.h
index c73e5e327e76f861da3f94531ce0326f0a365f94,bb72361834def225788eb8ce8c860c7ccdabccf2..92c78ed02b54d5bf59c9f5c7506dcf0c6afc1512
@@@ -233,6 -233,8 +233,8 @@@ extern struct dentry * d_alloc_parallel
                                        wait_queue_head_t *);
  extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
  extern struct dentry * d_add_ci(struct dentry *, struct inode *, struct qstr *);
+ extern bool d_same_name(const struct dentry *dentry, const struct dentry *parent,
+                       const struct qstr *name);
  extern struct dentry * d_exact_alias(struct dentry *, struct inode *);
  extern struct dentry *d_find_any_alias(struct inode *inode);
  extern struct dentry * d_obtain_alias(struct inode *);
@@@ -349,7 -351,7 +351,7 @@@ static inline void dont_mount(struct de
        spin_unlock(&dentry->d_lock);
  }
  
 -extern void __d_lookup_done(struct dentry *);
 +extern void __d_lookup_unhash_wake(struct dentry *dentry);
  
  static inline int d_in_lookup(const struct dentry *dentry)
  {
  
  static inline void d_lookup_done(struct dentry *dentry)
  {
 -      if (unlikely(d_in_lookup(dentry))) {
 -              spin_lock(&dentry->d_lock);
 -              __d_lookup_done(dentry);
 -              spin_unlock(&dentry->d_lock);
 -      }
 +      if (unlikely(d_in_lookup(dentry)))
 +              __d_lookup_unhash_wake(dentry);
  }
  
  extern void dput(struct dentry *);