diff --git a/build/rpm/xfstests.spec.in b/build/rpm/xfstests.spec.in
index 338e8839..5a35fbf8 100644
--- a/build/rpm/xfstests.spec.in
+++ b/build/rpm/xfstests.spec.in
@@ -17,8 +17,8 @@ Group: System Environment/Base
 
 %description
 The XFS regression test suite.  Also includes some support for
-acl, attr, dmapi, udf, and nfs testing.  Contains around 200 specific tests
-for userspace & kernelspace.
+acl, attr, dmapi, udf, nfs and afs testing.  Contains around 200 specific
+tests for userspace & kernelspace.
 
 %prep
 if [ -f .census ] ; then
diff --git a/check b/check
index 2e148e57..8aba8345 100755
--- a/check
+++ b/check
@@ -53,6 +53,7 @@ usage()
 
 check options
     -nfs		test NFS
+    -afs		test AFS
     -glusterfs		test GlusterFS
     -cifs		test CIFS
     -9p			test 9p
@@ -266,6 +267,7 @@ while [ $# -gt 0 ]; do
 	-\? | -h | --help) usage ;;
 
 	-nfs)		FSTYP=nfs ;;
+	-afs)		FSTYP=afs ;;
 	-glusterfs)	FSTYP=glusterfs ;;
 	-cifs)		FSTYP=cifs ;;
 	-9p)		FSTYP=9p ;;
diff --git a/common/config b/common/config
index 8023273d..bc998af7 100644
--- a/common/config
+++ b/common/config
@@ -255,6 +255,7 @@ case "$HOSTOS" in
 	export BTRFS_TUNE_PROG=$(type -P btrfstune)
 	export XFS_FSR_PROG=$(type -P xfs_fsr)
 	export MKFS_NFS_PROG="false"
+	export MKFS_AFS_PROG="false"
 	export MKFS_CIFS_PROG="false"
 	export MKFS_OVERLAY_PROG="false"
 	export MKFS_REISER4_PROG=$(type -P mkfs.reiser4)
@@ -306,6 +307,9 @@ _mount_opts()
 	nfs)
 		export MOUNT_OPTIONS=$NFS_MOUNT_OPTIONS
 		;;
+	afs)
+		export MOUNT_OPTIONS=$AFS_MOUNT_OPTIONS
+		;;
 	cifs)
 		export MOUNT_OPTIONS=$CIFS_MOUNT_OPTIONS
 		;;
@@ -364,6 +368,9 @@ _test_mount_opts()
 	nfs)
 		export TEST_FS_MOUNT_OPTS=$NFS_MOUNT_OPTIONS
 		;;
+	afs)
+		export TEST_FS_MOUNT_OPTS=$AFS_MOUNT_OPTIONS
+		;;
 	glusterfs)
 		export TEST_FS_MOUNT_OPTS=$GLUSTERFS_MOUNT_OPTIONS
 		;;
@@ -390,6 +397,9 @@ _mkfs_opts()
 	nfs)
 		export MKFS_OPTIONS=$NFS_MKFS_OPTIONS
 		;;
+	afs)
+		export MKFS_OPTIONS=$AFS_MKFS_OPTIONS
+		;;
 	cifs)
 		export MKFS_OPTIONS=$CIFS_MKFS_OPTIONS
 		;;
@@ -487,7 +497,7 @@ _check_device()
 	fi
 
 	case "$FSTYP" in
-	9p|tmpfs|virtiofs)
+	9p|afs|tmpfs|virtiofs)
 		# 9p and virtiofs mount tags are just plain strings, so anything is allowed
 		# tmpfs doesn't use mount source, ignore
 		;;
diff --git a/common/rc b/common/rc
index a6967831..17b3c538 100644
--- a/common/rc
+++ b/common/rc
@@ -139,6 +139,8 @@ case "$FSTYP" in
     nfs)
 	 . ./common/nfs
 	 ;;
+    afs)
+	 ;;
     cifs)
 	 ;;
     9p)
@@ -608,6 +610,9 @@ _test_mkfs()
     nfs*)
 	# do nothing for nfs
 	;;
+    afs*)
+	# do nothing for afs
+	;;
     cifs)
 	# do nothing for cifs
 	;;
@@ -651,6 +656,9 @@ _mkfs_dev()
     nfs*)
 	# do nothing for nfs
 	;;
+    afs*)
+	# do nothing for afs
+	;;
     9p)
 	# do nothing for 9p
 	;;
