--- old/usr/src/tools/smatch/src/check_atomic_inc_dec.c	Mon Jun  8 07:43:26 2020
+++ new/usr/src/tools/smatch/src/check_atomic_inc_dec.c	Mon Jun  8 07:43:26 2020
@@ -24,14 +24,22 @@
 static int my_id;
 
 STATE(inc);
-STATE(orig);
+STATE(start_state);
 STATE(dec);
 
 static struct smatch_state *unmatched_state(struct sm_state *sm)
 {
-	if (parent_is_gone_var_sym(sm->name, sm->sym))
+	/*
+	 * We default to decremented.  For example, say we have:
+	 * 	if (p)
+	 *		atomic_dec(p);
+	 *      <- p is decreemented.
+	 *
+	 */
+	if ((sm->state == &dec) &&
+	    parent_is_gone_var_sym(sm->name, sm->sym))
 		return sm->state;
-	return &undefined;
+	return &start_state;
 }
 
 static struct stree *start_states;
@@ -86,7 +94,7 @@
 	return NULL;
 }
 
-static void db_inc_dec(struct expression *expr, int param, const char *key, const char *value, int inc_dec)
+static void db_inc_dec(struct expression *expr, int param, const char *key, int inc_dec)
 {
 	struct sm_state *start_sm;
 	struct expression *arg;
@@ -129,7 +137,7 @@
 			set_start_state(name, sym, &inc);
 
 		if (start_sm && start_sm->state == &inc)
-			set_state(my_id, name, sym, &orig);
+			set_state(my_id, name, sym, &start_state);
 		else
 			set_state(my_id, name, sym, &dec);
 	}
@@ -139,24 +147,70 @@
 		free_string(name);
 }
 
+static const char *primitive_funcs[] = {
+	"atomic_inc_return",
+	"atomic_add_return",
+	"atomic_sub_return",
+	"atomic_sub_and_test",
+	"atomic_dec_and_test",
+	"_atomic_dec_and_lock",
+	"atomic_dec",
+	"atomic_long_inc",
+	"atomic_long_dec",
+	"atomic_inc",
+	"atomic_sub",
+	"refcount_inc",
+	"refcount_dec",
+	"refcount_add",
+	"refcount_add_not_zero",
+	"refcount_inc_not_zero",
+	"refcount_sub_and_test",
+	"refcount_dec_and_test",
+	"atomic_dec_if_positive",
+};
+
+static bool is_inc_dec_primitive(struct expression *expr)
+{
+	int i;
+
+	while (expr->type == EXPR_ASSIGNMENT)
+		expr = strip_expr(expr->right);
+	if (expr->type != EXPR_CALL)
+		return false;
+
+	if (expr->fn->type != EXPR_SYMBOL)
+		return false;
+
+	for (i = 0; i < ARRAY_SIZE(primitive_funcs); i++) {
+		if (sym_name_is(primitive_funcs[i], expr->fn))
+			return true;
+	}
+
+	return false;
+}
+
 static void db_inc(struct expression *expr, int param, char *key, char *value)
 {
-	db_inc_dec(expr, param, key, value, ATOMIC_INC);
+	if (is_inc_dec_primitive(expr))
+		return;
+	db_inc_dec(expr, param, key, ATOMIC_INC);
 }
 
 static void db_dec(struct expression *expr, int param, char *key, char *value)
 {
-	db_inc_dec(expr, param, key, value, ATOMIC_DEC);
+	if (is_inc_dec_primitive(expr))
+		return;
+	db_inc_dec(expr, param, key, ATOMIC_DEC);
 }
 
 static void match_atomic_inc(const char *fn, struct expression *expr, void *_unused)
 {
-	db_inc_dec(expr, 0, "$->counter", "", ATOMIC_INC);
+	db_inc_dec(expr, 0, "$->counter", ATOMIC_INC);
 }
 
 static void match_atomic_dec(const char *fn, struct expression *expr, void *_unused)
 {
-	db_inc_dec(expr, 0, "$->counter", "", ATOMIC_DEC);
+	db_inc_dec(expr, 0, "$->counter", ATOMIC_DEC);
 }
 
 static void match_atomic_add(const char *fn, struct expression *expr, void *_unused)
