diff --git a/doc/man/man1/sinfo.1 b/doc/man/man1/sinfo.1
index 68ab82404138c400f843ec9d59055a8a72371206..ec7affb8bd5a44add2fa0819b5b09427cfadbfa8 100644
--- a/doc/man/man1/sinfo.1
+++ b/doc/man/man1/sinfo.1
@@ -1,4 +1,4 @@
-.TH sinfo "1" "Slurm Commands" "Janury 2016" "Slurm Commands"
+.TH sinfo "1" "Slurm Commands" "January 2016" "Slurm Commands"
 
 .SH "NAME"
 sinfo \- view information about Slurm nodes and partitions.
@@ -325,7 +325,10 @@ Default time for any job in the format "days\-hours:minutes:seconds".
 Size of temporary disk space per node in megabytes.
 .TP
 \fBfeatures\fR
-Features associated with the nodes.
+Features available on the nodes. Also see \fBfeatures_act\fR.
+.TP
+\fBfeatures_act\fR
+Features currently active on the nodes. Also see \fBfeatures\fR.
 .TP
 \fBgroups\fR
 Groups which may use the nodes.
diff --git a/src/api/node_info.c b/src/api/node_info.c
index 8a8ff14a8ba5a1baa2e01fbfcf9aaf78f6de821d..92ca2da352c8ea013105937aff82a37be5c83a6c 100644
--- a/src/api/node_info.c
+++ b/src/api/node_info.c
@@ -231,8 +231,16 @@ slurm_sprint_node_table (node_info_t * node_ptr,
 
 	/****** Line ******/
 	snprintf(tmp_line, sizeof(tmp_line),
-		 "AvailableFeatures=%s ActiveFeatures=%s",
-		 node_ptr->features, node_ptr->features_act);
+		 "AvailableFeatures=%s", node_ptr->features);
+	xstrcat(out, tmp_line);
+	if (one_liner)
+		xstrcat(out, " ");
+	else
+		xstrcat(out, "\n   ");
+
+	/****** Line ******/
+	snprintf(tmp_line, sizeof(tmp_line),
+		 "ActiveFeatures=%s", node_ptr->features_act);
 	xstrcat(out, tmp_line);
 	if (one_liner)
 		xstrcat(out, " ");
diff --git a/src/common/read_config.c b/src/common/read_config.c
index 231e717ba692cdfe63fd73417a042c98d24ea8c9..35d1b651be7066dcaac9075abbd92c6b3c218a97 100644
--- a/src/common/read_config.c
+++ b/src/common/read_config.c
@@ -571,7 +571,8 @@ static int _parse_frontend(void **dest, slurm_parser_enum_t type,
 	/* should not get here */
 }
 
