Whamcloud - gitweb
LU-11832 ldiskfs: properly handle VFS parallel locking 14/34714/30
authorJames Simmons <jsimmons@infradead.org>
Thu, 21 Nov 2019 22:15:10 +0000 (17:15 -0500)
committerOleg Drokin <green@whamcloud.com>
Sat, 14 Dec 2019 05:56:51 +0000 (05:56 +0000)
commit41dd393ffbd94877dad411cfd66105379258b579
treed83a1db27517efd83778dbe7be2d318f1b0fd6ed
parente5e584fd386a2229809bc64d440c3255cf50c1bd
LU-11832 ldiskfs: properly handle VFS parallel locking

The lustre server stack has an OSD abstraction to treat the under
lying native file system as an object store. In this case it is
ext4 being used to store internal data the OSD abstraction uses.

The area the recent VFS parallel locking changes impacts us is in
the LFSCK scrubbing code. The method the scrubbing code uses to
ensure a healthy state for the OSD data that is stored is by
using the native file system backend directory scanning code which
is setup to pass in unique function hook to handle each OSD
object store data file.

Currently this starts with the scrub code calling code that resembles
the internals of iterate_dir() which in turn calls ext4_readdir() with
struct dir_context actor mapping to each unique callback defined
by the field olm_filldir of struct osd_lf_map. The inode_lock()
and inode_unlock() was called with osd_lookup_one_len_unlocked()
which is often present in the dir_context actor function as defined by
olm_filldir. In essence the locking was done at the lowest levels.
With newer kernels this low level locking preventing mounting
of the server back end with the following back trace:

 [  612.287442] mount.lustre    D    0  3026   3025 0x00000224
 [  612.289699] Call trace:
 [  612.290398] [<ffff000008085a8c>] __switch_to+0x8c/0xa8
 [  612.292363] [<ffff00000880b9d0>] __schedule+0x328/0x860
 [  612.294394] [<ffff00000880bf3c>] schedule+0x34/0x8c
 [  612.296285] [<ffff00000880f460>] rwsem_down_write_failed+0x134/0x238
 [  612.304803] [<ffff00000880e89c>] down_write+0x54/0x58
 [  612.307115] [<ffff0000028cae2c>] osd_ios_root_fill+0xd4/0x590 [osd_ldiskfs]
 [  612.310109] [<ffff000002281798>] call_filldir+0xd8/0x148 [ldiskfs]
 [  612.312450] [<ffff000002282170>] ldiskfs_readdir+0x670/0x7b8 [ldiskfs]
 [  612.314975] [<ffff0000082b18d0>] iterate_dir+0x150/0x1b8
 [  612.317118] [<ffff0000028c24ac>] osd_ios_general_scan+0x104/0x2b8 [osd_ldiskfs]
 [  612.320299] [<ffff0000028cb384>] osd_initial_OI_scrub+0x9c/0x13c0 [osd_ldiskfs]
 [  612.323047] [<ffff0000028cd53c>] osd_scrub_setup+0xb44/0x1118 [osd_ldiskfs]
 [  612.325758] [<ffff00000289d4ec>] osd_device_alloc+0x544/0x950 [osd_ldiskfs]
 [  612.328637] [<ffff000001e29dac>] class_setup+0x7bc/0xd20 [obdclass]
 [  612.331324] [<ffff000001e33a30>] class_process_config+0x1708/0x2e90 [obdclass]
 [  612.334259] [<ffff000001e3a368>] do_lcfg+0x2b0/0x6d8 [obdclass]
 [  612.336704] [<ffff000001e3f49c>] lustre_start_simple+0x154/0x3f8 [obdclass]
 [  612.339694] [<ffff000001e74ee0>] osd_start+0x500/0xa40 [obdclass]
 [  612.342277] [<ffff000001e80a84>] server_fill_super+0x1d4/0x1848 [obdclass]
 [  612.345078] [<ffff000001e437a4>] lustre_fill_super+0x62c/0xdb0 [obdclass]
 [  612.347655] [<ffff0000082a02e0>] mount_nodev+0x5c/0xbc
 [  612.349954] [<ffff000001e3adc4>] lustre_mount+0x4c/0x80 [obdclass]
 [  612.352263] [<ffff0000082a1324>] mount_fs+0x54/0x16c
 [  612.354159] [<ffff0000082bfb6c>] vfs_kern_mount+0x58/0x154
 [  612.356246] [<ffff0000082c2ff8>] do_mount+0x1cc/0xbac
 [  612.358192] [<ffff0000082c3d60>] SyS_mount+0x88/0xd4

This is due to ext4_readdir use of dir_relax_shared() that is taking the
inode_lock which conflicts with the lock taking in our dir_context actor.
Having the dir_context actor take the lock so deep in the stack is incorrect
behavior. Since this is the case we need to migrate the locking from the
dir_context actor to before ext4_readdir() is called. This ends up involving
implementing the locking in the code that calls fops->iterate_dir() osd-ldiskfs
implemented. Instead of handling the lock changes in at the osd-ldiskfs level
across many kernel versions we can use iterate_dir() directly which does this
handling for us.

To use interate_dir() we need to work around the fact that osd-ldiskfs does
not use the VFS layer to create or managed VFS data structure such as struct file
or struct dentry. Instead osd-ldiskfs uses these VFS data structures as scratch
areas just to interface with the ext4 layer. We could use the VFS layer to
manage these data structures but the would require a massive reworking of this
OSD abstraction. Instead we look at what pieces are missing from struct file
due to the open coding that iterate_dir() expects. The first thing missing
from struct file is the initialization of struct path which is used by
file_accessed() to update the atime. The current behavior for our OSD layer
is not to update the atime so we preserve this behavior by setting the
struct file f_flag field to O_NOATIME. This prevents the calling of
touch_atime() which expects a proper struct path. The second expected field
for iterate_dir() is f_security. The function security_file_alloc() is not
exported so we do this manually. For the Lustre OSD case we are not interested
in any file security messages since our object store is not exposed in any way.
We disable the sending of the fsnotify events by setting the f_mode flag to
FMODE_NONOTIFY.

With this migration of the lock handling up the OSD stack we can no longer
just use osd_lookup_one_len_unlocked(). Replace osd_lookup_one_len_unlocked()
with osd_lookup_one_len() for the cases when the inode_lock() is taken much
earlier in the stack. This also closely follows the behavior of similar
functions in the linux VFS layer.

Change-Id: I00893f41ef5ec01835f0e58da6bd5c96a62aea88
Signed-off-by: James Simmons <jsimmons@infradead.org>
Reviewed-on: https://review.whamcloud.com/34714
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Yang Sheng <ys@whamcloud.com>
Reviewed-by: Neil Brown <neilb@suse.de>
lustre/osd-ldiskfs/osd_compat.c
lustre/osd-ldiskfs/osd_handler.c
lustre/osd-ldiskfs/osd_internal.h
lustre/osd-ldiskfs/osd_oi.c
lustre/osd-ldiskfs/osd_scrub.c