@@ -166,28 +220,55 @@
 
 	amount = get_argument_from_call_expr(expr->args, 0);
 	if (get_implied_value(amount, &sval) && sval_is_negative(sval)) {
-		db_inc_dec(expr, 1, "$->counter", "", ATOMIC_DEC);
+		db_inc_dec(expr, 1, "$->counter", ATOMIC_DEC);
 		return;
 	}
 
-	db_inc_dec(expr, 1, "$->counter", "", ATOMIC_INC);
+	db_inc_dec(expr, 1, "$->counter", ATOMIC_INC);
 }
 
 static void match_atomic_sub(const char *fn, struct expression *expr, void *_unused)
 {
-	db_inc_dec(expr, 1, "$->counter", "", ATOMIC_DEC);
+	db_inc_dec(expr, 1, "$->counter", ATOMIC_DEC);
 }
 
 static void refcount_inc(const char *fn, struct expression *expr, void *param)
 {
-	db_inc_dec(expr, PTR_INT(param), "$->ref.counter", "", ATOMIC_INC);
+	db_inc_dec(expr, PTR_INT(param), "$->ref.counter", ATOMIC_INC);
 }
 
 static void refcount_dec(const char *fn, struct expression *expr, void *param)
 {
-	db_inc_dec(expr, PTR_INT(param), "$->ref.counter", "", ATOMIC_DEC);
+	db_inc_dec(expr, PTR_INT(param), "$->ref.counter", ATOMIC_DEC);
 }
 