-static void _add_knl_features(slurm_conf_node_t *n)
+/* Expand a feature of "knl" to all appropriate KNL options */
+extern void add_knl_features(char **features)
 {
 	static uint16_t avail_mcdram = 0, avail_numa = 0;
 	static uint16_t default_mcdram = 0, default_numa = 0;
@@ -580,17 +581,17 @@ static void _add_knl_features(slurm_conf_node_t *n)
 	char *tmp_str, *token, *last = NULL;
 	int i, j;
 
-	if (!n->feature)
+	if ((features == NULL) || (features[0] ==  NULL))
 		return;
 
-	i = strlen(n->feature) + 1;	/* oversized */
+	i = strlen(features[0]) + 1;	/* oversized */
 	tmp_str = xmalloc(i);
 	/* Remove white space from feature specification */
-	for (i = 0, j = 0; n->feature[i]; i++) {
-		if (!isspace(n->feature[i]))
-			tmp_str[j++] = n->feature[i];
+	for (i = 0, j = 0; features[0][i]; i++) {
+		if (!isspace(features[0][i]))
+			tmp_str[j++] = features[0][i];
 	}
-	strcpy(n->feature, tmp_str);
+	strcpy(*features, tmp_str);
 
 	token = strtok_r(tmp_str, ",", &last);
 	while (token) {
@@ -614,13 +615,13 @@ static void _add_knl_features(slurm_conf_node_t *n)
 
 	if (!has_mcdram) {
 		tmp_str = knl_mcdram_str(avail_mcdram);
-		xstrfmtcat(n->feature, ",%s", tmp_str);
+		xstrfmtcat(*features, ",%s", tmp_str);
 		xfree(tmp_str);
 	}
 
 	if (!has_numa) {
 		tmp_str = knl_numa_str(avail_numa);
-		xstrfmtcat(n->feature, ",%s", tmp_str);
+		xstrfmtcat(*features, ",%s", tmp_str);
 		xfree(tmp_str);
 	}
 }
@@ -639,6 +640,7 @@ static int _parse_nodename(void **dest, slurm_parser_enum_t type,
 		{"CPUs", S_P_UINT16},
 		{"CPUSpecList", S_P_STRING},
 		{"Feature", S_P_STRING},
+		{"Features", S_P_STRING},
 		{"Gres", S_P_STRING},
 		{"MemSpecLimit", S_P_UINT32},
 		{"NodeAddr", S_P_STRING},
@@ -726,9 +728,11 @@ static int _parse_nodename(void **dest, slurm_parser_enum_t type,
 		if (!s_p_get_string(&n->cpu_spec_list, "CPUSpecList", tbl))
 			s_p_get_string(&n->cpu_spec_list, "CPUSpecList", dflt);
 
-		if (!s_p_get_string(&n->feature, "Feature", tbl))
-			s_p_get_string(&n->feature, "Feature", dflt);
-		_add_knl_features(n);
+		if (!s_p_get_string(&n->feature, "Feature",  tbl) &&
+		    !s_p_get_string(&n->feature, "Features", tbl) &&
+		    !s_p_get_string(&n->feature, "Feature",  dflt))
+			s_p_get_string(&n->feature, "Features", dflt);
+		add_knl_features(&n->feature);
 
 		if (!s_p_get_string(&n->gres, "Gres", tbl))
 			s_p_get_string(&n->gres, "Gres", dflt);
diff --git a/src/common/read_config.h b/src/common/read_config.h
index 96f0ea20a834a56dbf40563c817a47dbe5b22a3c..7a322fed4a8f9ce7251adfede542d9b032e518d5 100644
--- a/src/common/read_config.h
+++ b/src/common/read_config.h
@@ -578,4 +578,8 @@ extern char *get_extra_conf_path(char *conf_name);
  */
 extern bool run_in_daemon(char *daemons);
 
+/* Expand a feature of "knl" to all appropriate KNL options
+ * features IN/OUT - Expand string as appropriate, must be xmalloc'ed */
+extern void add_knl_features(char **features);
+
 #endif /* !_READ_CONFIG_H */
diff --git a/src/sinfo/opts.c b/src/sinfo/opts.c
index c901b9f472c0302991fbb5bf3454e8336cacab33..0184cf3c3fb2f8f860643faf27e08e99394a50e7 100644
--- a/src/sinfo/opts.c
+++ b/src/sinfo/opts.c
@@ -3,7 +3,7 @@
  *****************************************************************************
  *  Copyright (C) 2002-2007 The Regents of the University of California.
  *  Copyright (C) 2008-2010 Lawrence Livermore National Security.
- *  Portions Copyright (C) 2010 SchedMD <http://www.schedmd.com>.
+ *  Portions Copyright (C) 2010-2016 SchedMD <http://www.schedmd.com>.
  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
  *  Written by Joey Ekstrom <ekstrom1@llnl.gov>, Morris Jette <jette1@llnl.gov>
  *  CODE-OCEC-09-009. All rights reserved.
@@ -606,6 +606,7 @@ _parse_format( char* format )
 					right_justify,
 					suffix );
 		} else if (field[0] == 'b') {
+			params.match_flags.features_act_flag = true;
 			format_add_features_act( params.format_list,
 					field_size,
 					right_justify,
@@ -937,6 +938,12 @@ static int _parse_long_format (char* format_long)
 					     field_size,
 					     right_justify,
 					     suffix );
+		} else if (!strcasecmp(token, "features_act")) {
+			params.match_flags.features_act_flag = true;
+			format_add_features_act( params.format_list,
+					field_size,
+					right_justify,
+					suffix );
 		} else if (!strcasecmp(token, "groups")) {
 			params.match_flags.groups_flag = true;
 			format_add_groups( params.format_list,
@@ -1241,6 +1248,8 @@ void _print_options( void )
 			"true" : "false");
 	printf("features_flag   = %s\n", params.match_flags.features_flag ?
 			"true" : "false");
+	printf("features_flag_act = %s\n", params.match_flags.features_act_flag?
+			"true" : "false");
 	printf("groups_flag     = %s\n", params.match_flags.groups_flag ?
 					"true" : "false");
 	printf("gres_flag       = %s\n", params.match_flags.gres_flag ?
diff --git a/src/sinfo/sinfo.c b/src/sinfo/sinfo.c
index 47ea1597b68baff12e57f93af2c6c66d1dc9a23f..477357806fcf38bfa210bd7d18efac012f53d21f 100644
--- a/src/sinfo/sinfo.c
+++ b/src/sinfo/sinfo.c
@@ -3,7 +3,7 @@
  *****************************************************************************
  *  Copyright (C) 2002-2007 The Regents of the University of California.
  *  Copyright (C) 2008-2010 Lawrence Livermore National Security.
- *  Portions Copyright (C) 2010-2011 SchedMD <http://www.schedmd.com>.
+ *  Portions Copyright (C) 2010-2016 SchedMD <http://www.schedmd.com>.
  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
  *  Written by Joey Ekstrom <ekstrom1@llnl.gov>, Morris Jette <jette1@llnl.gov>
  *  CODE-OCEC-09-009. All rights reserved.
@@ -753,6 +753,11 @@ static bool _match_node_data(sinfo_data_t *sinfo_ptr, node_info_t *node_ptr)
 	    (xstrcmp(node_ptr->features, sinfo_ptr->features)))
 		return false;
 
+	if (sinfo_ptr->nodes &&
+	    params.match_flags.features_act_flag &&
+	    (xstrcmp(node_ptr->features_act, sinfo_ptr->features_act)))
+		return false;
+
 	if (sinfo_ptr->nodes &&
 	    params.match_flags.gres_flag &&
 	    (xstrcmp(node_ptr->gres, sinfo_ptr->gres)))
diff --git a/src/sinfo/sinfo.h b/src/sinfo/sinfo.h
index e7e9eeede34bfaeebddfba8522b193a454776244..290fb85d2f06ada69c3e59f1abf3c9cbaacbdda6 100644
--- a/src/sinfo/sinfo.h
+++ b/src/sinfo/sinfo.h
@@ -137,6 +137,7 @@ struct sinfo_match_flags {
 	bool sct_flag;
 	bool disk_flag;
 	bool features_flag;
+	bool features_act_flag;
 	bool groups_flag;
 	bool gres_flag;
 	bool hostnames_flag;
diff --git a/src/slurmctld/node_mgr.c b/src/slurmctld/node_mgr.c
index 0f5d10afed6092a3b397f0834337eecf7665779e..154e9541fec33a70e6c2f15065eeddd3578cd6e9 100644
--- a/src/slurmctld/node_mgr.c
+++ b/src/slurmctld/node_mgr.c
@@ -1200,6 +1200,41 @@ _equivalent_node_state(struct node_record *node_ptr, uint32_t new_state)
 	return false;
 }
 
+/* Confirm that the selected ActiveFeatures are a subset of AvailableFeatures */
+static bool _valid_features_act(char *features_act, char *features)
+{
+	bool valid_subset = true;
+	char *tmp_act, *last_act = NULL, *tok_act;
+	char *tmp_avail, *last_avail = NULL, *tok_avail;
+
+	if (!features_act || (features_act[0] == '\0'))
+		return true;
+	if (!features || (features[0] == '\0'))
+		return false;
+
+	tmp_act = xstrdup(features_act);
+        tok_act = strtok_r(tmp_act, ",", &last_act);
+        while (tok_act) {
+		last_avail = NULL;
+		tmp_avail = xstrdup(features);
+		tok_avail = strtok_r(tmp_avail, ",", &last_avail);
+		while (tok_avail) {
+			if (!xstrcmp(tok_act, tok_avail))
+				break;
+		        tok_avail = strtok_r(NULL, ",", &last_avail);
+		}
+		xfree(tmp_avail);
+		if (!tok_avail) {	/* No match found */
+			valid_subset = false;
+			break;
+		}
+                tok_act = strtok_r(NULL, ",", &last_act);
+	}
+	xfree(tmp_act);
+
+	return valid_subset;
+}
+
 /*
  * update_node - update the configuration data for one or more nodes
  * IN update_node_msg - update node request
@@ -1263,6 +1298,8 @@ int update_node ( update_node_msg_t * update_node_msg )
 		}
 	}
 
+	add_knl_features(&update_node_msg->features);
+
 	while ( (this_node_name = hostlist_shift (host_list)) ) {
 		int err_code = 0;
 
@@ -1296,17 +1333,26 @@ int update_node ( update_node_msg_t * update_node_msg )
 
 		if (update_node_msg->features) {
 			xfree(node_ptr->features);
-			if (update_node_msg->features[0])
+			if (update_node_msg->features[0]) {
 				node_ptr->features =
 					xstrdup(update_node_msg->features);
+			}
 			/* _update_node_features() logs and updates config */
 		}
 
-		if (update_node_msg->features_act) {
+		if (update_node_msg->features_act &&
+		    !_valid_features_act(update_node_msg->features_act,
+					 node_ptr->features)) {
+			info("Invalid node ActiveFeatures (%s not subset of %s)",
+			     update_node_msg->features_act,
+			     node_ptr->features);
+			error_code = ESLURM_INVALID_FEATURE;
+		} else if (update_node_msg->features_act) {
 			xfree(node_ptr->features_act);
-			if (update_node_msg->features_act[0])
+			if (update_node_msg->features_act[0]) {
 				node_ptr->features_act =
 					xstrdup(update_node_msg->features_act);
+			}
 			/* _update_node_features() logs and updates config */
 		}