@@ -693,7 +701,7 @@ _mkfs_dev()
     rm -f $tmp.mkfserr $tmp.mkfsstd
 }
 
-# remove all files in $SCRATCH_MNT, useful when testing on NFS/CIFS
+# remove all files in $SCRATCH_MNT, useful when testing on NFS/AFS/CIFS
 _scratch_cleanup_files()
 {
 	case $FSTYP in
@@ -721,7 +729,7 @@ _scratch_mkfs()
 	local mkfs_status
 
 	case $FSTYP in
-	nfs*|cifs|ceph|overlay|glusterfs|pvfs2|9p|virtiofs)
+	nfs*|afs|cifs|ceph|overlay|glusterfs|pvfs2|9p|virtiofs)
 		# unable to re-create this fstyp, just remove all files in
 		# $SCRATCH_MNT to avoid EEXIST caused by the leftover files
 		# created in previous runs
@@ -1473,7 +1481,7 @@ _check_mounted_on()
 
 	if [ -n "$type" -a "`_fs_type $dev`" != "$type" ]; then
 		echo "$devname=$dev is mounted but not a type $type filesystem"
-		# raw $DF_PROG cannot handle NFS/CIFS/overlay correctly
+		# raw $DF_PROG cannot handle NFS/AFS/CIFS/overlay correctly
 		_df_device $dev
 		return 3 # 3 = mounted as wrong type
 	fi
@@ -1512,6 +1520,15 @@ _require_scratch_nocheck()
 			_notrun "this test requires a valid \$SCRATCH_MNT"
 		fi
 		;;
+	afs)
+		echo $SCRATCH_DEV | grep -q "^%" > /dev/null 2>&1
+		if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
+			_notrun "this test requires a valid \$SCRATCH_DEV"
+		fi
+		if [ ! -d "$SCRATCH_MNT" ]; then
+			_notrun "this test requires a valid \$SCRATCH_MNT"
+		fi
+		;;
 	pvfs2)
 		echo $SCRATCH_DEV | grep -q "://" > /dev/null 2>&1
 		if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
@@ -1636,6 +1653,15 @@ _require_test()
 			_notrun "this test requires a valid \$TEST_DIR"
 		fi
 		;;
+	afs)
+		echo $TEST_DEV | grep -q "^%" > /dev/null 2>&1
+		if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
+			_notrun "this test requires a valid \$TEST_DEV"
+		fi
+		if [ ! -d "$TEST_DIR" ]; then
+			_notrun "this test requires a valid \$TEST_DIR"
+		fi
+		;;
 	cifs)
 		echo $TEST_DEV | grep -q "//" > /dev/null 2>&1
 		if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
@@ -2526,7 +2552,7 @@ _scratch_mkfs_richacl()
 		;;
 	ext4)   _scratch_mkfs -O richacl
 		;;
-	nfs*|cifs|overlay)
+	nfs*|afs|cifs|overlay)
 		_scratch_mkfs
 		;;
 	esac
@@ -2741,6 +2767,9 @@ _check_test_fs()
     nfs)
 	# no way to check consistency for nfs
 	;;
+    afs)
+	# no way to check consistency for afs
+	;;
     cifs)
 	# no way to check consistency for cifs
 	;;
@@ -2802,6 +2831,9 @@ _check_scratch_fs()
     nfs*)
 	# Don't know how to check an NFS filesystem, yet.
 	;;
+    afs*)
+	# Don't know how to check an AFS filesystem, yet.
+	;;
     cifs)
 	# Don't know how to check a CIFS filesystem, yet.
 	;;
@@ -3414,7 +3446,7 @@ _require_atime()
 {
 	_exclude_scratch_mount_option "noatime"
 	case $FSTYP in
-	nfs|cifs)
+	nfs|afs|cifs)
 		_notrun "atime related mount options have no effect on $FSTYP"
 		;;
 	esac
@@ -4224,6 +4256,35 @@ _require_mknod()
 	rm -f $TEST_DIR/$seq.null
 }
 
+_has_mknod()
+{
+	case $FSTYP in
+	afs)
+		return 1;;
+	*)
+		return 0;;
+	esac
+}
+
+_require_use_local_uidgid()
+{
+	case $FSTYP in
+	afs)
+		_notrun "$FSTYP doesn't honour local uid and gid"
+		;;
+	esac
+}
+
+_require_sgid_inheritance()
+{
+	case $FSTYP in
+	afs)
+		_notrun "SGID-based group ID inheritance is not supported on $FSTYP"
+		;;
+	esac
+
+}
+
 init_rc
 
 ################################################################################