+static void pm_runtime_get_sync(const char *fn, struct expression *expr, void *param)
+{
+	db_inc_dec(expr, PTR_INT(param), "$->power.usage_count.counter", ATOMIC_INC);
+}
+
+static void match_implies_inc(const char *fn, struct expression *call_expr,
+			      struct expression *assign_expr, void *param)
+{
+	db_inc_dec(call_expr, PTR_INT(param), "$->ref.counter", ATOMIC_INC);
+}
+
+static void match_implies_atomic_dec(const char *fn, struct expression *call_expr,
+			      struct expression *assign_expr, void *param)
+{
+	db_inc_dec(call_expr, PTR_INT(param), "$->counter", ATOMIC_DEC);
+}
+
+static bool is_maybe_dec(struct sm_state *sm)
+{
+	if (sm->state == &dec)
+		return true;
+	if (slist_has_state(sm->possible, &dec) &&
+	    !slist_has_state(sm->possible, &inc))
+		return true;
+	return false;
+}
+
 static void match_return_info(int return_id, char *return_ranges, struct expression *expr)
 {
 	struct sm_state *sm;
@@ -194,10 +275,14 @@
 	const char *param_name;
 	int param;
 
+	if (is_impossible_path())
+		return;
+
 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
-		if (sm->state != &inc &&
-		    sm->state != &dec)
+		if (sm->state != &inc && !is_maybe_dec(sm))
 			continue;
+		if (sm->state == get_state_stree(start_states, my_id, sm->name, sm->sym))
+			continue;
 		if (parent_is_gone_var_sym(sm->name, sm->sym))
 			continue;
 		param = get_param_num_from_sym(sm->sym);
@@ -221,7 +306,7 @@
 	if (!rl)
 		return EMPTY;
 
-	if (sval_is_negative(rl_min(rl)))
+	if (!is_whole_rl(rl) && sval_is_negative(rl_min(rl)))
 		return NEGATIVE;
 
 	if (rl_min(rl).value == 0)
@@ -237,14 +322,23 @@
 	int inc_buckets[NUM_BUCKETS] = {};
 	int dec_buckets[NUM_BUCKETS] = {};
 	struct stree *stree, *orig_stree;
+	struct smatch_state *state;
 	struct sm_state *return_sm;
 	struct sm_state *sm;
 	sval_t line = sval_type_val(&int_ctype, 0);
 	int bucket;
 
+	/* static variable are probably just counters */
+	if (sym->ctype.modifiers & MOD_STATIC &&
+	    !(sym->ctype.modifiers & MOD_TOPLEVEL))
+		return;
+
 	FOR_EACH_PTR(get_all_return_strees(), stree) {
 		orig_stree = __swap_cur_stree(stree);
 
+		if (is_impossible_path())
+			goto swap_stree;
+
 		return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL);
 		if (!return_sm)
 			goto swap_stree;
@@ -257,21 +351,23 @@
 			goto swap_stree;
 
 		sm = get_sm_state(my_id, name, sym);
-		if (!sm)
-			goto swap_stree;
+		if (sm)
+			state = sm->state;
+		else
+			state = &start_state;
 
-		if (sm->state != &inc &&
-		    sm->state != &dec &&
-		    sm->state != &orig)
+		if (state != &inc &&
+		    state != &dec &&
+		    state != &start_state)
 			goto swap_stree;
 
 		bucket = success_fail_positive(estate_rl(return_sm->state));
 
-		if (sm->state == &inc) {
+		if (state == &inc) {
 			add_range(&inc_lines, line, line);
 			inc_buckets[bucket] = true;
 		}
-		if (sm->state == &dec || sm->state == &orig) {
+		if (state == &dec || state == &start_state) {
 			add_range(&dec_lines, line, line);
 			dec_buckets[bucket] = true;
 		}
@@ -345,9 +441,14 @@
 	add_function_hook("atomic_add_return", &match_atomic_add, NULL);
 	add_function_hook("atomic_sub_return", &match_atomic_sub, NULL);
 	add_function_hook("atomic_sub_and_test", &match_atomic_sub, NULL);
+	add_function_hook("atomic_long_sub_and_test", &match_atomic_sub, NULL);
+	add_function_hook("atomic64_sub_and_test", &match_atomic_sub, NULL);
 	add_function_hook("atomic_dec_and_test", &match_atomic_dec, NULL);
+	add_function_hook("atomic_long_dec_and_test", &match_atomic_dec, NULL);
+	add_function_hook("atomic64_dec_and_test", &match_atomic_dec, NULL);
 	add_function_hook("_atomic_dec_and_lock", &match_atomic_dec, NULL);
 	add_function_hook("atomic_dec", &match_atomic_dec, NULL);
+	add_function_hook("atomic_dec_return", &match_atomic_dec, NULL);
 	add_function_hook("atomic_long_inc", &match_atomic_inc, NULL);
 	add_function_hook("atomic_long_dec", &match_atomic_dec, NULL);
 	add_function_hook("atomic_inc", &match_atomic_inc, NULL);
@@ -356,11 +457,17 @@
 	add_function_hook("refcount_inc", &refcount_inc, INT_PTR(0));
 	add_function_hook("refcount_dec", &refcount_dec, INT_PTR(0));
 	add_function_hook("refcount_add", &refcount_inc, INT_PTR(1));
-	add_function_hook("refcount_add_not_zero", &refcount_inc, INT_PTR(1));
-	add_function_hook("refcount_inc_not_zero", &refcount_inc, INT_PTR(0));
+
+	return_implies_state("refcount_add_not_zero", 1, 1, &match_implies_inc, INT_PTR(1));
+	return_implies_state("refcount_inc_not_zero", 1, 1, &match_implies_inc, INT_PTR(0));
+
+	return_implies_state("atomic_dec_if_positive", 0, INT_MAX, &match_implies_atomic_dec, INT_PTR(0));
+
 	add_function_hook("refcount_sub_and_test", &refcount_dec, INT_PTR(1));
 	add_function_hook("refcount_dec_and_test", &refcount_dec, INT_PTR(0));
 
+	add_function_hook("pm_runtime_get_sync", &pm_runtime_get_sync, INT_PTR(0));
+
 	add_hook(&match_check_missed, END_FUNC_HOOK);
 
 	add_hook(&match_after_func, AFTER_FUNC_HOOK);
