diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index a7d1921ac76b8ee5d85d563c1ceed80f4a65a2dd..fcc65802f367dd16d066a45d2149761af0aae042 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4518,6 +4518,11 @@ int btrfs_dirty_inode(struct inode *inode)
 static int btrfs_update_time(struct inode *inode, struct timespec *now,
 			     int flags)
 {
+	struct btrfs_root *root = BTRFS_I(inode)->root;
+
+	if (btrfs_root_readonly(root))
+		return -EROFS;
+
 	if (flags & S_VERSION)
 		inode_inc_iversion(inode);
 	if (flags & S_CTIME)
diff --git a/fs/inode.c b/fs/inode.c
index c99163b1b31036ef68974c0c5dbc192f8f73f4da..033529ecd242b94ca91f1b1818fe51df7b8c4ba8 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1551,6 +1551,8 @@ void touch_atime(struct path *path)
 	 * Btrfs), but since we touch atime while walking down the path we
 	 * really don't care if we failed to update the atime of the file,
 	 * so just ignore the return value.
+	 * We may also fail on filesystems that have the ability to make parts
+	 * of the fs read only, e.g. subvolumes in Btrfs.
 	 */
 	update_time(inode, &now, S_ATIME);
 	mnt_drop_write(mnt);