diff --git a/doc/requirement-checking.txt b/doc/requirement-checking.txt
index 45d2756b..c945e16a 100644
--- a/doc/requirement-checking.txt
+++ b/doc/requirement-checking.txt
@@ -16,6 +16,10 @@ they have.  This is done with _require_<xxx> macros, which may take parameters.
 
 	_require_chattr <letters>
 	_require_exportfs
+	_require_mknod
+	_has_mknod
+	_require_sgid_inheritance
+	_require_use_local_uidgid
 
  (3) System call requirements.
 
@@ -97,6 +101,26 @@ _require_exportfs
      The test also requires the use of the open_by_handle_at() system call and
      will be skipped if it isn't available in the kernel.
 
+_require_mknod
+_has_mknod
+
+     The test requires that the $TEST_DEV filesystem supports mknod(2).
+     _require_mknod will cause the test to be skipped; _has_mknod returns 0 if
+     mknod is supported and 1 otherwise.
+
+_require_sgid_inheritance
+
+     The test required that the $TEST_DEV filesystem supports the inheritance
+     of the SGID bit and the GID from a marked directory.  The test will be
+     skipped if not supported.
+
+_require_use_local_uidgid
+
+     The test requires that the $TEST_DEV filesystem sets the uid and gid of a
+     newly created file to the creating process's fsuid and fsgid.  Remote
+     filesystems, for example, may choose other settings or not even have these
+     concepts available.  The test will be skipped if not supported.
+
 
 ========================
 SYSTEM CALL REQUIREMENTS
diff --git a/tests/generic/294 b/tests/generic/294
index 32c89b03..71f861de 100755
--- a/tests/generic/294
+++ b/tests/generic/294
@@ -8,6 +8,7 @@
 # we ask to create an already-existing entity on an RO filesystem
 #
 seq=`basename $0`
+seqfull=$0
 seqres=$RESULT_DIR/$seq
 echo "QA output created by $seq"
 
@@ -34,8 +35,13 @@ _supported_os Linux
 _require_scratch
 _require_symlinks
 
-rm -f $seqres.full
-_scratch_mkfs > $seqres.full 2>&1 || _fail "Could not mkfs scratch device"
+features=""
+if ! _has_mknod; then
+	features="nomknod"
+fi
+_link_out_file "$features"
+
+_scratch_mkfs 2>&1 || _fail "Could not mkfs scratch device"
 
 THIS_TEST_DIR=$SCRATCH_MNT/$seq.test
 
@@ -57,5 +63,6 @@ _try_scratch_mount -o remount,ro || _fail "Could not remount scratch readonly"
 _create_files 2>&1 | _filter_scratch
 
 # success, all done
+rm -f $seqres.full
 status=0
 exit
diff --git a/tests/generic/294.cfg b/tests/generic/294.cfg
new file mode 100644
index 00000000..c0466cde
--- /dev/null
+++ b/tests/generic/294.cfg
@@ -0,0 +1 @@
+nomknod: nomknod
diff --git a/tests/generic/294.out.mknod b/tests/generic/294.out.mknod
new file mode 100644
index 00000000..4aea9d82
--- /dev/null
+++ b/tests/generic/294.out.mknod
@@ -0,0 +1,6 @@
+QA output created by 294
+mknod: SCRATCH_MNT/294.test/testnode: Operation not permitted
+mknod: SCRATCH_MNT/294.test/testnode: Read-only file system
+mkdir: cannot create directory 'SCRATCH_MNT/294.test/testdir': File exists
+touch: cannot touch 'SCRATCH_MNT/294.test/testtarget': Read-only file system
+ln: creating symbolic link 'SCRATCH_MNT/294.test/testlink': File exists
diff --git a/tests/generic/314 b/tests/generic/314
index 03df81ce..13fdfc31 100755
--- a/tests/generic/314
+++ b/tests/generic/314
@@ -29,6 +29,7 @@ _cleanup()
 _supported_fs generic
 _require_test
 _require_user
+_require_sgid_inheritance
 
 rm -rf $TEST_DIR/$seq-dir
 
diff --git a/tests/generic/317 b/tests/generic/317
index 8ea9d81c..45e47a91 100755
--- a/tests/generic/317
+++ b/tests/generic/317
@@ -46,6 +46,7 @@ _require_scratch
 _require_user
 _require_ugid_map
 _require_userns
+_require_use_local_uidgid
 qa_user_id=`id -u $qa_user`
 
 _filter_output()
