One example of complete setattr code path: fs/open.c:sys_chmod(): downs the i_sem and sets up a struct iattr: struct iattr { unsigned int ia_valid; umode_t ia_mode; uid_t ia_uid; gid_t ia_gid; loff_t ia_size; struct timespec ia_atime; struct timespec ia_mtime; struct timespec ia_ctime; unsigned int ia_attr_flags; }; setting ia_mode and setting ia_valid to ATTR_MODE | ATTR_CTIME. It calls notify_change with newattr with the above struture and a dentry. The first thing notify_change does is update ia_ctime unconditionally. Then it ends up calling the setattr op. After that, for certain attribute updates, it calls dnotify_parent. The nfs setattr op takes the BKL throughout. It first calls nfs_revalidate_inode, which may return without doing anything if jiffies is before NFS_READTIME(inode)+NFSATTRTIMEO(inode). (Question: how are these two times set?); otherwise it will call __nfs_revalidate_inode, which calls the protocol-specific getattr and then calls nfs_refresh_inode to update the inode with the results. After calling nfs_revalidate_inode, if S_ISREG, does filemap_fdatawrite, nfs_wb_all, and filemap_fdatawait, so we don't continue till all data is written back. Then it calls protocol-specific setattr. After the rpc call, calls vmtruncate if the size changed. If !(fattr.valid & NFS_ATTR_WCC), ets fattr.pre_size, mtime, ctime to values from inode. Calls NFS_CACHEINV to set NFS_READTIME to a long time ago. Finally, calls nfs_refresh_inode(), which calls __nfs_refresh_inode(). __nfs_refresh_inode: If fattr->timestamp is too old, return without doing anything. If size has changed or mtime has changed or v4 change_attr has changed, update; unless some wcc checks pass. Update the inode, update attrtimeo values.