--- old/usr/src/tools/smatch/Makefile	Tue Jan  7 08:01:51 2020
+++ new/usr/src/tools/smatch/Makefile	Tue Jan  7 08:01:51 2020
@@ -8,7 +8,7 @@
 # source.  A copy of the CDDL is also available via the Internet at
 # http://www.illumos.org/license/CDDL.
 #
-# Copyright 2019 Joyent, Inc.
+# Copyright 2020 Joyent, Inc.
 #
 
 #
@@ -20,7 +20,7 @@
 #
 
 PROG = smatch
-SPARSE_VERSION = 0.6.1-rc1-il-2
+SPARSE_VERSION = 0.6.1-rc1-il-3
 
 include ../Makefile.tools
 
@@ -31,7 +31,8 @@
 i386_SMATCHFLAGS = -msave-args
 sparc_SMATCHFLAGS =
 
-CFLAGS = -O -m64 $($(MACH)_SMATCHFLAGS) -D__sun -Wall -Wno-unknown-pragmas -std=gnu99 -nodefaultlibs
+CFLAGS = -O -m64 -std=gnu99 -nodefaultlibs -D__sun $($(MACH)_SMATCHFLAGS)
+CFLAGS += -Wall -Wno-unknown-pragmas -Wno-psabi
 
 SMATCHDATADIR = $(ROOTONBLDSHARE)/smatch
 
@@ -176,6 +177,7 @@
 SMATCH_OBJS += smatch_unknown_value.o
 SMATCH_OBJS += smatch_untracked_param.o
 SMATCH_OBJS += smatch_var_sym.o
+SMATCH_OBJS += smatch_parsed_conditions.o
 
 SMATCH_CHECK_OBJS:sh=ls src/check_*.c | sed -e 's+\.c+.o+;s+src/++;'
 
--- old/usr/src/tools/smatch/src/Makefile	Tue Jan  7 08:01:51 2020
+++ new/usr/src/tools/smatch/src/Makefile	Tue Jan  7 08:01:51 2020
@@ -1,4 +1,4 @@
-VERSION=0.6.1-rc1-il-2
+VERSION=0.6.1-rc1-il-3
 
 ########################################################################
 # The following variables can be overwritten from the command line
@@ -95,7 +95,7 @@
 # common flags/options/...
 
 cflags = -fno-strict-aliasing
-cflags += -Wall -Wwrite-strings -Wno-switch
+cflags += -Wall -Wwrite-strings -Wno-switch -Wno-psabi
 
 GCC_BASE := $(shell $(CC) --print-file-name=)
 cflags += -DGCC_BASE=\"$(GCC_BASE)\"
@@ -254,6 +254,7 @@
 SMATCH_OBJS += smatch_param_to_mtag_data.o
 SMATCH_OBJS += smatch_param_used.o
 SMATCH_OBJS += smatch_parse_call_math.o
+SMATCH_OBJS += smatch_parsed_conditions.o
 SMATCH_OBJS += smatch_passes_array_size.o
 SMATCH_OBJS += smatch_project.o
 SMATCH_OBJS += smatch_ranges.o
--- old/usr/src/tools/smatch/src/check_debug.c	Tue Jan  7 08:01:52 2020
+++ new/usr/src/tools/smatch/src/check_debug.c	Tue Jan  7 08:01:52 2020
@@ -15,6 +15,8 @@
  * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
  */
 
+#include <string.h>
+
 #include "smatch.h"
 #include "smatch_slist.h"
 #include "smatch_extra.h"
@@ -86,7 +88,7 @@
 	}
 
 	FOR_EACH_SM(__get_cur_stree(), sm) {
-		if (strcmp(check_name(sm->owner), check_arg->string->data) != 0)
+		if (!strstr(check_name(sm->owner), check_arg->string->data))
 			continue;
 		sm_msg("%s", show_sm(sm));
 		found = 1;
@@ -514,6 +516,16 @@
 	local_debug = 0;
 }
 
+static void match_debug_db_on(const char *fn, struct expression *expr, void *info)
+{
+	debug_db = 1;
+}
+
+static void match_debug_db_off(const char *fn, struct expression *expr, void *info)
+{
+	debug_db = 0;
+}
+
 static void match_about(const char *fn, struct expression *expr, void *info)
 {
 	struct expression *arg;
@@ -773,6 +785,8 @@
 	add_function_hook("__smatch_debug_off", &match_debug_off, NULL);
 	add_function_hook("__smatch_local_debug_on", &match_local_debug_on, NULL);
 	add_function_hook("__smatch_local_debug_off", &match_local_debug_off, NULL);
+	add_function_hook("__smatch_debug_db_on", &match_debug_db_on, NULL);
+	add_function_hook("__smatch_debug_db_off", &match_debug_db_off, NULL);
 	add_function_hook("__smatch_intersection", &match_intersection, NULL);
 	add_function_hook("__smatch_type", &match_type, NULL);
 	add_implied_return_hook("__smatch_type_rl_helper", &match_type_rl_return, NULL);
--- old/usr/src/tools/smatch/src/check_debug.h	Tue Jan  7 08:01:52 2020
+++ new/usr/src/tools/smatch/src/check_debug.h	Tue Jan  7 08:01:52 2020
@@ -46,6 +46,9 @@
 static inline void __smatch_local_debug_on(void){}
 static inline void __smatch_local_debug_off(void){}
 
+static inline void __smatch_debug_db_on(void){}
+static inline void __smatch_debug_db_off(void){}
+
 static inline void __smatch_debug_implied_on(void){}
 static inline void __smatch_debug_implied_off(void){}
 
--- old/usr/src/tools/smatch/src/check_frees_param.c	Tue Jan  7 08:01:52 2020
+++ new/usr/src/tools/smatch/src/check_frees_param.c	Tue Jan  7 08:01:52 2020
@@ -29,7 +29,6 @@
 
 STATE(freed);
 STATE(ignore);
-STATE(param);
 
 static void set_ignore(struct sm_state *sm, struct expression *mod_expr)
 {
@@ -36,20 +35,6 @@
 	set_state(my_id, sm->name, sm->sym, &ignore);
 }
 
-static void match_function_def(struct symbol *sym)
-{
-	struct symbol *arg;
-	int i;
-
-	i = -1;
-	FOR_EACH_PTR(sym->ctype.base_type->arguments, arg) {
-		i++;
-		if (!arg->ident)
-			continue;
-		set_state(my_id, arg->ident->name, arg, &param);
-	} END_FOR_EACH_PTR(arg);
-}
-
 static void freed_variable(struct expression *expr)
 {
 	struct sm_state *sm;
@@ -111,8 +96,6 @@
 		return;
 	}
 
-	add_hook(&match_function_def, FUNC_DEF_HOOK);
-
 	add_function_hook("free", &match_free, INT_PTR(0));
 
 	select_return_implies_hook(PARAM_FREED, &set_param_freed);
--- old/usr/src/tools/smatch/src/check_list.h	Tue Jan  7 08:01:52 2020
+++ new/usr/src/tools/smatch/src/check_list.h	Tue Jan  7 08:01:52 2020
@@ -25,6 +25,9 @@
 CK(register_param_compare_limit_links)
 CK(register_returns_early)
 
+CK(register_param_cleared)  /* param_set relies on param_cleared */
+CK(register_param_set)
+
 CK(register_smatch_ignore)
 CK(register_buf_size)
 CK(register_strlen)
@@ -31,10 +34,7 @@
 CK(register_strlen_equiv)
 CK(register_capped)
 CK(register_parse_call_math)
-CK(register_param_limit)
 CK(register_param_filter)
-CK(register_param_set)
-CK(register_param_cleared)
 CK(register_struct_assignment)
 CK(register_comparison)
 CK(register_comparison_links)
@@ -58,6 +58,7 @@
 CK(register_bits)
 CK(register_fn_arg_link)
 CK(register_parameter_names)
+CK(register_param_limit)
 CK(register_return_to_param)
 CK(register_return_to_param_links)
 CK(register_constraints)
@@ -207,6 +208,7 @@
 CK(register_scope)
 CK(register_stored_conditions)
 CK(register_stored_conditions_links)
+CK(register_parsed_conditions)
 CK(register_sval)
 CK(register_buf_size_late)
 CK(register_smatch_extra_late)
--- old/usr/src/tools/smatch/src/check_locking.c	Tue Jan  7 08:01:52 2020
+++ new/usr/src/tools/smatch/src/check_locking.c	Tue Jan  7 08:01:52 2020
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009 Dan Carpenter.
+ * Copyright (C) 2019 Oracle.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -15,17 +16,7 @@
  * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
  */
 
-/*
- * This test checks that locks are held the same across all returns.
- *
- * Of course, some functions are designed to only hold the locks on success.
- * Oh well... We can rewrite it later if we want.
- *
- * The list of wine locking functions came from an earlier script written
- * by Michael Stefaniuc.
- *
- */
-
+#include <ctype.h>
 #include "parse.h"
 #include "smatch.h"
 #include "smatch_extra.h"
@@ -33,25 +24,55 @@
 
 static int my_id;
 
-static int func_has_transition;
-
 STATE(locked);
+STATE(half_locked);
 STATE(start_state);
 STATE(unlocked);
 STATE(impossible);
+STATE(restore);
 
 enum action {
 	LOCK,
 	UNLOCK,
+	RESTORE,
 };
 
+enum lock_type {
+	spin_lock,
+	read_lock,
+	write_lock,
+	mutex,
+	bottom_half,
+	irq,
+	sem,
+	prepare_lock,
+	enable_lock,
+};
+
+const char *get_lock_name(enum lock_type type)
+{
+	static const char *names[] = {
+		[spin_lock] = "spin_lock",
+		[read_lock] = "read_lock",
+		[write_lock] = "write_lock",
+		[mutex] = "mutex",
+		[bottom_half] = "bottom_half",
+		[irq] = "irq",
+		[sem] = "sem",
+		[prepare_lock] = "prepare_lock",
+		[enable_lock] = "enable_lock",
+	};
+
+	return names[type];
+}
+
 enum return_type {
 	ret_any,
-	ret_non_zero,
 	ret_zero,
 	ret_one,
 	ret_negative,
 	ret_positive,
+	ret_valid_ptr,
 };
 
 #define RETURN_VAL -1
@@ -60,338 +81,361 @@
 struct lock_info {
 	const char *function;
 	enum action action;
-	const char *name;
+	enum lock_type type;
 	int arg;
 	enum return_type return_type;
 };
 
-static struct lock_info wine_lock_table[] = {
-	{"create_window_handle", LOCK, "create_window_handle", RETURN_VAL, ret_non_zero},
-	{"WIN_GetPtr", LOCK, "create_window_handle", RETURN_VAL, ret_non_zero},
-	{"WIN_ReleasePtr", UNLOCK, "create_window_handle", 0, ret_any},
-	{"EnterCriticalSection", LOCK, "CriticalSection", 0, ret_any},
-	{"LeaveCriticalSection", UNLOCK, "CriticalSection", 0, ret_any},
-	{"RtlEnterCriticalSection", LOCK, "RtlCriticalSection", 0, ret_any},
-	{"RtlLeaveCriticalSection", UNLOCK, "RtlCriticalSection", 0, ret_any},
-	{"GDI_GetObjPtr", LOCK, "GDI_Get", 0, ret_non_zero},
-	{"GDI_ReleaseObj", UNLOCK, "GDI_Get", 0, ret_any},
-	{"LdrLockLoaderLock", LOCK, "LdrLockLoaderLock", 2, ret_any},
-	{"LdrUnlockLoaderLock", UNLOCK, "LdrLockLoaderLock", 1, ret_any},
-	{"_lock", LOCK, "_lock", 0, ret_any},
-	{"_unlock", UNLOCK, "_lock", 0, ret_any},
-	{"msiobj_lock", LOCK, "msiobj_lock", 0, ret_any},
-	{"msiobj_unlock", UNLOCK, "msiobj_lock", 0, ret_any},
-	{"RtlAcquirePebLock", LOCK, "PebLock", NO_ARG, ret_any},
-	{"RtlReleasePebLock", UNLOCK, "PebLock", NO_ARG, ret_any},
-	{"server_enter_uninterrupted_section", LOCK, "server_uninterrupted_section", 0, ret_any},
-	{"server_leave_uninterrupted_section", UNLOCK, "server_uninterrupted_section", 0, ret_any},
-	{"RtlLockHeap", LOCK, "RtlLockHeap", 0, ret_any},
-	{"RtlUnlockHeap", UNLOCK, "RtlLockHeap", 0, ret_any},
-	{"_EnterSysLevel", LOCK, "SysLevel", 0, ret_any},
-	{"_LeaveSysLevel", UNLOCK, "SysLevel", 0, ret_any},
-	{"USER_Lock", LOCK, "USER_Lock", NO_ARG, ret_any},
-	{"USER_Unlock", UNLOCK, "USER_Lock", NO_ARG, ret_any},
-	{"wine_tsx11_lock", LOCK, "wine_tsx11_lock", NO_ARG, ret_any},
-	{"wine_tsx11_unlock", UNLOCK, "wine_tsx11_lock", NO_ARG, ret_any},
-	{"wine_tsx11_lock_ptr", LOCK, "wine_tsx11_lock_ptr", NO_ARG, ret_any},
-	{"wine_tsx11_unlock_ptr", UNLOCK, "wine_tsx11_lock_ptr", NO_ARG, ret_any},
-	{"wined3d_mutex_lock", LOCK, "wined3d_mutex_lock", NO_ARG, ret_any},
-	{"wined3d_mutex_unlock", UNLOCK, "wined3d_mutex_lock", NO_ARG, ret_any},
-	{"X11DRV_DIB_Lock", LOCK, "X11DRV_DIB_Lock", 0, ret_any},
-	{"X11DRV_DIB_Unlock", UNLOCK, "X11DRV_DIB_Lock", 0, ret_any},
-};
+static struct lock_info lock_table[] = {
+	{"spin_lock",                  LOCK,   spin_lock, 0, ret_any},
+	{"spin_unlock",                UNLOCK, spin_lock, 0, ret_any},
+	{"spin_lock_nested",           LOCK,   spin_lock, 0, ret_any},
+	{"_spin_lock",                 LOCK,   spin_lock, 0, ret_any},
+	{"_spin_unlock",               UNLOCK, spin_lock, 0, ret_any},
+	{"_spin_lock_nested",          LOCK,   spin_lock, 0, ret_any},
+	{"__spin_lock",                LOCK,   spin_lock, 0, ret_any},
+	{"__spin_unlock",              UNLOCK, spin_lock, 0, ret_any},
+	{"__spin_lock_nested",         LOCK,   spin_lock, 0, ret_any},
+	{"raw_spin_lock",              LOCK,   spin_lock, 0, ret_any},
+	{"raw_spin_unlock",            UNLOCK, spin_lock, 0, ret_any},
+	{"_raw_spin_lock",             LOCK,   spin_lock, 0, ret_any},
+	{"_raw_spin_lock_nested",      LOCK,   spin_lock, 0, ret_any},
+	{"_raw_spin_unlock",           UNLOCK, spin_lock, 0, ret_any},
+	{"__raw_spin_lock",            LOCK,   spin_lock, 0, ret_any},
+	{"__raw_spin_unlock",          UNLOCK, spin_lock, 0, ret_any},
 
-static struct lock_info kernel_lock_table[] = {
-	{"lock_kernel",   LOCK,   "BKL", NO_ARG, ret_any},
-	{"unlock_kernel", UNLOCK, "BKL", NO_ARG, ret_any},
+	{"spin_lock_irq",                 LOCK,   spin_lock, 0, ret_any},
+	{"spin_unlock_irq",               UNLOCK, spin_lock, 0, ret_any},
+	{"_spin_lock_irq",                LOCK,   spin_lock, 0, ret_any},
+	{"_spin_unlock_irq",              UNLOCK, spin_lock, 0, ret_any},
+	{"__spin_lock_irq",               LOCK,   spin_lock, 0, ret_any},
+	{"__spin_unlock_irq",             UNLOCK, spin_lock, 0, ret_any},
+	{"_raw_spin_lock_irq",            LOCK,   spin_lock, 0, ret_any},
+	{"_raw_spin_unlock_irq",          UNLOCK, spin_lock, 0, ret_any},
+	{"__raw_spin_unlock_irq",         UNLOCK, spin_lock, 0, ret_any},
+	{"spin_lock_irqsave",             LOCK,   spin_lock, 0, ret_any},
+	{"spin_unlock_irqrestore",        UNLOCK, spin_lock, 0, ret_any},
+	{"_spin_lock_irqsave",            LOCK,   spin_lock, 0, ret_any},
+	{"_spin_unlock_irqrestore",       UNLOCK, spin_lock, 0, ret_any},
+	{"__spin_lock_irqsave",           LOCK,   spin_lock, 0, ret_any},
+	{"__spin_unlock_irqrestore",      UNLOCK, spin_lock, 0, ret_any},
+	{"_raw_spin_lock_irqsave",        LOCK,   spin_lock, 0, ret_any},
+	{"_raw_spin_unlock_irqrestore",   UNLOCK, spin_lock, 0, ret_any},
+	{"__raw_spin_lock_irqsave",       LOCK,   spin_lock, 0, ret_any},
+	{"__raw_spin_unlock_irqrestore",  UNLOCK, spin_lock, 0, ret_any},
+	{"spin_lock_irqsave_nested",      LOCK,   spin_lock, 0, ret_any},
+	{"_spin_lock_irqsave_nested",     LOCK,   spin_lock, 0, ret_any},
+	{"__spin_lock_irqsave_nested",    LOCK,   spin_lock, 0, ret_any},
+	{"_raw_spin_lock_irqsave_nested", LOCK,   spin_lock, 0, ret_any},
+	{"spin_lock_bh",                  LOCK,   spin_lock, 0, ret_any},
+	{"spin_unlock_bh",                UNLOCK, spin_lock, 0, ret_any},
+	{"_spin_lock_bh",                 LOCK,   spin_lock, 0, ret_any},
+	{"_spin_unlock_bh",               UNLOCK, spin_lock, 0, ret_any},
+	{"__spin_lock_bh",                LOCK,   spin_lock, 0, ret_any},
+	{"__spin_unlock_bh",              UNLOCK, spin_lock, 0, ret_any},
 
-	{"spin_lock",                  LOCK,   "spin_lock", 0, ret_any},
-	{"spin_unlock",                UNLOCK, "spin_lock", 0, ret_any},
-	{"spin_lock_nested",           LOCK,   "spin_lock", 0, ret_any},
-	{"_spin_lock",                 LOCK,   "spin_lock", 0, ret_any},
-	{"_spin_unlock",               UNLOCK, "spin_lock", 0, ret_any},
-	{"_spin_lock_nested",          LOCK,   "spin_lock", 0, ret_any},
-	{"__spin_lock",                LOCK,   "spin_lock", 0, ret_any},
-	{"__spin_unlock",              UNLOCK, "spin_lock", 0, ret_any},
-	{"__spin_lock_nested",         LOCK,   "spin_lock", 0, ret_any},
-	{"raw_spin_lock",              LOCK,   "spin_lock", 0, ret_any},
-	{"raw_spin_unlock",            UNLOCK, "spin_lock", 0, ret_any},
-	{"_raw_spin_lock",             LOCK,   "spin_lock", 0, ret_any},
-	{"_raw_spin_lock_nested",      LOCK,   "spin_lock", 0, ret_any},
-	{"_raw_spin_unlock",           UNLOCK, "spin_lock", 0, ret_any},
-	{"__raw_spin_lock",            LOCK,   "spin_lock", 0, ret_any},
-	{"__raw_spin_unlock",          UNLOCK, "spin_lock", 0, ret_any},
+	{"spin_trylock",               LOCK,   spin_lock, 0, ret_one},
+	{"_spin_trylock",              LOCK,   spin_lock, 0, ret_one},
+	{"__spin_trylock",             LOCK,   spin_lock, 0, ret_one},
+	{"raw_spin_trylock",           LOCK,   spin_lock, 0, ret_one},
+	{"_raw_spin_trylock",          LOCK,   spin_lock, 0, ret_one},
+	{"spin_trylock_irq",           LOCK,   spin_lock, 0, ret_one},
+	{"spin_trylock_irqsave",       LOCK,   spin_lock, 0, ret_one},
+	{"spin_trylock_bh",            LOCK,   spin_lock, 0, ret_one},
+	{"_spin_trylock_bh",           LOCK,   spin_lock, 0, ret_one},
+	{"__spin_trylock_bh",          LOCK,   spin_lock, 0, ret_one},
+	{"__raw_spin_trylock",         LOCK,   spin_lock, 0, ret_one},
+	{"_atomic_dec_and_lock",       LOCK,   spin_lock, 1, ret_one},
 
-	{"spin_lock_irq",              LOCK,   "spin_lock", 0, ret_any},
-	{"spin_unlock_irq",            UNLOCK, "spin_lock", 0, ret_any},
-	{"_spin_lock_irq",             LOCK,   "spin_lock", 0, ret_any},
-	{"_spin_unlock_irq",           UNLOCK, "spin_lock", 0, ret_any},
-	{"__spin_lock_irq",            LOCK,   "spin_lock", 0, ret_any},
-	{"__spin_unlock_irq",          UNLOCK, "spin_lock", 0, ret_any},
-	{"_raw_spin_lock_irq",         LOCK,   "spin_lock", 0, ret_any},
-	{"_raw_spin_unlock_irq",       UNLOCK, "spin_lock", 0, ret_any},
-	{"__raw_spin_unlock_irq",      UNLOCK, "spin_lock", 0, ret_any},
-	{"spin_lock_irqsave",          LOCK,   "spin_lock", 0, ret_any},
-	{"spin_unlock_irqrestore",     UNLOCK, "spin_lock", 0, ret_any},
-	{"_spin_lock_irqsave",         LOCK,   "spin_lock", 0, ret_any},
-	{"_spin_unlock_irqrestore",    UNLOCK, "spin_lock", 0, ret_any},
-	{"__spin_lock_irqsave",        LOCK,   "spin_lock", 0, ret_any},
-	{"__spin_unlock_irqrestore",   UNLOCK, "spin_lock", 0, ret_any},
-	{"_raw_spin_lock_irqsave",     LOCK,   "spin_lock", 0, ret_any},
-	{"_raw_spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any},
-	{"__raw_spin_lock_irqsave",    LOCK,   "spin_lock", 0, ret_any},
-	{"__raw_spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any},
-	{"spin_lock_irqsave_nested",   LOCK,   "spin_lock", 0, ret_any},
-	{"_spin_lock_irqsave_nested",  LOCK,   "spin_lock", 0, ret_any},
-	{"__spin_lock_irqsave_nested", LOCK,   "spin_lock", 0, ret_any},
-	{"_raw_spin_lock_irqsave_nested", LOCK, "spin_lock", 0, ret_any},
-	{"spin_lock_bh",               LOCK,   "spin_lock", 0, ret_any},
-	{"spin_unlock_bh",             UNLOCK, "spin_lock", 0, ret_any},
-	{"_spin_lock_bh",              LOCK,   "spin_lock", 0, ret_any},
-	{"_spin_unlock_bh",            UNLOCK, "spin_lock", 0, ret_any},
-	{"__spin_lock_bh",             LOCK,   "spin_lock", 0, ret_any},
-	{"__spin_unlock_bh",           UNLOCK, "spin_lock", 0, ret_any},
+	{"read_lock",                 LOCK,   read_lock, 0, ret_any},
+	{"down_read",                 LOCK,   read_lock, 0, ret_any},
+	{"down_read_nested",          LOCK,   read_lock, 0, ret_any},
+	{"down_read_trylock",         LOCK,   read_lock, 0, ret_one},
+	{"up_read",                   UNLOCK, read_lock, 0, ret_any},
+	{"read_unlock",               UNLOCK, read_lock, 0, ret_any},
+	{"_read_lock",                LOCK,   read_lock, 0, ret_any},
+	{"_read_unlock",              UNLOCK, read_lock, 0, ret_any},
+	{"__read_lock",               LOCK,   read_lock, 0, ret_any},
+	{"__read_unlock",             UNLOCK, read_lock, 0, ret_any},
+	{"_raw_read_lock",            LOCK,   read_lock, 0, ret_any},
+	{"_raw_read_unlock",          UNLOCK, read_lock, 0, ret_any},
+	{"__raw_read_lock",           LOCK,   read_lock, 0, ret_any},
+	{"__raw_read_unlock",         UNLOCK, read_lock, 0, ret_any},
+	{"read_lock_irq",             LOCK,   read_lock, 0, ret_any},
+	{"read_unlock_irq" ,          UNLOCK, read_lock, 0, ret_any},
+	{"_read_lock_irq",            LOCK,   read_lock, 0, ret_any},
+	{"_read_unlock_irq",          UNLOCK, read_lock, 0, ret_any},
+	{"__read_lock_irq",           LOCK,   read_lock, 0, ret_any},
+	{"__read_unlock_irq",         UNLOCK, read_lock, 0, ret_any},
+	{"_raw_read_unlock_irq",      UNLOCK, read_lock, 0, ret_any},
+	{"_raw_read_lock_irq",        LOCK,   read_lock, 0, ret_any},
+	{"_raw_read_lock_bh",         LOCK,   read_lock, 0, ret_any},
+	{"_raw_read_unlock_bh",       UNLOCK, read_lock, 0, ret_any},
+	{"read_lock_irqsave",         LOCK,   read_lock, 0, ret_any},
+	{"read_unlock_irqrestore",    UNLOCK, read_lock, 0, ret_any},
+	{"_read_lock_irqsave",        LOCK,   read_lock, 0, ret_any},
+	{"_read_unlock_irqrestore",   UNLOCK, read_lock, 0, ret_any},
+	{"__read_lock_irqsave",       LOCK,   read_lock, 0, ret_any},
+	{"__read_unlock_irqrestore",  UNLOCK, read_lock, 0, ret_any},
+	{"read_lock_bh",              LOCK,   read_lock, 0, ret_any},
+	{"read_unlock_bh",            UNLOCK, read_lock, 0, ret_any},
+	{"_read_lock_bh",             LOCK,   read_lock, 0, ret_any},
+	{"_read_unlock_bh",           UNLOCK, read_lock, 0, ret_any},
+	{"__read_lock_bh",            LOCK,   read_lock, 0, ret_any},
+	{"__read_unlock_bh",          UNLOCK, read_lock, 0, ret_any},
+	{"__raw_read_lock_bh",        LOCK,   read_lock, 0, ret_any},
+	{"__raw_read_unlock_bh",      UNLOCK, read_lock, 0, ret_any},
 
-	{"spin_trylock",               LOCK,   "spin_lock", 0, ret_one},
-	{"_spin_trylock",              LOCK,   "spin_lock", 0, ret_one},
-	{"__spin_trylock",             LOCK,   "spin_lock", 0, ret_one},
-	{"raw_spin_trylock",           LOCK,   "spin_lock", 0, ret_one},
-	{"_raw_spin_trylock",          LOCK,   "spin_lock", 0, ret_one},
-	{"spin_trylock_irq",           LOCK,   "spin_lock", 0, ret_one},
-	{"spin_trylock_irqsave",       LOCK,   "spin_lock", 0, ret_one},
-	{"spin_trylock_bh",            LOCK,   "spin_lock", 0, ret_one},
-	{"_spin_trylock_bh",           LOCK,   "spin_lock", 0, ret_one},
-	{"__spin_trylock_bh",          LOCK,   "spin_lock", 0, ret_one},
-	{"__raw_spin_trylock",         LOCK,   "spin_lock", 0, ret_one},
-	{"_atomic_dec_and_lock",       LOCK,   "spin_lock", 1, ret_one},
+	{"_raw_read_lock_irqsave",        LOCK,    read_lock,   0,          ret_any},
+	{"_raw_read_lock_irqsave",        LOCK,    irq,	        RETURN_VAL, ret_any},
+	{"_raw_read_unlock_irqrestore",   UNLOCK,  read_lock,   0,          ret_any},
+	{"_raw_read_unlock_irqrestore",   RESTORE, irq,         1,          ret_any},
+	{"_raw_spin_lock_bh",             LOCK,    read_lock,   0,          ret_any},
+	{"_raw_spin_lock_bh",             LOCK,    bottom_half, NO_ARG,     ret_any},
+	{"_raw_spin_lock_nest_lock",      LOCK,    read_lock,   0,          ret_any},
+	{"_raw_spin_unlock_bh",           UNLOCK,  read_lock,   0,          ret_any},
+	{"_raw_spin_unlock_bh",           UNLOCK,  bottom_half, NO_ARG,     ret_any},
+	{"_raw_write_lock_irqsave",       LOCK,    write_lock,  0,          ret_any},
+	{"_raw_write_lock_irqsave",       LOCK,    irq,         RETURN_VAL, ret_any},
+	{"_raw_write_unlock_irqrestore",  UNLOCK,  write_lock,  0,          ret_any},
+	{"_raw_write_unlock_irqrestore",  RESTORE, irq,         1,          ret_any},
+	{"__raw_write_unlock_irqrestore", UNLOCK,  write_lock,  0,          ret_any},
+	{"__raw_write_unlock_irqrestore", RESTORE, irq,         1,          ret_any},
 
-	{"read_lock",                 LOCK,   "read_lock", 0, ret_any},
-	{"read_unlock",               UNLOCK, "read_lock", 0, ret_any},
-	{"_read_lock",                LOCK,   "read_lock", 0, ret_any},
-	{"_read_unlock",              UNLOCK, "read_lock", 0, ret_any},
-	{"__read_lock",               LOCK,   "read_lock", 0, ret_any},
-	{"__read_unlock",             UNLOCK, "read_lock", 0, ret_any},
-	{"_raw_read_lock",            LOCK,   "read_lock", 0, ret_any},
-	{"_raw_read_unlock",          UNLOCK, "read_lock", 0, ret_any},
-	{"__raw_read_lock",           LOCK,   "read_lock", 0, ret_any},
-	{"__raw_read_unlock",         UNLOCK, "read_lock", 0, ret_any},
-	{"read_lock_irq",             LOCK,   "read_lock", 0, ret_any},
-	{"read_unlock_irq" ,          UNLOCK, "read_lock", 0, ret_any},
-	{"_read_lock_irq",            LOCK,   "read_lock", 0, ret_any},
-	{"_read_unlock_irq",          UNLOCK, "read_lock", 0, ret_any},
-	{"__read_lock_irq",           LOCK,   "read_lock", 0, ret_any},
-	{"__read_unlock_irq",         UNLOCK, "read_lock", 0, ret_any},
-	{"read_lock_irqsave",         LOCK,   "read_lock", 0, ret_any},
-	{"read_unlock_irqrestore",    UNLOCK, "read_lock", 0, ret_any},
-	{"_read_lock_irqsave",        LOCK,   "read_lock", 0, ret_any},
-	{"_read_unlock_irqrestore",   UNLOCK, "read_lock", 0, ret_any},
-	{"__read_lock_irqsave",       LOCK,   "read_lock", 0, ret_any},
-	{"__read_unlock_irqrestore",  UNLOCK, "read_lock", 0, ret_any},
-	{"read_lock_bh",              LOCK,   "read_lock", 0, ret_any},
-	{"read_unlock_bh",            UNLOCK, "read_lock", 0, ret_any},
-	{"_read_lock_bh",             LOCK,   "read_lock", 0, ret_any},
-	{"_read_unlock_bh",           UNLOCK, "read_lock", 0, ret_any},
-	{"__read_lock_bh",            LOCK,   "read_lock", 0, ret_any},
-	{"__read_unlock_bh",          UNLOCK, "read_lock", 0, ret_any},
-	{"_raw_read_lock_bh",         LOCK,   "read_lock", 0, ret_any},
-	{"_raw_read_unlock_bh",       UNLOCK, "read_lock", 0, ret_any},
-	{"__raw_read_lock_bh",        LOCK,   "read_lock", 0, ret_any},
-	{"__raw_read_unlock_bh",      UNLOCK, "read_lock", 0, ret_any},
+	{"generic__raw_read_trylock", LOCK,   read_lock, 0, ret_one},
+	{"read_trylock",              LOCK,   read_lock, 0, ret_one},
+	{"_read_trylock",             LOCK,   read_lock, 0, ret_one},
+	{"raw_read_trylock",          LOCK,   read_lock, 0, ret_one},
+	{"_raw_read_trylock",         LOCK,   read_lock, 0, ret_one},
+	{"__raw_read_trylock",        LOCK,   read_lock, 0, ret_one},
+	{"__read_trylock",            LOCK,   read_lock, 0, ret_one},
 
-	{"generic__raw_read_trylock", LOCK,   "read_lock", 0, ret_one},
-	{"read_trylock",              LOCK,   "read_lock", 0, ret_one},
-	{"_read_trylock",             LOCK,   "read_lock", 0, ret_one},
-	{"raw_read_trylock",          LOCK,   "read_lock", 0, ret_one},
-	{"_raw_read_trylock",         LOCK,   "read_lock", 0, ret_one},
-	{"__raw_read_trylock",        LOCK,   "read_lock", 0, ret_one},
-	{"__read_trylock",            LOCK,   "read_lock", 0, ret_one},
+	{"write_lock",                LOCK,   write_lock, 0, ret_any},
+	{"down_write",                LOCK,   write_lock, 0, ret_any},
+	{"down_write_nested",         LOCK,   write_lock, 0, ret_any},
+	{"up_write",                  UNLOCK, write_lock, 0, ret_any},
+	{"write_unlock",              UNLOCK, write_lock, 0, ret_any},
+	{"_write_lock",               LOCK,   write_lock, 0, ret_any},
+	{"_write_unlock",             UNLOCK, write_lock, 0, ret_any},
+	{"__write_lock",              LOCK,   write_lock, 0, ret_any},
+	{"__write_unlock",            UNLOCK, write_lock, 0, ret_any},
+	{"write_lock_irq",            LOCK,   write_lock, 0, ret_any},
+	{"write_unlock_irq",          UNLOCK, write_lock, 0, ret_any},
+	{"_write_lock_irq",           LOCK,   write_lock, 0, ret_any},
+	{"_write_unlock_irq",         UNLOCK, write_lock, 0, ret_any},
+	{"__write_lock_irq",          LOCK,   write_lock, 0, ret_any},
+	{"__write_unlock_irq",        UNLOCK, write_lock, 0, ret_any},
+	{"_raw_write_unlock_irq",     UNLOCK, write_lock, 0, ret_any},
+	{"write_lock_irqsave",        LOCK,   write_lock, 0, ret_any},
+	{"write_unlock_irqrestore",   UNLOCK, write_lock, 0, ret_any},
+	{"_write_lock_irqsave",       LOCK,   write_lock, 0, ret_any},
+	{"_write_unlock_irqrestore",  UNLOCK, write_lock, 0, ret_any},
+	{"__write_lock_irqsave",      LOCK,   write_lock, 0, ret_any},
+	{"__write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any},
+	{"write_lock_bh",             LOCK,   write_lock, 0, ret_any},
+	{"write_unlock_bh",           UNLOCK, write_lock, 0, ret_any},
+	{"_write_lock_bh",            LOCK,   write_lock, 0, ret_any},
+	{"_write_unlock_bh",          UNLOCK, write_lock, 0, ret_any},
+	{"__write_lock_bh",           LOCK,   write_lock, 0, ret_any},
+	{"__write_unlock_bh",         UNLOCK, write_lock, 0, ret_any},
+	{"_raw_write_lock",           LOCK,   write_lock, 0, ret_any},
+	{"__raw_write_lock",          LOCK,   write_lock, 0, ret_any},
+	{"_raw_write_unlock",         UNLOCK, write_lock, 0, ret_any},
+	{"__raw_write_unlock",        UNLOCK, write_lock, 0, ret_any},
+	{"_raw_write_lock_bh",        LOCK,   write_lock, 0, ret_any},
+	{"_raw_write_unlock_bh",      UNLOCK, write_lock, 0, ret_any},
+	{"_raw_write_lock_irq",       LOCK,   write_lock, 0, ret_any},
 
-	{"write_lock",                LOCK,   "write_lock", 0, ret_any},
-	{"write_unlock",              UNLOCK, "write_lock", 0, ret_any},
-	{"_write_lock",               LOCK,   "write_lock", 0, ret_any},
-	{"_write_unlock",             UNLOCK, "write_lock", 0, ret_any},
-	{"__write_lock",              LOCK,   "write_lock", 0, ret_any},
-	{"__write_unlock",            UNLOCK, "write_lock", 0, ret_any},
-	{"write_lock_irq",            LOCK,   "write_lock", 0, ret_any},
-	{"write_unlock_irq",          UNLOCK, "write_lock", 0, ret_any},
-	{"_write_lock_irq",           LOCK,   "write_lock", 0, ret_any},
-	{"_write_unlock_irq",         UNLOCK, "write_lock", 0, ret_any},
-	{"__write_lock_irq",          LOCK,   "write_lock", 0, ret_any},
-	{"__write_unlock_irq",        UNLOCK, "write_lock", 0, ret_any},
-	{"write_lock_irqsave",        LOCK,   "write_lock", 0, ret_any},
-	{"write_unlock_irqrestore",   UNLOCK, "write_lock", 0, ret_any},
-	{"_write_lock_irqsave",       LOCK,   "write_lock", 0, ret_any},
-	{"_write_unlock_irqrestore",  UNLOCK, "write_lock", 0, ret_any},
-	{"__write_lock_irqsave",      LOCK,   "write_lock", 0, ret_any},
-	{"__write_unlock_irqrestore", UNLOCK, "write_lock", 0, ret_any},
-	{"write_lock_bh",             LOCK,   "write_lock", 0, ret_any},
-	{"write_unlock_bh",           UNLOCK, "write_lock", 0, ret_any},
-	{"_write_lock_bh",            LOCK,   "write_lock", 0, ret_any},
-	{"_write_unlock_bh",          UNLOCK, "write_lock", 0, ret_any},
-	{"__write_lock_bh",           LOCK,   "write_lock", 0, ret_any},
-	{"__write_unlock_bh",         UNLOCK, "write_lock", 0, ret_any},
-	{"_raw_write_lock",           LOCK,   "write_lock", 0, ret_any},
-	{"__raw_write_lock",          LOCK,   "write_lock", 0, ret_any},
-	{"_raw_write_unlock",         UNLOCK, "write_lock", 0, ret_any},
-	{"__raw_write_unlock",        UNLOCK, "write_lock", 0, ret_any},
+	{"write_trylock",             LOCK,   write_lock, 0, ret_one},
+	{"_write_trylock",            LOCK,   write_lock, 0, ret_one},
+	{"raw_write_trylock",         LOCK,   write_lock, 0, ret_one},
+	{"_raw_write_trylock",        LOCK,   write_lock, 0, ret_one},
+	{"__write_trylock",           LOCK,   write_lock, 0, ret_one},
+	{"__raw_write_trylock",       LOCK,   write_lock, 0, ret_one},
+	{"down_write_trylock",        LOCK,   write_lock, 0, ret_one},
+	{"down_write_killable",       LOCK,   write_lock, 0, ret_zero},
 
-	{"write_trylock",             LOCK,   "write_lock", 0, ret_one},
-	{"_write_trylock",            LOCK,   "write_lock", 0, ret_one},
-	{"raw_write_trylock",         LOCK,   "write_lock", 0, ret_one},
-	{"_raw_write_trylock",        LOCK,   "write_lock", 0, ret_one},
-	{"__write_trylock",           LOCK,   "write_lock", 0, ret_one},
-	{"__raw_write_trylock",       LOCK,   "write_lock", 0, ret_one},
+	{"down",               LOCK,   sem, 0, ret_any},
+	{"up",                 UNLOCK, sem, 0, ret_any},
+	{"down_trylock",       LOCK,   sem, 0, ret_zero},
+	{"down_timeout",       LOCK,   sem, 0, ret_zero},
+	{"down_interruptible", LOCK,   sem, 0, ret_zero},
+	{"down_killable",      LOCK,   sem, 0, ret_zero},
 
-	{"down",               LOCK,   "sem", 0, ret_any},
-	{"up",                 UNLOCK, "sem", 0, ret_any},
-	{"down_trylock",       LOCK,   "sem", 0, ret_zero},
-	{"down_timeout",       LOCK,   "sem", 0, ret_zero},
-	{"down_interruptible", LOCK,   "sem", 0, ret_zero},
 
+	{"mutex_lock",                      LOCK,   mutex, 0, ret_any},
+	{"mutex_unlock",                    UNLOCK, mutex, 0, ret_any},
+	{"mutex_lock_nested",               LOCK,   mutex, 0, ret_any},
+	{"mutex_lock_io",                   LOCK,   mutex, 0, ret_any},
+	{"mutex_lock_io_nested",            LOCK,   mutex, 0, ret_any},
 
-	{"down_write",          LOCK,   "rw_sem", 0, ret_any},
-	{"downgrade_write",     UNLOCK, "rw_sem", 0, ret_any},
-	{"downgrade_write",     LOCK,   "read_sem", 0, ret_any},
-	{"up_write",            UNLOCK, "rw_sem", 0, ret_any},
-	{"down_write_trylock",  LOCK,   "rw_sem", 0, ret_one},
-	{"down_write_killable", LOCK,   "rw_sem", 0, ret_zero},
-	{"down_read",           LOCK,   "read_sem", 0, ret_any},
-	{"down_read_trylock",   LOCK,   "read_sem", 0, ret_one},
-	{"down_read_killable",  LOCK,   "read_sem", 0, ret_zero},
-	{"up_read",             UNLOCK, "read_sem", 0, ret_any},
+	{"mutex_lock_interruptible",        LOCK,   mutex, 0, ret_zero},
+	{"mutex_lock_interruptible_nested", LOCK,   mutex, 0, ret_zero},
+	{"mutex_lock_killable",             LOCK,   mutex, 0, ret_zero},
+	{"mutex_lock_killable_nested",      LOCK,   mutex, 0, ret_zero},
 
-	{"mutex_lock",                      LOCK,   "mutex", 0, ret_any},
-	{"mutex_lock_io",                   LOCK,   "mutex", 0, ret_any},
-	{"mutex_unlock",                    UNLOCK, "mutex", 0, ret_any},
-	{"mutex_lock_nested",               LOCK,   "mutex", 0, ret_any},
-	{"mutex_lock_io_nested",            LOCK,   "mutex", 0, ret_any},
+	{"mutex_trylock",                   LOCK,   mutex, 0, ret_one},
 
-	{"mutex_lock_interruptible",        LOCK,   "mutex", 0, ret_zero},
-	{"mutex_lock_interruptible_nested", LOCK,   "mutex", 0, ret_zero},
-	{"mutex_lock_killable",             LOCK,   "mutex", 0, ret_zero},
-	{"mutex_lock_killable_nested",      LOCK,   "mutex", 0, ret_zero},
+	{"ww_mutex_lock",		LOCK,   mutex, 0, ret_any},
+	{"__ww_mutex_lock",		LOCK,   mutex, 0, ret_any},
+	{"ww_mutex_lock_interruptible",	LOCK,   mutex, 0, ret_zero},
+	{"ww_mutex_unlock",		UNLOCK, mutex, 0, ret_any},
 
-	{"mutex_trylock",                   LOCK,   "mutex", 0, ret_one},
+	{"raw_local_irq_disable", LOCK,   irq, NO_ARG, ret_any},
+	{"raw_local_irq_enable",  UNLOCK, irq, NO_ARG, ret_any},
+	{"spin_lock_irq",         LOCK,   irq, NO_ARG, ret_any},
+	{"spin_unlock_irq",       UNLOCK, irq, NO_ARG, ret_any},
+	{"_spin_lock_irq",        LOCK,   irq, NO_ARG, ret_any},
+	{"_spin_unlock_irq",      UNLOCK, irq, NO_ARG, ret_any},
+	{"__spin_lock_irq",       LOCK,   irq, NO_ARG, ret_any},
+	{"__spin_unlock_irq",     UNLOCK, irq, NO_ARG, ret_any},
+	{"_raw_spin_lock_irq",    LOCK,   irq, NO_ARG, ret_any},
+	{"_raw_spin_unlock_irq",  UNLOCK, irq, NO_ARG, ret_any},
+	{"__raw_spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
+	{"spin_trylock_irq",      LOCK,   irq, NO_ARG, ret_one},
+	{"read_lock_irq",         LOCK,   irq, NO_ARG, ret_any},
+	{"read_unlock_irq",       UNLOCK, irq, NO_ARG, ret_any},
+	{"_read_lock_irq",        LOCK,   irq, NO_ARG, ret_any},
+	{"_read_unlock_irq",      UNLOCK, irq, NO_ARG, ret_any},
+	{"__read_lock_irq",       LOCK,   irq, NO_ARG, ret_any},
+	{"_raw_read_lock_irq",    LOCK,   irq, NO_ARG, ret_any},
+	{"__read_unlock_irq",     UNLOCK, irq, NO_ARG, ret_any},
+	{"_raw_read_unlock_irq",  UNLOCK, irq, NO_ARG, ret_any},
+	{"write_lock_irq",        LOCK,   irq, NO_ARG, ret_any},
+	{"write_unlock_irq",      UNLOCK, irq, NO_ARG, ret_any},
+	{"_write_lock_irq",       LOCK,   irq, NO_ARG, ret_any},
+	{"_write_unlock_irq",     UNLOCK, irq, NO_ARG, ret_any},
+	{"__write_lock_irq",      LOCK,   irq, NO_ARG, ret_any},
+	{"__write_unlock_irq",    UNLOCK, irq, NO_ARG, ret_any},
+	{"_raw_write_lock_irq",   LOCK,   irq, NO_ARG, ret_any},
+	{"_raw_write_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
 
-	{"raw_local_irq_disable", LOCK,   "irq", NO_ARG, ret_any},
-	{"raw_local_irq_enable",  UNLOCK, "irq", NO_ARG, ret_any},
-	{"spin_lock_irq",         LOCK,   "irq", NO_ARG, ret_any},
-	{"spin_unlock_irq",       UNLOCK, "irq", NO_ARG, ret_any},
-	{"_spin_lock_irq",        LOCK,   "irq", NO_ARG, ret_any},
-	{"_spin_unlock_irq",      UNLOCK, "irq", NO_ARG, ret_any},
-	{"__spin_lock_irq",       LOCK,   "irq", NO_ARG, ret_any},
-	{"__spin_unlock_irq",     UNLOCK, "irq", NO_ARG, ret_any},
-	{"_raw_spin_lock_irq",    LOCK,   "irq", NO_ARG, ret_any},
-	{"_raw_spin_unlock_irq",  UNLOCK, "irq", NO_ARG, ret_any},
-	{"__raw_spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any},
-	{"spin_trylock_irq",      LOCK,   "irq", NO_ARG, ret_one},
-	{"read_lock_irq",         LOCK,   "irq", NO_ARG, ret_any},
-	{"read_unlock_irq",       UNLOCK, "irq", NO_ARG, ret_any},
-	{"_read_lock_irq",        LOCK,   "irq", NO_ARG, ret_any},
-	{"_read_unlock_irq",      UNLOCK, "irq", NO_ARG, ret_any},
-	{"__read_lock_irq",       LOCK,   "irq", NO_ARG, ret_any},
-	{"__read_unlock_irq",     UNLOCK, "irq", NO_ARG, ret_any},
-	{"write_lock_irq",        LOCK,   "irq", NO_ARG, ret_any},
-	{"write_unlock_irq",      UNLOCK, "irq", NO_ARG, ret_any},
-	{"_write_lock_irq",       LOCK,   "irq", NO_ARG, ret_any},
-	{"_write_unlock_irq",     UNLOCK, "irq", NO_ARG, ret_any},
-	{"__write_lock_irq",      LOCK,   "irq", NO_ARG, ret_any},
-	{"__write_unlock_irq",    UNLOCK, "irq", NO_ARG, ret_any},
+	{"arch_local_irq_save",        LOCK,      irq, RETURN_VAL, ret_any},
+	{"arch_local_irq_restore",     RESTORE,   irq, 0,	   ret_any},
+	{"__raw_local_irq_save",       LOCK,      irq, RETURN_VAL, ret_any},
+	{"raw_local_irq_restore",      RESTORE,   irq, 0,	   ret_any},
+	{"spin_lock_irqsave_nested",   LOCK,      irq, RETURN_VAL, ret_any},
+	{"spin_lock_irqsave",          LOCK,      irq, 1,	   ret_any},
+	{"spin_unlock_irqrestore",     RESTORE,   irq, 1,	   ret_any},
+	{"_spin_lock_irqsave_nested",  LOCK,      irq, RETURN_VAL, ret_any},
+	{"_spin_lock_irqsave",         LOCK,      irq, RETURN_VAL, ret_any},
+	{"_spin_lock_irqsave",         LOCK,      irq, 1,	   ret_any},
+	{"_spin_unlock_irqrestore",    RESTORE,   irq, 1,	   ret_any},
+	{"__spin_lock_irqsave_nested", LOCK,      irq, 1,	   ret_any},
+	{"__spin_lock_irqsave",        LOCK,      irq, 1,	   ret_any},
+	{"__spin_unlock_irqrestore",   RESTORE,   irq, 1,	   ret_any},
+	{"_raw_spin_lock_irqsave",     LOCK,      irq, RETURN_VAL, ret_any},
+	{"_raw_spin_lock_irqsave",     LOCK,      irq, 1,	   ret_any},
+	{"_raw_spin_unlock_irqrestore", RESTORE,  irq, 1,	   ret_any},
+	{"__raw_spin_lock_irqsave",    LOCK,      irq, RETURN_VAL, ret_any},
+	{"__raw_spin_unlock_irqrestore", RESTORE, irq, 1,	   ret_any},
+	{"_raw_spin_lock_irqsave_nested", LOCK,   irq, RETURN_VAL, ret_any},
+	{"spin_trylock_irqsave",       LOCK,      irq, 1,	   ret_one},
+	{"read_lock_irqsave",          LOCK,      irq, RETURN_VAL, ret_any},
+	{"read_lock_irqsave",          LOCK,      irq, 1,	   ret_any},
+	{"read_unlock_irqrestore",     RESTORE,   irq, 1,	   ret_any},
+	{"_read_lock_irqsave",         LOCK,      irq, RETURN_VAL, ret_any},
+	{"_read_lock_irqsave",         LOCK,      irq, 1,	   ret_any},
+	{"_read_unlock_irqrestore",    RESTORE,   irq, 1,	   ret_any},
+	{"__read_lock_irqsave",        LOCK,      irq, RETURN_VAL, ret_any},
+	{"__read_unlock_irqrestore",   RESTORE,   irq, 1,	   ret_any},
+	{"write_lock_irqsave",         LOCK,      irq, RETURN_VAL, ret_any},
+	{"write_lock_irqsave",         LOCK,      irq, 1,	   ret_any},
+	{"write_unlock_irqrestore",    RESTORE,   irq, 1,	   ret_any},
+	{"_write_lock_irqsave",        LOCK,      irq, RETURN_VAL, ret_any},
+	{"_write_lock_irqsave",        LOCK,      irq, 1,	   ret_any},
+	{"_write_unlock_irqrestore",   RESTORE,   irq, 1,	   ret_any},
+	{"__write_lock_irqsave",       LOCK,      irq, RETURN_VAL, ret_any},
+	{"__write_unlock_irqrestore",  RESTORE,   irq, 1,	   ret_any},
 
-	{"arch_local_irq_save",        LOCK,   "irqsave", RETURN_VAL, ret_any},
-	{"arch_local_irq_restore",     UNLOCK, "irqsave", 0, ret_any},
-	{"__raw_local_irq_save",       LOCK,   "irqsave", RETURN_VAL, ret_any},
-	{"raw_local_irq_restore",      UNLOCK, "irqsave", 0, ret_any},
-	{"spin_lock_irqsave_nested",   LOCK,   "irqsave", RETURN_VAL, ret_any},
-	{"spin_lock_irqsave",          LOCK,   "irqsave", RETURN_VAL, ret_any},
-	{"spin_lock_irqsave",          LOCK,   "irqsave", 1, ret_any},
-	{"spin_unlock_irqrestore",     UNLOCK, "irqsave", 1, ret_any},
-	{"_spin_lock_irqsave_nested",  LOCK,   "irqsave", RETURN_VAL, ret_any},
-	{"_spin_lock_irqsave",         LOCK,   "irqsave", RETURN_VAL, ret_any},
-	{"_spin_lock_irqsave",         LOCK,   "irqsave", 1, ret_any},
-	{"_spin_unlock_irqrestore",    UNLOCK, "irqsave", 1, ret_any},
-	{"__spin_lock_irqsave_nested", LOCK,   "irqsave", 1, ret_any},
-	{"__spin_lock_irqsave",        LOCK,   "irqsave", 1, ret_any},
-	{"__spin_unlock_irqrestore",   UNLOCK, "irqsave", 1, ret_any},
-	{"_raw_spin_lock_irqsave",     LOCK,   "irqsave", RETURN_VAL, ret_any},
-	{"_raw_spin_lock_irqsave",     LOCK,   "irqsave", 1, ret_any},
-	{"_raw_spin_unlock_irqrestore",UNLOCK, "irqsave", 1, ret_any},
-	{"__raw_spin_lock_irqsave",    LOCK,   "irqsave", RETURN_VAL, ret_any},
-	{"__raw_spin_unlock_irqrestore",UNLOCK, "irqsave", 1, ret_any},
-	{"_raw_spin_lock_irqsave_nested", LOCK, "irqsave", RETURN_VAL, ret_any},
-	{"spin_trylock_irqsave",       LOCK,   "irqsave", 1, ret_one},
-	{"read_lock_irqsave",          LOCK,   "irqsave", RETURN_VAL, ret_any},
-	{"read_lock_irqsave",          LOCK,   "irqsave", 1, ret_any},
-	{"read_unlock_irqrestore",     UNLOCK, "irqsave", 1, ret_any},
-	{"_read_lock_irqsave",         LOCK,   "irqsave", RETURN_VAL, ret_any},
-	{"_read_lock_irqsave",         LOCK,   "irqsave", 1, ret_any},
-	{"_read_unlock_irqrestore",    UNLOCK, "irqsave", 1, ret_any},
-	{"__read_lock_irqsave",        LOCK,   "irqsave", RETURN_VAL, ret_any},
-	{"__read_unlock_irqrestore",   UNLOCK, "irqsave", 1, ret_any},
-	{"write_lock_irqsave",         LOCK,   "irqsave", RETURN_VAL, ret_any},
-	{"write_lock_irqsave",         LOCK,   "irqsave", 1, ret_any},
-	{"write_unlock_irqrestore",    UNLOCK, "irqsave", 1, ret_any},
-	{"_write_lock_irqsave",        LOCK,   "irqsave", RETURN_VAL, ret_any},
-	{"_write_lock_irqsave",        LOCK,   "irqsave", 1, ret_any},
-	{"_write_unlock_irqrestore",   UNLOCK, "irqsave", 1, ret_any},
-	{"__write_lock_irqsave",       LOCK,   "irqsave", RETURN_VAL, ret_any},
-	{"__write_unlock_irqrestore",  UNLOCK, "irqsave", 1, ret_any},
+	{"local_bh_disable",	LOCK,	bottom_half, NO_ARG, ret_any},
+	{"_local_bh_disable",	LOCK,	bottom_half, NO_ARG, ret_any},
+	{"__local_bh_disable",	LOCK,	bottom_half, NO_ARG, ret_any},
+	{"local_bh_enable",	UNLOCK,	bottom_half, NO_ARG, ret_any},
+	{"_local_bh_enable",	UNLOCK,	bottom_half, NO_ARG, ret_any},
+	{"__local_bh_enable",	UNLOCK,	bottom_half, NO_ARG, ret_any},
+	{"spin_lock_bh",        LOCK,   bottom_half, NO_ARG, ret_any},
+	{"spin_unlock_bh",      UNLOCK, bottom_half, NO_ARG, ret_any},
+	{"_spin_lock_bh",       LOCK,   bottom_half, NO_ARG, ret_any},
+	{"_spin_unlock_bh",     UNLOCK, bottom_half, NO_ARG, ret_any},
+	{"__spin_lock_bh",      LOCK,   bottom_half, NO_ARG, ret_any},
+	{"__spin_unlock_bh",    UNLOCK, bottom_half, NO_ARG, ret_any},
+	{"read_lock_bh",        LOCK,   bottom_half, NO_ARG, ret_any},
+	{"read_unlock_bh",      UNLOCK, bottom_half, NO_ARG, ret_any},
+	{"_read_lock_bh",       LOCK,   bottom_half, NO_ARG, ret_any},
+	{"_read_unlock_bh",     UNLOCK, bottom_half, NO_ARG, ret_any},
+	{"__read_lock_bh",      LOCK,   bottom_half, NO_ARG, ret_any},
+	{"__read_unlock_bh",    UNLOCK, bottom_half, NO_ARG, ret_any},
+	{"_raw_read_lock_bh",   LOCK,   bottom_half, NO_ARG, ret_any},
+	{"_raw_read_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
+	{"write_lock_bh",       LOCK,   bottom_half, NO_ARG, ret_any},
+	{"write_unlock_bh",     UNLOCK, bottom_half, NO_ARG, ret_any},
+	{"_write_lock_bh",      LOCK,   bottom_half, NO_ARG, ret_any},
+	{"_write_unlock_bh",    UNLOCK, bottom_half, NO_ARG, ret_any},
+	{"__write_lock_bh",     LOCK,   bottom_half, NO_ARG, ret_any},
+	{"__write_unlock_bh",   UNLOCK, bottom_half, NO_ARG, ret_any},
+	{"_raw_write_lock_bh",  LOCK,   bottom_half, NO_ARG, ret_any},
+	{"_raw_write_unlock_bh",UNLOCK, bottom_half, NO_ARG, ret_any},
+	{"spin_trylock_bh",     LOCK,   bottom_half, NO_ARG, ret_one},
+	{"_spin_trylock_bh",    LOCK,   bottom_half, NO_ARG, ret_one},
+	{"__spin_trylock_bh",   LOCK,   bottom_half, NO_ARG, ret_one},
 
-	{"local_bh_disable",	LOCK,	"bottom_half", NO_ARG, ret_any},
-	{"_local_bh_disable",	LOCK,	"bottom_half", NO_ARG, ret_any},
-	{"__local_bh_disable",	LOCK,	"bottom_half", NO_ARG, ret_any},
-	{"local_bh_enable",	UNLOCK,	"bottom_half", NO_ARG, ret_any},
-	{"_local_bh_enable",	UNLOCK,	"bottom_half", NO_ARG, ret_any},
-	{"__local_bh_enable",	UNLOCK,	"bottom_half", NO_ARG, ret_any},
-	{"spin_lock_bh",        LOCK,   "bottom_half", NO_ARG, ret_any},
-	{"spin_unlock_bh",      UNLOCK, "bottom_half", NO_ARG, ret_any},
-	{"_spin_lock_bh",       LOCK,   "bottom_half", NO_ARG, ret_any},
-	{"_spin_unlock_bh",     UNLOCK, "bottom_half", NO_ARG, ret_any},
-	{"__spin_lock_bh",      LOCK,   "bottom_half", NO_ARG, ret_any},
-	{"__spin_unlock_bh",    UNLOCK, "bottom_half", NO_ARG, ret_any},
-	{"read_lock_bh",        LOCK,   "bottom_half", NO_ARG, ret_any},
-	{"read_unlock_bh",      UNLOCK, "bottom_half", NO_ARG, ret_any},
-	{"_read_lock_bh",       LOCK,   "bottom_half", NO_ARG, ret_any},
-	{"_read_unlock_bh",     UNLOCK, "bottom_half", NO_ARG, ret_any},
-	{"__read_lock_bh",      LOCK,   "bottom_half", NO_ARG, ret_any},
-	{"__read_unlock_bh",    UNLOCK, "bottom_half", NO_ARG, ret_any},
-	{"_raw_read_lock_bh",   LOCK,   "bottom_half", NO_ARG, ret_any},
-	{"_raw_read_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any},
-	{"write_lock_bh",       LOCK,   "bottom_half", NO_ARG, ret_any},
-	{"write_unlock_bh",     UNLOCK, "bottom_half", NO_ARG, ret_any},
-	{"_write_lock_bh",      LOCK,   "bottom_half", NO_ARG, ret_any},
-	{"_write_unlock_bh",    UNLOCK, "bottom_half", NO_ARG, ret_any},
-	{"__write_lock_bh",     LOCK,   "bottom_half", NO_ARG, ret_any},
-	{"__write_unlock_bh",   UNLOCK, "bottom_half", NO_ARG, ret_any},
-	{"spin_trylock_bh",     LOCK,   "bottom_half", NO_ARG, ret_one},
-	{"_spin_trylock_bh",    LOCK,   "bottom_half", NO_ARG, ret_one},
-	{"__spin_trylock_bh",   LOCK,   "bottom_half", NO_ARG, ret_one},
+	{"ffs_mutex_lock",      LOCK,   mutex, 0, ret_zero},
 
-	{"ffs_mutex_lock",        LOCK,   "mutex", 0, ret_zero},
+	{"clk_prepare_lock",    LOCK,   prepare_lock, NO_ARG, ret_any},
+	{"clk_prepare_unlock",  UNLOCK, prepare_lock, NO_ARG, ret_any},
+	{"clk_enable_lock",     LOCK,   enable_lock, -1, ret_any},
+	{"clk_enable_unlock",   UNLOCK, enable_lock,  0, ret_any},
+
+	{"dma_resv_lock",	        LOCK,   mutex, 0, ret_zero},
+	{"dma_resv_trylock",	        LOCK,	mutex, 0, ret_one},
+	{"dma_resv_lock_interruptible", LOCK,	mutex, 0, ret_zero},
+	{"dma_resv_unlock",		UNLOCK, mutex, 0, ret_any},
+
+	{"modeset_lock",			  LOCK,   mutex, 0, ret_zero},
+	{"drm_ modeset_lock",			  LOCK,   mutex, 0, ret_zero},
+	{"drm_modeset_lock_single_interruptible", LOCK,   mutex, 0, ret_zero},
+	{"modeset_unlock",			  UNLOCK, mutex, 0, ret_any},
+
+	{"reiserfs_write_lock_nested",	 LOCK,   mutex, 0, ret_any},
+	{"reiserfs_write_unlock_nested", UNLOCK, mutex, 0, ret_any},
+
+	{"rw_lock",                LOCK,   write_lock, 1, ret_any},
+	{"rw_unlock",              UNLOCK, write_lock, 1, ret_any},
+
+	{"sem_lock",               LOCK,   mutex, 0, ret_any},
+	{"sem_unlock",             UNLOCK, mutex, 0, ret_any},
+
+	{},
 };
 
-static struct lock_info *lock_table;
+struct macro_info {
+	const char *macro;
+	enum action action;
+	int param;
+};
 
-static struct tracker_list *starts_locked;
-static struct tracker_list *starts_unlocked;
+static struct macro_info macro_table[] = {
+	{"genpd_lock",               LOCK,   0},
+	{"genpd_lock_nested",        LOCK,   0},
+	{"genpd_lock_interruptible", LOCK,   0},
+	{"genpd_unlock",             UNLOCK, 0},
+};
 
-struct locks_on_return {
-	int line;
-	struct tracker_list *locked;
-	struct tracker_list *unlocked;
-	struct tracker_list *impossible;
-	struct range_list *return_values;
+static const char *false_positives[][2] = {
+	{"fs/jffs2/", "->alloc_sem"},
+	{"fs/xfs/", "->b_sema"},
+	{"mm/", "pvmw->ptl"},
 };
-DECLARE_PTR_LIST(return_list, struct locks_on_return);
-static struct return_list *all_returns;
 
-static char *make_full_name(const char *lock, const char *var)
-{
-	static char tmp_buf[512];
+static struct stree *start_states;
+static struct stree_stack *saved_stack;
 
-	snprintf(tmp_buf, sizeof(tmp_buf), "%s:%s", lock, var);
-	remove_parens(tmp_buf);
-	return alloc_string(tmp_buf);
+static struct tracker_list *locks;
+
+static void reset(struct sm_state *sm, struct expression *mod_expr)
+{
+	set_state(my_id, sm->name, sm->sym, &start_state);
 }
 
 static struct expression *remove_spinlock_check(struct expression *expr)
@@ -406,139 +450,320 @@
 	return expr;
 }
 
-static char *get_full_name(struct expression *expr, int index)
+static struct expression *filter_kernel_args(struct expression *arg)
 {
-	struct expression *arg;
-	char *name = NULL;
-	char *full_name = NULL;
+	if (arg->type == EXPR_PREOP && arg->op == '&')
+		return strip_expr(arg->unop);
+	if (!is_pointer(arg))
+		return arg;
+	return deref_expression(strip_expr(arg));
+}
+
+static char *lock_to_name_sym(struct expression *expr, struct symbol **sym)
+{
+	expr = remove_spinlock_check(expr);
+	expr = filter_kernel_args(expr);
+	return expr_to_str_sym(expr, sym);
+}
+
+static char *get_full_name(struct expression *expr, int index, struct symbol **sym)
+{
 	struct lock_info *lock = &lock_table[index];
+	struct expression *arg;
 
+	*sym = NULL;
 	if (lock->arg == RETURN_VAL) {
-		name = expr_to_var(expr->left);
-		full_name = make_full_name(lock->name, name);
+		return expr_to_var_sym(strip_expr(expr->left), sym);
 	} else if (lock->arg == NO_ARG) {
-		full_name = make_full_name(lock->name, "");
+		return alloc_string(get_lock_name(lock->type));
 	} else {
 		arg = get_argument_from_call_expr(expr->args, lock->arg);
 		if (!arg)
-			goto free;
-		arg = remove_spinlock_check(arg);
-		name = expr_to_str(arg);
-		if (!name)
-			goto free;
-		full_name = make_full_name(lock->name, name);
+			return NULL;
+		return lock_to_name_sym(arg, sym);
 	}
-free:
-	free_string(name);
-	return full_name;
 }
 
-static struct smatch_state *get_start_state(struct sm_state *sm)
+static struct smatch_state *unmatched_state(struct sm_state *sm)
 {
-	int is_locked = 0;
-	int is_unlocked = 0;
+	return &start_state;
+}
 
-	if (in_tracker_list(starts_locked, my_id, sm->name, sm->sym))
-		is_locked = 1;
-	if (in_tracker_list(starts_unlocked, my_id, sm->name, sm->sym))
-		is_unlocked = 1;
-	if (is_locked && is_unlocked)
-		return &undefined;
-	if (is_locked)
+static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
+{
+	if (is_impossible_path())
+		set_state(my_id, cur->name, cur->sym, &impossible);
+}
+
+static struct smatch_state *merge_func(struct smatch_state *s1, struct smatch_state *s2)
+{
+	if (s1 == &impossible)
+		return s2;
+	if (s2 == &impossible)
+		return s1;
+	return &merged;
+}
+
+static struct smatch_state *action_to_state(enum action lock_unlock)
+{
+	switch (lock_unlock) {
+	case LOCK:
 		return &locked;
-	if (is_unlocked)
+	case UNLOCK:
 		return &unlocked;
-	return &undefined;
+	case RESTORE:
+		return &restore;
+	}
+	return NULL;
 }
 
-static struct smatch_state *unmatched_state(struct sm_state *sm)
+static struct sm_state *get_best_match(const char *key, enum action lock_unlock)
 {
-	return &start_state;
+	struct sm_state *sm;
+	struct sm_state *match;
+	int cnt = 0;
+	int start_pos, state_len, key_len, chunks, i;
+
+	if (strncmp(key, "$->", 3) == 0)
+		key += 3;
+
+	key_len = strlen(key);
+	chunks = 0;
+	for (i = key_len - 1; i > 0; i--) {
+		if (key[i] == '>' || key[i] == '.')
+			chunks++;
+		if (chunks == 2) {
+			key += (i + 1);
+			key_len = strlen(key);
+			break;
+		}
+	}
+
+	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
+		if (((lock_unlock == UNLOCK || lock_unlock == RESTORE) &&
+		     sm->state != &locked) ||
+		    (lock_unlock == LOCK && sm->state != &unlocked))
+			continue;
+		state_len = strlen(sm->name);
+		if (state_len < key_len)
+			continue;
+		start_pos = state_len - key_len;
+		if ((start_pos == 0 || !isalnum(sm->name[start_pos - 1])) &&
+		    strcmp(sm->name + start_pos, key) == 0) {
+			cnt++;
+			match = sm;
+		}
+	} END_FOR_EACH_SM(sm);
+
+	if (cnt == 1)
+		return match;
+	return NULL;
 }
 
-static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
+static void use_best_match(char *key, enum action lock_unlock)
 {
-	if (is_impossible_path())
-		set_state(my_id, cur->name, cur->sym, &impossible);
+	struct sm_state *match;
+
+	match = get_best_match(key, lock_unlock);
+	if (match)
+		set_state(my_id, match->name, match->sym, action_to_state(lock_unlock));
+	else
+		set_state(my_id, key, NULL, action_to_state(lock_unlock));
 }
 
-static bool nestable(const char *name)
+static void set_start_state(const char *name, struct symbol *sym, struct smatch_state *start)
 {
-	if (strstr(name, "read_sem:"))
-		return true;
-	if (strcmp(name, "bottom_half:") == 0)
-		return true;
+	struct smatch_state *orig;
+
+	orig = get_state_stree(start_states, my_id, name, sym);
+	if (!orig)
+		set_state_stree(&start_states, my_id, name, sym, start);
+	else if (orig != start)
+		set_state_stree(&start_states, my_id, name, sym, &undefined);
+}
+
+static bool common_false_positive(const char *name)
+{
+	const char *path, *lname;
+	int i, len_total, len_path, len_name, skip;
+
+	if (!get_filename())
+		return false;
+
+	len_total = strlen(name);
+	for (i = 0; i < ARRAY_SIZE(false_positives); i++) {
+		path = false_positives[i][0];
+		lname = false_positives[i][1];
+
+		len_path = strlen(path);
+		len_name = strlen(lname);
+
+		if (len_name > len_total)
+			continue;
+		skip = len_total - len_name;
+
+		if (strncmp(get_filename(), path, len_path) == 0 &&
+		    strcmp(name + skip, lname) == 0)
+			return true;
+	}
+
 	return false;
 }
 
-static void do_lock(const char *name)
+static void warn_on_double(struct sm_state *sm, struct smatch_state *state)
 {
-	struct sm_state *sm;
+	struct sm_state *tmp;
 
-	if (__inline_fn)
+	if (!sm)
 		return;
 
-	sm = get_sm_state(my_id, name, NULL);
-	if (!sm)
-		add_tracker(&starts_unlocked, my_id, name, NULL);
-	if (sm && slist_has_state(sm->possible, &locked) && !nestable(name))
-		sm_error("double lock '%s'", name);
-	if (sm)
-		func_has_transition = TRUE;
-	set_state(my_id, name, NULL, &locked);
+	FOR_EACH_PTR(sm->possible, tmp) {
+		if (tmp->state == state)
+			goto found;
+	} END_FOR_EACH_PTR(tmp);
+
+	return;
+found:
+	if (strcmp(sm->name, "bottom_half") == 0)
+		return;
+	if (common_false_positive(sm->name))
+		return;
+	sm_msg("error: double %s '%s' (orig line %u)",
+	       state->name, sm->name, tmp->line);
 }
 
-static void do_lock_failed(const char *name)
+static bool handle_macro_lock_unlock(void)
 {
+	struct expression *expr, *arg;
+	struct macro_info *info;
 	struct sm_state *sm;
+	struct symbol *sym;
+	const char *macro;
+	char *name;
+	bool ret = false;
+	int i;
 
-	if (__inline_fn)
+	expr = last_ptr_list((struct ptr_list *)big_expression_stack);
+	while (expr && expr->type == EXPR_ASSIGNMENT)
+		expr = strip_expr(expr->right);
+	if (!expr || expr->type != EXPR_CALL)
+		return false;
+
+	macro = get_macro_name(expr->pos);
+	if (!macro)
+		return false;
+
+	for (i = 0; i < ARRAY_SIZE(macro_table); i++) {
+		info = &macro_table[i];
+
+		if (strcmp(macro, info->macro) != 0)
+			continue;
+		arg = get_argument_from_call_expr(expr->args, info->param);
+		name = expr_to_str_sym(arg, &sym);
+		if (!name || !sym)
+			goto free;
+		sm = get_sm_state(my_id, name, sym);
+
+		if (info->action == LOCK) {
+			if (!sm)
+				set_start_state(name, sym, &unlocked);
+			if (sm && sm->line != expr->pos.line)
+				warn_on_double(sm, &locked);
+			set_state(my_id, name, sym, &locked);
+		} else {
+			if (!sm)
+				set_start_state(name, sym, &locked);
+			if (sm && sm->line != expr->pos.line)
+				warn_on_double(sm, &unlocked);
+			set_state(my_id, name, sym, &unlocked);
+		}
+		ret = true;
+free:
+		free_string(name);
+		return ret;
+	}
+	return false;
+}
+
+static void do_lock(const char *name, struct symbol *sym, struct lock_info *info)
+{
+	struct sm_state *sm;
+
+	if (handle_macro_lock_unlock())
 		return;
 
-	sm = get_sm_state(my_id, name, NULL);
+	add_tracker(&locks, my_id, name, sym);
+
+	sm = get_sm_state(my_id, name, sym);
 	if (!sm)
-		add_tracker(&starts_unlocked, my_id, name, NULL);
-	set_state(my_id, name, NULL, &unlocked);
+		set_start_state(name, sym, &unlocked);
+	warn_on_double(sm, &locked);
+	set_state(my_id, name, sym, &locked);
 }
 
-static void do_unlock(const char *name)
+static void do_lock_failed(const char *name, struct symbol *sym)
 {
+	add_tracker(&locks, my_id, name, sym);
+	set_state(my_id, name, sym, &unlocked);
+}
+
+static void do_unlock(const char *name, struct symbol *sym, struct lock_info *info)
+{
 	struct sm_state *sm;
 
-	if (__inline_fn)
-		return;
 	if (__path_is_null())
 		return;
-	sm = get_sm_state(my_id, name, NULL);
+
+	if (handle_macro_lock_unlock())
+		return;
+
+	add_tracker(&locks, my_id, name, sym);
+	sm = get_sm_state(my_id, name, sym);
+	if (!sm) {
+		sm = get_best_match(name, UNLOCK);
+		if (sm) {
+			name = sm->name;
+			sym = sm->sym;
+		}
+	}
 	if (!sm)
-		add_tracker(&starts_locked, my_id, name, NULL);
-	if (sm && slist_has_state(sm->possible, &unlocked) &&
-			strcmp(name, "bottom_half:") != 0)
-		sm_error("double unlock '%s'", name);
-	if (sm)
-		func_has_transition = TRUE;
-	set_state(my_id, name, NULL, &unlocked);
+		set_start_state(name, sym, &locked);
+	warn_on_double(sm, &unlocked);
+	set_state(my_id, name, sym, &unlocked);
 }
 
+static void do_restore(const char *name, struct symbol *sym, struct lock_info *info)
+{
+	if (__path_is_null())
+		return;
+
+	if (!get_state(my_id, name, sym))
+		set_start_state(name, sym, &locked);
+
+	add_tracker(&locks, my_id, name, sym);
+	set_state(my_id, name, sym, &restore);
+}
+
 static void match_lock_held(const char *fn, struct expression *call_expr,
-			struct expression *assign_expr, void *_index)
+			    struct expression *assign_expr, void *_index)
 {
 	int index = PTR_INT(_index);
-	char *lock_name;
 	struct lock_info *lock = &lock_table[index];
+	char *lock_name;
+	struct symbol *sym;
 
 	if (lock->arg == NO_ARG) {
-		lock_name = get_full_name(NULL, index);
+		lock_name = get_full_name(NULL, index, &sym);
 	} else if (lock->arg == RETURN_VAL) {
 		if (!assign_expr)
 			return;
-		lock_name = get_full_name(assign_expr, index);
+		lock_name = get_full_name(assign_expr, index, &sym);
 	} else {
-		lock_name = get_full_name(call_expr, index);
+		lock_name = get_full_name(call_expr, index, &sym);
 	}
 	if (!lock_name)
 		return;
-	do_lock(lock_name);
+	do_lock(lock_name, sym, lock);
 	free_string(lock_name);
 }
 
@@ -546,21 +771,22 @@
 			struct expression *assign_expr, void *_index)
 {
 	int index = PTR_INT(_index);
-	char *lock_name;
 	struct lock_info *lock = &lock_table[index];
+	char *lock_name;
+	struct symbol *sym;
 
 	if (lock->arg == NO_ARG) {
-		lock_name = get_full_name(NULL, index);
+		lock_name = get_full_name(NULL, index, &sym);
 	} else if (lock->arg == RETURN_VAL) {
 		if (!assign_expr)
 			return;
-		lock_name = get_full_name(assign_expr, index);
+		lock_name = get_full_name(assign_expr, index, &sym);
 	} else {
-		lock_name = get_full_name(call_expr, index);
+		lock_name = get_full_name(call_expr, index, &sym);
 	}
 	if (!lock_name)
 		return;
-	do_lock_failed(lock_name);
+	do_lock_failed(lock_name, sym);
 	free_string(lock_name);
 }
 
@@ -567,398 +793,452 @@
 static void match_returns_locked(const char *fn, struct expression *expr,
 				      void *_index)
 {
-	char *full_name = NULL;
 	int index = PTR_INT(_index);
 	struct lock_info *lock = &lock_table[index];
+	char *full_name;
+	struct symbol *sym;
 
 	if (lock->arg != RETURN_VAL)
 		return;
-	full_name = get_full_name(expr, index);
-	do_lock(full_name);
+	full_name = get_full_name(expr, index, &sym);
+	if (!full_name)
+		return;
+	do_lock(full_name, sym, lock);
 }
 
 static void match_lock_unlock(const char *fn, struct expression *expr, void *_index)
 {
-	char *full_name = NULL;
 	int index = PTR_INT(_index);
 	struct lock_info *lock = &lock_table[index];
+	char *full_name;
+	struct symbol *sym;
 
-	if (__inline_fn)
-		return;
-
-	full_name = get_full_name(expr, index);
+	full_name = get_full_name(expr, index, &sym);
 	if (!full_name)
 		return;
-	if (lock->action == LOCK)
-		do_lock(full_name);
-	else
-		do_unlock(full_name);
+	switch (lock->action) {
+	case LOCK:
+		do_lock(full_name, sym, lock);
+		break;
+	case UNLOCK:
+		do_unlock(full_name, sym, lock);
+		break;
+	case RESTORE:
+		do_restore(full_name, sym, lock);
+		break;
+	}
 	free_string(full_name);
 }
 
-static struct locks_on_return *alloc_return(struct expression *expr)
+static struct smatch_state *get_start_state(struct sm_state *sm)
 {
-	struct locks_on_return *ret;
+	struct smatch_state *orig;
 
-	ret = malloc(sizeof(*ret));
-	if (!get_implied_rl(expr, &ret->return_values))
-		ret->return_values = NULL;
-	ret->line = get_lineno();
-	ret->locked = NULL;
-	ret->unlocked = NULL;
-	ret->impossible = NULL;
-	return ret;
+	orig = get_state_stree(start_states, my_id, sm->name, sm->sym);
+	if (orig)
+		return orig;
+	return &undefined;
 }
 
-static int check_possible(struct sm_state *sm)
+static int get_param_lock_name(struct sm_state *sm, struct expression *expr,
+			       const char **name)
 {
-	struct sm_state *tmp;
-	int islocked = 0;
-	int isunlocked = 0;
-	int undef = 0;
+	char *other_name;
+	struct symbol *other_sym;
+	const char *param_name;
+	int param;
 
-	if (!option_spammy)
-		return 0;
+	*name = sm->name;
 
-	FOR_EACH_PTR(sm->possible, tmp) {
-		if (tmp->state == &locked)
-			islocked = 1;
-		if (tmp->state == &unlocked)
-			isunlocked = 1;
-		if (tmp->state == &start_state) {
-			struct smatch_state *s;
+	param = get_param_num_from_sym(sm->sym);
+	if (param >= 0) {
+		param_name = get_param_name(sm);
+		if (param_name)
+			*name = param_name;
+		return param;
+	}
 
-			s = get_start_state(tmp);
-			if (s == &locked)
-				islocked = 1;
-			else if (s == &unlocked)
-				isunlocked = 1;
-			else
-				undef = 1;
+	if (expr) {
+		struct symbol *ret_sym;
+		char *ret_str;
+
+		ret_str = expr_to_str_sym(expr, &ret_sym);
+		if (ret_str && ret_sym == sm->sym) {
+			param_name = state_name_to_param_name(sm->name, ret_str);
+			if (param_name) {
+				free_string(ret_str);
+				*name = param_name;
+				return -1;
+			}
 		}
-		if (tmp->state == &undefined)
-			undef = 1;  // i don't think this is possible any more.
-	} END_FOR_EACH_PTR(tmp);
-	if ((islocked && isunlocked) || undef) {
-		sm_warning("'%s' is sometimes locked here and sometimes unlocked.", sm->name);
-		return 1;
+		free_string(ret_str);
 	}
-	return 0;
+
+	other_name = get_other_name_sym(sm->name, sm->sym, &other_sym);
+	if (!other_name)
+		return -2;
+	param = get_param_num_from_sym(other_sym);
+	if (param < 0)
+		return -2;
+
+	param_name = get_param_name_var_sym(other_name, other_sym);
+	free_string(other_name);
+	if (param_name)
+		*name = param_name;
+	return param;
 }
 
-static struct position warned_pos;
+static int get_db_type(struct sm_state *sm)
+{
+	if (sm->state == get_start_state(sm)) {
+		if (sm->state == &locked)
+			return KNOWN_LOCKED;
+		if (sm->state == &unlocked)
+			return KNOWN_UNLOCKED;
+	}
 
-static void match_return(int return_id, char *return_ranges, struct expression *expr)
+	if (sm->state == &locked)
+		return LOCKED;
+	if (sm->state == &unlocked)
+		return UNLOCKED;
+	if (sm->state == &restore)
+		return LOCK_RESTORED;
+	return LOCKED;
+}
+
+static void match_return_info(int return_id, char *return_ranges, struct expression *expr)
 {
-	struct locks_on_return *ret;
-	struct stree *stree;
-	struct sm_state *tmp;
+	struct sm_state *sm;
+	const char *param_name;
+	int param;
 
-	if (!final_pass)
-		return;
-	if (__inline_fn)
-		return;
+	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
+		if (sm->state != &locked &&
+		    sm->state != &unlocked &&
+		    sm->state != &restore)
+			continue;
 
-	if (expr && cmp_pos(expr->pos, warned_pos) == 0)
-		return;
+		param = get_param_lock_name(sm, expr, &param_name);
+		sql_insert_return_states(return_id, return_ranges,
+					 get_db_type(sm),
+					 param, param_name, "");
+	} END_FOR_EACH_SM(sm);
+}
 
-	ret = alloc_return(expr);
+enum {
+	ERR_PTR, VALID_PTR, NEGATIVE, ZERO, POSITIVE, NUM_BUCKETS,
+};
 
-	stree = __get_cur_stree();
-	FOR_EACH_MY_SM(my_id, stree, tmp) {
-		if (tmp->state == &locked) {
-			add_tracker(&ret->locked, tmp->owner, tmp->name,
-				tmp->sym);
-		} else if (tmp->state == &unlocked) {
-			add_tracker(&ret->unlocked, tmp->owner, tmp->name,
-				tmp->sym);
-		} else if (tmp->state == &start_state) {
-			struct smatch_state *s;
+static bool is_EINTR(struct range_list *rl)
+{
+	sval_t sval;
 
-			s = get_start_state(tmp);
-			if (s == &locked)
-				add_tracker(&ret->locked, tmp->owner, tmp->name, 
-					    tmp->sym);
-			if (s == &unlocked)
-				add_tracker(&ret->unlocked, tmp->owner,tmp->name,
-					     tmp->sym);
-		} else if (tmp->state == &impossible) {
-			add_tracker(&ret->impossible, tmp->owner, tmp->name,
-				    tmp->sym);
-		} else {
-			if (check_possible(tmp)) {
-				if (expr)
-					warned_pos = expr->pos;
-			}
-		}
-	} END_FOR_EACH_SM(tmp);
-	add_ptr_list(&all_returns, ret);
+	if (!rl_to_sval(rl, &sval))
+		return false;
+	return sval.value == -4;
 }
 
-static void add_line(struct range_list **rl, int line)
+static int success_fail_positive(struct range_list *rl)
 {
-	sval_t sval = sval_type_val(&int_ctype, line);
+	/* void returns are the same as success (zero in the kernel) */
+	if (!rl)
+		return ZERO;
 
-	add_range(rl, sval, sval);
+	if (rl_type(rl)->type != SYM_PTR && sval_is_negative(rl_min(rl)))
+		return NEGATIVE;
+
+	if (rl_min(rl).value == 0 && rl_max(rl).value == 0)
+		return ZERO;
+
+	if (is_err_ptr(rl_min(rl)) &&
+	    is_err_ptr(rl_max(rl)))
+		return ERR_PTR;
+
+	/*
+	 * Trying to match ERR_PTR(ret) but without the expression struct.
+	 * Ugly...
+	 */
+	if (type_bits(&long_ctype) == 64 &&
+	    rl_type(rl)->type == SYM_PTR &&
+	    rl_min(rl).value == INT_MIN)
+		return ERR_PTR;
+
+	return POSITIVE;
 }
 
-static int line_printed(struct range_list *rl, int line)
+static bool sym_in_lock_table(struct symbol *sym)
 {
-	sval_t sval = sval_type_val(&int_ctype, line);
+	int i;
 
-	return rl_has_sval(rl, sval);
+	if (!sym || !sym->ident)
+		return false;
+
+	for (i = 0; lock_table[i].function != NULL; i++) {
+		if (strcmp(lock_table[i].function, sym->ident->name) == 0)
+			return true;
+	}
+	return false;
 }
 
-static void print_inconsistent_returns(struct tracker *lock,
-				struct smatch_state *start)
+static bool func_in_lock_table(struct expression *expr)
 {
-	struct locks_on_return *tmp;
-	struct range_list *printed = NULL;
+	if (expr->type != EXPR_SYMBOL)
+		return false;
+	return sym_in_lock_table(expr->symbol);
+}
+
+static void check_lock(char *name, struct symbol *sym)
+{
+	struct range_list *locked_lines = NULL;
+	struct range_list *unlocked_lines = NULL;
+	int locked_buckets[NUM_BUCKETS] = {};
+	int unlocked_buckets[NUM_BUCKETS] = {};
+	struct stree *stree, *orig;
+	struct sm_state *return_sm;
+	struct sm_state *sm;
+	sval_t line = sval_type_val(&int_ctype, 0);
+	int bucket;
 	int i;
 
-	sm_warning("inconsistent returns '%s'.", lock->name);
-	sm_printf("  Locked on:   ");
+	if (sym_in_lock_table(cur_func_sym))
+		return;
 
-	i = 0;
-	FOR_EACH_PTR(all_returns, tmp) {
-		if (line_printed(printed, tmp->line))
-			continue;
-		if (in_tracker_list(tmp->unlocked, lock->owner, lock->name, lock->sym))
-			continue;
-		if (in_tracker_list(tmp->locked, lock->owner, lock->name, lock->sym)) {
-			if (i++)
-				sm_printf("               ");
-			sm_printf("line %d\n", tmp->line);
-			add_line(&printed, tmp->line);
-			continue;
-		}
-		if (start == &locked) {
-			if (i++)
-				sm_printf("               ");
-			sm_printf("line %d\n", tmp->line);
-			add_line(&printed, tmp->line);
-		}
-	} END_FOR_EACH_PTR(tmp);
+	FOR_EACH_PTR(get_all_return_strees(), stree) {
+		orig = __swap_cur_stree(stree);
 
-	sm_printf("  Unlocked on: ");
-	printed = NULL;
-	i = 0;
-	FOR_EACH_PTR(all_returns, tmp) {
-		if (line_printed(printed, tmp->line))
-			continue;
-		if (in_tracker_list(tmp->unlocked, lock->owner, lock->name, lock->sym)) {
-			if (i++)
-				sm_printf("               ");
-			sm_printf("line %d\n", tmp->line);
-			add_line(&printed, tmp->line);
-			continue;
+		if (is_impossible_path())
+			goto swap_stree;
+
+		return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL);
+		if (!return_sm)
+			goto swap_stree;
+		line.value = return_sm->line;
+
+		sm = get_sm_state(my_id, name, sym);
+		if (!sm)
+			goto swap_stree;
+
+		if (parent_is_gone_var_sym(sm->name, sm->sym))
+			goto swap_stree;
+
+		if (sm->state != &locked && sm->state != &unlocked)
+			goto swap_stree;
+
+		if (sm->state == &unlocked && is_EINTR(estate_rl(return_sm->state)))
+			goto swap_stree;
+
+		bucket = success_fail_positive(estate_rl(return_sm->state));
+		if (sm->state == &locked) {
+			add_range(&locked_lines, line, line);
+			locked_buckets[bucket] = true;
 		}
-		if (in_tracker_list(tmp->locked, lock->owner, lock->name, lock->sym))
-			continue;
-		if (start == &unlocked) {
-			if (i++)
-				sm_printf("               ");
-			sm_printf("line %d\n", tmp->line);
-			add_line(&printed, tmp->line);
+		if (sm->state == &unlocked) {
+			add_range(&unlocked_lines, line, line);
+			unlocked_buckets[bucket] = true;
 		}
-	} END_FOR_EACH_PTR(tmp);
-}
+swap_stree:
+		__swap_cur_stree(orig);
+	} END_FOR_EACH_PTR(stree);
 
-static int matches_return_type(struct range_list *rl, enum return_type type)
-{
-	sval_t zero_sval = ll_to_sval(0);
-	sval_t one_sval = ll_to_sval(1);
 
-	/* All these double negatives are super ugly!  */
+	if (!locked_lines || !unlocked_lines)
+		return;
 
-	switch (type) {
-	case ret_zero:
-		return !possibly_true_rl(rl, SPECIAL_NOTEQUAL, alloc_rl(zero_sval, zero_sval));
-	case ret_one:
-		return !possibly_true_rl(rl, SPECIAL_NOTEQUAL, alloc_rl(one_sval, one_sval));
-	case ret_non_zero:
-		return !possibly_true_rl(rl, SPECIAL_EQUAL, alloc_rl(zero_sval, zero_sval));
-	case ret_negative:
-		return !possibly_true_rl(rl, SPECIAL_GTE, alloc_rl(zero_sval, zero_sval));
-	case ret_positive:
-		return !possibly_true_rl(rl, '<', alloc_rl(zero_sval, zero_sval));
-	case ret_any:
-	default:
-		return 1;
+	for (i = 0; i < NUM_BUCKETS; i++) {
+		if (locked_buckets[i] && unlocked_buckets[i])
+			goto complain;
 	}
-}
+	if (locked_buckets[NEGATIVE] &&
+	    (unlocked_buckets[ZERO] || unlocked_buckets[POSITIVE]))
+		goto complain;
 
-static int match_held(struct tracker *lock, struct locks_on_return *this_return, struct smatch_state *start)
-{
-	if (in_tracker_list(this_return->impossible, lock->owner, lock->name, lock->sym))
-		return 0;
-	if (in_tracker_list(this_return->unlocked, lock->owner, lock->name, lock->sym))
-		return 0;
-	if (in_tracker_list(this_return->locked, lock->owner, lock->name, lock->sym))
-		return 1;
-	if (start == &unlocked)
-		return 0;
-	return 1;
+	if (locked_buckets[ERR_PTR])
+		goto complain;
+
+	return;
+
+complain:
+	sm_msg("warn: inconsistent returns '%s'.", name);
+	sm_printf("  Locked on  : %s\n", show_rl(locked_lines));
+	sm_printf("  Unlocked on: %s\n", show_rl(unlocked_lines));
 }
 
-static int match_released(struct tracker *lock, struct locks_on_return *this_return, struct smatch_state *start)
+static void match_func_end(struct symbol *sym)
 {
-	if (in_tracker_list(this_return->impossible, lock->owner, lock->name, lock->sym))
-		return 0;
-	if (in_tracker_list(this_return->unlocked, lock->owner, lock->name, lock->sym))
-		return 1;
-	if (in_tracker_list(this_return->locked, lock->owner, lock->name, lock->sym))
-		return 0;
-	if (start == &unlocked)
-		return 1;
-	return 0;
+	struct tracker *tracker;
+
+	FOR_EACH_PTR(locks, tracker) {
+		check_lock(tracker->name, tracker->sym);
+	} END_FOR_EACH_PTR(tracker);
 }
 
-static int held_on_return(struct tracker *lock, struct smatch_state *start, enum return_type type)
+static void register_lock(int index)
 {
-	struct locks_on_return *tmp;
+	struct lock_info *lock = &lock_table[index];
+	void *idx = INT_PTR(index);
 
-	FOR_EACH_PTR(all_returns, tmp) {
-		if (!matches_return_type(tmp->return_values, type))
-			continue;
-		if (match_held(lock, tmp, start))
-			return 1;
-	} END_FOR_EACH_PTR(tmp);
-	return 0;
+	if (lock->return_type == ret_one) {
+		return_implies_state(lock->function, 1, 1, &match_lock_held, idx);
+		return_implies_state(lock->function, 0, 0, &match_lock_failed, idx);
+	} else if (lock->return_type == ret_any && lock->arg == RETURN_VAL) {
+		add_function_assign_hook(lock->function, &match_returns_locked, idx);
+	} else if (lock->return_type == ret_any) {
+		add_function_hook(lock->function, &match_lock_unlock, idx);
+	} else if (lock->return_type == ret_zero) {
+		return_implies_state(lock->function, 0, 0, &match_lock_held, idx);
+		return_implies_state(lock->function, -4095, -1, &match_lock_failed, idx);
+	} else if (lock->return_type == ret_valid_ptr) {
+		return_implies_state_sval(lock->function, valid_ptr_min_sval, valid_ptr_max_sval, &match_lock_held, idx);
+	}
 }
 
-static int released_on_return(struct tracker *lock, struct smatch_state *start, enum return_type type)
+static void load_table(struct lock_info *lock_table)
 {
-	struct locks_on_return *tmp;
+	int i;
 
-	FOR_EACH_PTR(all_returns, tmp) {
-		if (!matches_return_type(tmp->return_values, type))
-			continue;
-		if (match_released(lock, tmp, start))
-			return 1;
-	} END_FOR_EACH_PTR(tmp);
-	return 0;
+	for (i = 0; lock_table[i].function != NULL; i++) {
+		if (lock_table[i].action == LOCK)
+			register_lock(i);
+		else
+			add_function_hook(lock_table[i].function, &match_lock_unlock, INT_PTR(i));
+	}
 }
 
-static void check_returns_consistently(struct tracker *lock,
-				struct smatch_state *start)
+static void db_param_locked_unlocked(struct expression *expr, int param, char *key, char *value, enum action lock_unlock)
 {
-	struct symbol *type;
+	struct expression *call, *arg;
+	char *name;
+	struct symbol *sym;
 
-	if (!held_on_return(lock, start, ret_any) ||
-	    !released_on_return(lock, start, ret_any))
+	call = expr;
+	while (call->type == EXPR_ASSIGNMENT)
+		call = strip_expr(call->right);
+	if (call->type != EXPR_CALL)
 		return;
 
-	if (held_on_return(lock, start, ret_zero) &&
-	    !held_on_return(lock, start, ret_non_zero))
+	if (func_in_lock_table(call->fn))
 		return;
 
-	if (held_on_return(lock, start, ret_positive) &&
-	    !held_on_return(lock, start, ret_zero))
+	if (param == -2) {
+		use_best_match(key, lock_unlock);
 		return;
+	}
 
-	if (held_on_return(lock, start, ret_positive) &&
-	    !held_on_return(lock, start, ret_negative))
-		return;
-
-	type = cur_func_return_type();
-	if (type && type->type == SYM_PTR) {
-		if (held_on_return(lock, start, ret_non_zero) &&
-		    !held_on_return(lock, start, ret_zero))
+	if (param == -1) {
+		if (expr->type != EXPR_ASSIGNMENT)
 			return;
+		name = get_variable_from_key(expr->left, key, &sym);
+	} else {
+		arg = get_argument_from_call_expr(call->args, param);
+		if (!arg)
+			return;
+
+		name = get_variable_from_key(arg, key, &sym);
 	}
+	if (!name || !sym)
+		goto free;
 
-	print_inconsistent_returns(lock, start);
+	if (lock_unlock == LOCK)
+		do_lock(name, sym, NULL);
+	else if (lock_unlock == UNLOCK)
+		do_unlock(name, sym, NULL);
+	else if (lock_unlock == RESTORE)
+		do_restore(name, sym, NULL);
+
+free:
+	free_string(name);
 }
 
-static void check_consistency(struct symbol *sym)
+static void db_param_locked(struct expression *expr, int param, char *key, char *value)
 {
-	struct tracker *tmp;
+	db_param_locked_unlocked(expr, param, key, value, LOCK);
+}
 
-	FOR_EACH_PTR(starts_locked, tmp) {
-		if (in_tracker_list(starts_unlocked, tmp->owner, tmp->name,
-					tmp->sym))
-			sm_error("locking inconsistency.  We assume "
-				   "'%s' is both locked and unlocked at the "
-				   "start.",
-				tmp->name);
-	} END_FOR_EACH_PTR(tmp);
+static void db_param_unlocked(struct expression *expr, int param, char *key, char *value)
+{
+	db_param_locked_unlocked(expr, param, key, value, UNLOCK);
+}
 
-	FOR_EACH_PTR(starts_locked, tmp) {
-		check_returns_consistently(tmp, &locked);
-	} END_FOR_EACH_PTR(tmp);
-
-	FOR_EACH_PTR(starts_unlocked, tmp) {
-		check_returns_consistently(tmp, &unlocked);
-	} END_FOR_EACH_PTR(tmp);
+static void db_param_restore(struct expression *expr, int param, char *key, char *value)
+{
+	db_param_locked_unlocked(expr, param, key, value, RESTORE);
 }
 
-static void clear_lists(void)
+static int get_caller_param_lock_name(struct expression *call, struct sm_state *sm, const char **name)
 {
-	struct locks_on_return *tmp;
+	struct expression *arg;
+	char *arg_name;
+	int param;
 
-	func_has_transition = FALSE;
+	param = 0;
+	FOR_EACH_PTR(call->args, arg) {
+		arg_name = sm_to_arg_name(arg, sm);
+		if (arg_name) {
+			*name = arg_name;
+			return param;
+		}
+		param++;
+	} END_FOR_EACH_PTR(arg);
 
-	free_trackers_and_list(&starts_locked);
-	free_trackers_and_list(&starts_unlocked);
-
-	FOR_EACH_PTR(all_returns, tmp) {
-		free_trackers_and_list(&tmp->locked);
-		free_trackers_and_list(&tmp->unlocked);
-		free(tmp);
-	} END_FOR_EACH_PTR(tmp);
-	__free_ptr_list((struct ptr_list **)&all_returns);
+	*name = sm->name;
+	return -2;
 }
 
-static void match_func_end(struct symbol *sym)
+static void match_call_info(struct expression *expr)
 {
-	if (__inline_fn)
-		return;
+	struct sm_state *sm;
+	const char *param_name;
+	int locked_type;
+	int param;
 
-	if (func_has_transition)
-		check_consistency(sym);
+	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
+		param = get_caller_param_lock_name(expr, sm, &param_name);
+		if (sm->state == &locked)
+			locked_type = LOCKED;
+		else if (sm->state == &half_locked ||
+			 slist_has_state(sm->possible, &locked))
+			locked_type = HALF_LOCKED;
+		else
+			continue;
+		sql_insert_caller_info(expr, locked_type, param, param_name, "xxx type");
+
+	} END_FOR_EACH_SM(sm);
 }
 
-static void match_after_func(struct symbol *sym)
+static void match_save_states(struct expression *expr)
 {
-	if (__inline_fn)
-		return;
-	clear_lists();
+	push_stree(&saved_stack, start_states);
+	start_states = NULL;
 }
 
-static void register_lock(int index)
+static void match_restore_states(struct expression *expr)
 {
-	struct lock_info *lock = &lock_table[index];
-	void *idx = INT_PTR(index);
+	start_states = pop_stree(&saved_stack);
+}
 
-	if (lock->return_type == ret_non_zero) {
-		return_implies_state(lock->function, 1, INT_MAX, &match_lock_held, idx);
-		return_implies_state(lock->function, 0, 0, &match_lock_failed, idx);
-	} else if (lock->return_type == ret_any && lock->arg == RETURN_VAL) {
-		add_function_assign_hook(lock->function, &match_returns_locked, idx);
-	} else if (lock->return_type == ret_any) {
-		add_function_hook(lock->function, &match_lock_unlock, idx);
-	} else if (lock->return_type == ret_zero) {
-		return_implies_state(lock->function, 0, 0, &match_lock_held, idx);
-		return_implies_state(lock->function, -4095, -1, &match_lock_failed, idx);
-	} else if (lock->return_type == ret_one) {
-		return_implies_state(lock->function, 1, 1, &match_lock_held, idx);
-		return_implies_state(lock->function, 0, 0, &match_lock_failed, idx);
-	}
+static void match_after_func(struct symbol *sym)
+{
+	free_stree(&start_states);
 }
 
-static void load_table(struct lock_info *_lock_table, int size)
+static void match_dma_resv_lock_NULL(const char *fn, struct expression *call_expr,
+				     struct expression *assign_expr, void *_index)
 {
-	int i;
+	struct expression *lock, *ctx;
+	char *lock_name;
+	struct symbol *sym;
 
-	lock_table = _lock_table;
+	lock = get_argument_from_call_expr(call_expr->args, 0);
+	ctx = get_argument_from_call_expr(call_expr->args, 1);
+	if (!expr_is_zero(ctx))
+		return;
 
-	for (i = 0; i < size; i++) {
-		if (lock_table[i].action == LOCK)
-			register_lock(i);
-		else
-			add_function_hook(lock_table[i].function, &match_lock_unlock, INT_PTR(i));
-	}
+	lock_name = lock_to_name_sym(lock, &sym);
+	if (!lock_name || !sym)
+		goto free;
+	do_lock(lock_name, sym, NULL);
+free:
+	free_string(lock_name);
 }
 
 /* print_held_locks() is used in check_call_tree.c */
@@ -982,17 +1262,29 @@
 {
 	my_id = id;
 
-	if (option_project == PROJ_WINE)
-		load_table(wine_lock_table, ARRAY_SIZE(wine_lock_table));
-	else if (option_project == PROJ_KERNEL)
-		load_table(kernel_lock_table, ARRAY_SIZE(kernel_lock_table));
-	else
+	if (option_project != PROJ_KERNEL)
 		return;
 
+	load_table(lock_table);
+
+	set_dynamic_states(my_id);
 	add_unmatched_state_hook(my_id, &unmatched_state);
 	add_pre_merge_hook(my_id, &pre_merge_hook);
-	add_split_return_callback(match_return);
+	add_merge_hook(my_id, &merge_func);
+	add_modification_hook(my_id, &reset);
+
 	add_hook(&match_func_end, END_FUNC_HOOK);
+
 	add_hook(&match_after_func, AFTER_FUNC_HOOK);
+	add_hook(&match_save_states, INLINE_FN_START);
+	add_hook(&match_restore_states, INLINE_FN_END);
 
+	add_hook(&match_call_info, FUNCTION_CALL_HOOK);
+
+	add_split_return_callback(match_return_info);
+	select_return_states_hook(LOCKED, &db_param_locked);
+	select_return_states_hook(UNLOCKED, &db_param_unlocked);
+	select_return_states_hook(LOCK_RESTORED, &db_param_restore);
+
+	return_implies_state("dma_resv_lock", -4095, -1, &match_dma_resv_lock_NULL, 0);
 }
--- old/usr/src/tools/smatch/src/check_param_mapper.c	Tue Jan  7 08:01:52 2020
+++ new/usr/src/tools/smatch/src/check_param_mapper.c	Tue Jan  7 08:01:52 2020
@@ -23,7 +23,7 @@
  * }
  *
  * Passing "bar" to foo() really means passing "bar" to baz();
- * 
+ *
  * In this case it would print:
  * info: param_mapper 0 => bar 1
  *
@@ -33,54 +33,6 @@
 
 static int my_id;
 
-STATE(argument);
-
-static struct symbol *func_sym;
-
-static void delete(struct sm_state *sm, struct expression *mod_expr)
-{
-	set_state(my_id, sm->name, sm->sym, &undefined);
-}
-
-static void match_function_def(struct symbol *sym)
-{
-	struct symbol *arg;
-
-	func_sym = sym;
-	FOR_EACH_PTR(func_sym->ctype.base_type->arguments, arg) {
-		if (!arg->ident) {
-			continue;
-		}
-		set_state(my_id, arg->ident->name, arg, &argument);
-	} END_FOR_EACH_PTR(arg);
-}
-
-static int get_arg_num(struct expression *expr)
-{
-	struct smatch_state *state;
-	struct symbol *arg;
-	struct symbol *this_arg;
-	int i;
-
-	expr = strip_expr(expr);
-	if (expr->type != EXPR_SYMBOL)
-		return -1;
-	this_arg = expr->symbol;
-
-	state = get_state_expr(my_id, expr);
-	if (!state || state != &argument)
-		return -1;
-	
-	i = 0;
-	FOR_EACH_PTR(func_sym->ctype.base_type->arguments, arg) {
-		if (arg == this_arg)
-			return i;
-		i++;
-	} END_FOR_EACH_PTR(arg);
-
-	return -1;
-}
-
 static void match_call(struct expression *expr)
 {
 	struct expression *tmp;
@@ -93,22 +45,25 @@
 
 	func = expr->fn->symbol_name->name;
 
-	i = 0;
+	i = -1;
 	FOR_EACH_PTR(expr->args, tmp) {
-		tmp = strip_expr(tmp);
-		arg_num = get_arg_num(tmp);
-		if (arg_num >= 0)
-			sm_msg("info: param_mapper %d => %s %d", arg_num, func, i);
 		i++;
+		tmp = strip_expr(tmp);
+		if (tmp->type != EXPR_SYMBOL)
+			continue;
+		if (param_was_set(tmp))
+			continue;
+		arg_num = get_param_num_from_sym(tmp->symbol);
+		if (arg_num < 0)
+			continue;
+		sm_msg("info: param_mapper %d => %s %d", arg_num, func, i);
 	} END_FOR_EACH_PTR(tmp);
 }
 
 void check_param_mapper(int id)
 {
-	if (!option_param_mapper)
+	if (!option_info)
 		return;
 	my_id = id;
-	add_modification_hook(my_id, &delete);
-	add_hook(&match_function_def, FUNC_DEF_HOOK);
 	add_hook(&match_call, FUNCTION_CALL_HOOK);
 }
--- old/usr/src/tools/smatch/src/smatch.c	Tue Jan  7 08:01:52 2020
+++ new/usr/src/tools/smatch/src/smatch.c	Tue Jan  7 08:01:52 2020
@@ -34,7 +34,6 @@
 int option_spammy = 0;
 int option_info = 0;
 int option_full_path = 0;
-int option_param_mapper = 0;
 int option_call_tree = 0;
 int option_no_db = 0;
 int option_enable = 0;
@@ -142,7 +141,6 @@
 	printf("--spammy:  print superfluous crap.\n");
 	printf("--info:  print info used to fill smatch_data/.\n");
 	printf("--debug:  print lots of debug output.\n");
-	printf("--param-mapper:  enable param_mapper output.\n");
 	printf("--no-data:  do not use the /smatch_data/ directory.\n");
 	printf("--data=<dir>: overwrite path to default smatch data directory.\n");
 	printf("--full-path:  print the full pathname.\n");
@@ -235,7 +233,6 @@
 		OPTION(no_data);
 		OPTION(two_passes);
 		OPTION(full_path);
-		OPTION(param_mapper);
 		OPTION(call_tree);
 		OPTION(file_output);
 		OPTION(time);
--- old/usr/src/tools/smatch/src/smatch.h	Tue Jan  7 08:01:53 2020
+++ new/usr/src/tools/smatch/src/smatch.h	Tue Jan  7 08:01:53 2020
@@ -23,6 +23,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <limits.h>
+#include <float.h>
 #include <sys/time.h>
 #include <sqlite3.h>
 #include "lib.h"
@@ -37,6 +38,9 @@
 	union {
 		long long value;
 		unsigned long long uvalue;
+		float fvalue;
+		double dvalue;
+		long double ldvalue;
 	};
 } sval_t;
 
@@ -206,6 +210,7 @@
 extern struct symbol *cur_func_sym;
 extern int option_debug;
 extern int local_debug;
+extern int debug_db;
 bool debug_implied(void);
 extern int option_info;
 extern int option_spammy;
@@ -240,7 +245,10 @@
  * sm_msg(): other message (please avoid using this)
  */
 
-#define sm_printf(msg...) do { if (final_pass || option_debug || local_debug) fprintf(sm_outfd, msg); } while (0)
+#define sm_printf(msg...) do {						\
+	if (final_pass || option_debug || local_debug || debug_db)	\
+		fprintf(sm_outfd, msg);					\
+} while (0)
 
 static inline void sm_prefix(void)
 {
@@ -254,7 +262,7 @@
 #define sm_print_msg(type, msg...) \
 do {                                                           \
 	print_implied_debug_msg();                             \
-	if (!final_pass && !option_debug && !local_debug)      \
+	if (!final_pass && !option_debug && !local_debug && !debug_db)	  \
 		break;                                         \
 	if (__silence_warnings_for_stmt && !option_debug && !local_debug) \
 		break;					       \
@@ -277,12 +285,6 @@
 
 #define sm_msg(msg...) do { sm_print_msg(0, msg); } while (0)
 
-#define local_debug(msg...)					\
-do {								\
-	if (local_debug)					\
-		sm_msg(msg);					\
-} while (0)
-
 extern char *implied_debug_msg;
 static inline void print_implied_debug_msg(void)
 {
@@ -297,6 +299,7 @@
 }
 
 #define sm_debug(msg...) do { if (option_debug) sm_printf(msg); } while (0)
+#define db_debug(msg...) do { if (option_debug || debug_db) sm_printf(msg); } while (0)
 
 #define sm_info(msg...) do {					\
 	if (option_debug || (option_info && final_pass)) {	\
@@ -438,6 +441,7 @@
 struct expression *get_last_expr_from_expression_stmt(struct expression *expr);
 int get_param_num_from_sym(struct symbol *sym);
 int get_param_num(struct expression *expr);
+struct symbol *get_param_sym_from_num(int num);
 int ms_since(struct timeval *start);
 int parent_is_gone_var_sym(const char *name, struct symbol *sym);
 int parent_is_gone(struct expression *expr);
@@ -806,8 +810,6 @@
 	RETURN_VALUE	= 1005,
 	DEREFERENCE	= 1006,
 	RANGE_CAP	= 1007,
-	LOCK_HELD	= 1008,
-	LOCK_RELEASED	= 1009,
 	ABSOLUTE_LIMITS	= 1010,
 	PARAM_ADD	= 1012,
 	PARAM_FREED	= 1013,
@@ -845,6 +847,10 @@
 	NO_OVERFLOW_SIMPLE = 8019,
 	LOCKED		= 8020,
 	UNLOCKED	= 8021,
+	HALF_LOCKED	= 9022,
+	LOCK_RESTORED	= 9023,
+	KNOWN_LOCKED	= 9024,
+	KNOWN_UNLOCKED 	= 9025,
 	SET_FS		= 8022,
 	ATOMIC_INC	= 8023,
 	ATOMIC_DEC	= 8024,
@@ -887,6 +893,7 @@
 const char *get_mtag_name_var_sym(const char *state_name, struct symbol *sym);
 const char *get_mtag_name_expr(struct expression *expr);
 char *get_data_info_name(struct expression *expr);
+char *sm_to_arg_name(struct expression *expr, struct sm_state *sm);
 int is_recursive_member(const char *param_name);
 
 char *escape_newlines(const char *str);
@@ -897,7 +904,7 @@
 	char sql_txt[1024];							\
 										\
 	sqlite3_snprintf(sizeof(sql_txt), sql_txt, sql);			\
-	sm_debug("debug: %s\n", sql_txt);					\
+	db_debug("debug: %s\n", sql_txt);					\
 	sql_exec(db, call_back, data, sql_txt);					\
 } while (0)
 
@@ -931,7 +938,7 @@
 			      ignore ? "or ignore " : "", #table);		\
 		p += snprintf(p, buf + sizeof(buf) - p, values);		\
 		p += snprintf(p, buf + sizeof(buf) - p, ");");			\
-		sm_debug("mem-db: %s\n", buf);					\
+		db_debug("mem-db: %s\n", buf);					\
 		rc = sqlite3_exec(_db, buf, NULL, NULL, &err);			\
 		if (rc != SQLITE_OK) {						\
 			sm_ierror("SQL error #2: %s", err);			\
@@ -1007,7 +1014,6 @@
 extern char *data_dir;
 extern int option_no_data;
 extern int option_full_path;
-extern int option_param_mapper;
 extern int option_call_tree;
 extern int num_checks;
 
@@ -1062,6 +1068,7 @@
 char *map_call_to_param_name_sym(struct expression *expr, struct symbol **sym);
 
 /* smatch_comparison.c */
+extern int comparison_id;
 #define UNKNOWN_COMPARISON 0
 #define IMPOSSIBLE_COMPARISON -1
 struct compare_data {
@@ -1117,8 +1124,11 @@
 sval_t *sval_alloc_permanent(sval_t sval);
 sval_t sval_blank(struct expression *expr);
 sval_t sval_type_val(struct symbol *type, long long val);
+sval_t sval_type_fval(struct symbol *type, long double fval);
 sval_t sval_from_val(struct expression *expr, long long val);
+sval_t sval_from_fval(struct expression *expr, long double fval);
 int sval_is_ptr(sval_t sval);
+bool sval_is_fp(sval_t sval);
 int sval_unsigned(sval_t sval);
 int sval_signed(sval_t sval);
 int sval_bits(sval_t sval);
@@ -1171,6 +1181,10 @@
 struct sm_state *stored_condition_implication_hook(struct expression *expr,
 			struct state_list **true_stack,
 			struct state_list **false_stack);
+/* smatch_parsed_conditions.c */
+struct sm_state *parsed_condition_implication_hook(struct expression *expr,
+			struct state_list **true_stack,
+			struct state_list **false_stack);
 
 /* check_string_len.c */
 int get_formatted_string_size(struct expression *call, int arg);
@@ -1272,6 +1286,14 @@
 		type->type == SYM_FN);
 }
 
+static inline bool type_is_fp(struct symbol *type)
+{
+	return type &&
+	       (type == &float_ctype ||
+		type == &double_ctype ||
+		type == &ldouble_ctype);
+}
+
 static inline int type_bits(struct symbol *type)
 {
 	if (!type)
@@ -1313,10 +1335,53 @@
 /*
  * Returns -1 if one is smaller, 0 if they are the same and 1 if two is larger.
  */
+
+static inline int fp_cmp(sval_t one, sval_t two)
+{
+	struct symbol *type;
+
+	if (sval_is_fp(one) && sval_is_fp(two))
+		type = type_bits(one.type) > type_bits(two.type) ? one.type : two.type;
+	else if (sval_is_fp(one))
+		type = one.type;
+	else
+		type = two.type;
+
+	one = sval_cast(type, one);
+	two = sval_cast(type, two);
+
+	if (one.type == &float_ctype) {
+		if (one.fvalue < two.fvalue)
+			return -1;
+		if (one.fvalue == two.fvalue)
+			return 0;
+		return 1;
+	}
+	if (one.type == &double_ctype) {
+		if (one.dvalue < two.dvalue)
+			return -1;
+		if (one.dvalue == two.dvalue)
+			return 0;
+		return 1;
+	}
+	if (one.type == &ldouble_ctype) {
+		if (one.ldvalue < two.ldvalue)
+			return -1;
+		if (one.ldvalue == two.ldvalue)
+			return 0;
+		return 1;
+	}
+	sm_perror("bad type in fp_cmp(): %s", type_to_str(type));
+	return 1;
+}
+
 static inline int sval_cmp(sval_t one, sval_t two)
 {
 	struct symbol *type;
 
+	if (sval_is_fp(one) || sval_is_fp(two))
+		return fp_cmp(one, two);
+
 	type = one.type;
 	if (sval_positive_bits(two) > sval_positive_bits(one))
 		type = two.type;
--- old/usr/src/tools/smatch/src/smatch_address.c	Tue Jan  7 08:01:53 2020
+++ new/usr/src/tools/smatch/src/smatch_address.c	Tue Jan  7 08:01:53 2020
@@ -119,6 +119,11 @@
 	struct ident *member;
 	int offset;
 
+	/*
+	 * FIXME: This doesn't handle foo.u.bar correctly.
+	 *
+	 */
+
 	if (expr->type != EXPR_DEREF)  /* hopefully, this doesn't happen */
 		return -1;
 
@@ -202,25 +207,66 @@
 	if (!sym)
 		return NULL;
 
+	/* This should just be the mtag if it's not on the stack */
 	return alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
 }
 
+static bool handle_fn_address(struct expression *expr, struct range_list **rl)
+{
+	struct symbol *type;
+
+	if (expr->type == EXPR_PREOP && expr->op == '&')
+		expr = strip_expr(expr->unop);
+
+	if (expr->type != EXPR_SYMBOL)
+		return false;
+
+	type = get_type(expr);
+	if (!type || type->type != SYM_FN)
+		return false;
+
+	*rl = alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
+	return true;
+}
+
 int get_address_rl(struct expression *expr, struct range_list **rl)
 {
 	struct expression *unop;
 
+	/*
+	 * Ugh...  This function is bad.  It doesn't work where it's supposed to
+	 * and it does more than it really should.  It shouldn't handle string
+	 * literals I think...
+	 *
+	 * There are several complications.  For arrays and functions the "&foo"
+	 * "foo" are equivalent.  But the problem is that we're also passing in
+	 * foo->array[] and foo->fn.
+	 *
+	 * Then, when we have foo->bar.baz.one.two; that needs to be handled
+	 * correctly but right now, it is not.
+	 *
+	 */
+
 	expr = strip_expr(expr);
 	if (!expr)
 		return 0;
 
-	if (expr->type == EXPR_STRING) {
-		*rl = alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
+	/*
+	 * For functions &fn and fn are equivalent.  I don't know if this is
+	 * really the right place to handle it, but let's just get it out of the
+	 * way for now.
+	 *
+	 */
+	if (handle_fn_address(expr, rl))
 		return 1;
-	}
 
-	if (expr->type == EXPR_PREOP && expr->op == '&')
+	/*
+	 * For arrays, &foo->array and foo->array are equivalent.
+	 *
+	 */
+	if (expr->type == EXPR_PREOP && expr->op == '&') {
 		expr = strip_expr(expr->unop);
-	else {
+	} else {
 		struct symbol *type;
 
 		type = get_type(expr);
--- old/usr/src/tools/smatch/src/smatch_buf_size.c	Tue Jan  7 08:01:53 2020
+++ new/usr/src/tools/smatch/src/smatch_buf_size.c	Tue Jan  7 08:01:53 2020
@@ -910,6 +910,7 @@
 		add_allocation_function("kzalloc_node", &match_alloc, 0);
 		add_allocation_function("vmalloc", &match_alloc, 0);
 		add_allocation_function("__vmalloc", &match_alloc, 0);
+		add_allocation_function("kvmalloc", &match_alloc, 0);
 		add_allocation_function("kcalloc", &match_calloc, 0);
 		add_allocation_function("kmalloc_array", &match_calloc, 0);
 		add_allocation_function("drm_malloc_ab", &match_calloc, 0);
--- old/usr/src/tools/smatch/src/smatch_comparison.c	Tue Jan  7 08:01:53 2020
+++ new/usr/src/tools/smatch/src/smatch_comparison.c	Tue Jan  7 08:01:53 2020
@@ -32,7 +32,7 @@
 #include "smatch_extra.h"
 #include "smatch_slist.h"
 
-static int compare_id;
+int comparison_id;
 static int link_id;
 static int inc_dec_id;
 static int inc_dec_link_id;
@@ -542,6 +542,7 @@
 	int extra, new;
 	static bool in_recurse;
 
+	// FIXME.  No data is useless
 	if (!data)
 		return;
 
@@ -556,7 +557,8 @@
 	if (new == data->comparison)
 		return;
 
-	set_state(compare_id, cur->name, NULL,
+	// FIXME: we should always preserve implications
+	set_state(comparison_id, cur->name, NULL,
 		  alloc_compare_state(data->left, data->left_var, data->left_vsl,
 				      new,
 				      data->right, data->right_var, data->right_vsl));
@@ -624,7 +626,7 @@
 				NULL, param->ident->name, left_vsl,
 				SPECIAL_EQUAL,
 				NULL, alloc_sname(orig), right_vsl);
-		set_state(compare_id, state_name, NULL, state);
+		set_state(comparison_id, state_name, NULL, state);
 
 		link = alloc_sname(state_name);
 		links = NULL;
@@ -675,7 +677,7 @@
 	links = sm->state->data;
 
 	FOR_EACH_PTR(links, tmp) {
-		state = get_state(compare_id, tmp, NULL);
+		state = get_state(comparison_id, tmp, NULL);
 		if (!state)
 			continue;
 		data = state->data;
@@ -701,7 +703,7 @@
 					data->left, data->left_var, data->left_vsl,
 					flip ? '<' : '>',
 					data->right, data->right_var, data->right_vsl);
-			set_state(compare_id, tmp, NULL, new);
+			set_state(comparison_id, tmp, NULL, new);
 			break;
 		case '<':
 		case SPECIAL_UNSIGNED_LT:
@@ -709,7 +711,7 @@
 					data->left, data->left_var, data->left_vsl,
 					flip ? SPECIAL_GTE : SPECIAL_LTE,
 					data->right, data->right_var, data->right_vsl);
-			set_state(compare_id, tmp, NULL, new);
+			set_state(comparison_id, tmp, NULL, new);
 			break;
 		default:
 			new = alloc_compare_state(
@@ -716,7 +718,7 @@
 					data->left, data->left_var, data->left_vsl,
 					UNKNOWN_COMPARISON,
 					data->right, data->right_var, data->right_vsl);
-			set_state(compare_id, tmp, NULL, new);
+			set_state(comparison_id, tmp, NULL, new);
 		}
 	} END_FOR_EACH_PTR(tmp);
 }
@@ -733,7 +735,7 @@
 		struct compare_data *data;
 		struct smatch_state *new;
 
-		state = get_state(compare_id, tmp, NULL);
+		state = get_state(comparison_id, tmp, NULL);
 		if (!state || !state->data)
 			continue;
 
@@ -752,7 +754,7 @@
 					data->left, data->left_var, data->left_vsl,
 					'<',
 					data->right, data->right_var, data->right_vsl);
-			set_state(compare_id, tmp, NULL, new);
+			set_state(comparison_id, tmp, NULL, new);
 			break;
 			}
 		default:
@@ -760,7 +762,7 @@
 					data->left, data->left_var, data->left_vsl,
 					UNKNOWN_COMPARISON,
 					data->right, data->right_var, data->right_vsl);
-			set_state(compare_id, tmp, NULL, new);
+			set_state(comparison_id, tmp, NULL, new);
 		}
 	} END_FOR_EACH_PTR(tmp);
 }
@@ -775,7 +777,7 @@
 	FOR_EACH_PTR(links, tmp) {
 		struct smatch_state *old, *new;
 
-		old = get_state(compare_id, tmp, NULL);
+		old = get_state(comparison_id, tmp, NULL);
 		if (!old || !old->data) {
 			new = &undefined;
 		} else {
@@ -786,7 +788,7 @@
 					UNKNOWN_COMPARISON,
 					data->right, data->right_var, data->right_vsl);
 		}
-		set_state(compare_id, tmp, NULL, new);
+		set_state(comparison_id, tmp, NULL, new);
 	} END_FOR_EACH_PTR(tmp);
 	set_state(link_id, sm->name, sm->sym, &undefined);
 }
@@ -988,7 +990,7 @@
 	}
 
 	snprintf(state_name, sizeof(state_name), "%s vs %s", left, right);
-	state = get_state_stree(pre_stree, compare_id, state_name, NULL);
+	state = get_state_stree(pre_stree, comparison_id, state_name, NULL);
 	if (!state || !state->data)
 		return 0;
 	data = state->data;
@@ -1041,7 +1043,7 @@
 	struct var_sym *vs;
 
 	FOR_EACH_PTR(links, tmp) {
-		state = get_state_stree(pre_stree, compare_id, tmp, NULL);
+		state = get_state_stree(pre_stree, comparison_id, tmp, NULL);
 		if (!state || !state->data)
 			continue;
 		data = state->data;
@@ -1100,7 +1102,7 @@
 			false_state = NULL;
 
 		snprintf(state_name, sizeof(state_name), "%s vs %s", left_var, right_var);
-		set_true_false_states(compare_id, state_name, NULL, true_state, false_state);
+		set_true_false_states(comparison_id, state_name, NULL, true_state, false_state);
 		FOR_EACH_PTR(left_vsl, vs) {
 			save_link_var_sym(vs->var, vs->sym, state_name);
 		} END_FOR_EACH_PTR(vs);
@@ -1199,7 +1201,8 @@
 			SPECIAL_EQUAL,
 			data->right, data->right_var, data->right_vsl);
 
-	set_true_false_states(compare_id, state_name, NULL, NULL, false_state);
+	// FIXME: This doesn't handle links correct so it doesn't set "param orig"
+	set_true_false_states(comparison_id, state_name, NULL, NULL, false_state);
 }
 
 static int is_plus_one(struct expression *expr)
@@ -1340,7 +1343,7 @@
 	update_tf_data(pre_stree, left_expr, left, left_vsl, right_expr, right, right_vsl, op, false_op);
 	free_stree(&pre_stree);
 
-	set_true_false_states(compare_id, state_name, NULL, true_state, false_state);
+	set_true_false_states(comparison_id, state_name, NULL, true_state, false_state);
 	__compare_param_limit_hook(left_expr, right_expr, state_name, true_state, false_state);
 	save_link(left_expr, state_name);
 	save_link(right_expr, state_name);
@@ -1442,7 +1445,7 @@
 			comparison,
 			right_expr, right_name, right_vsl);
 
-	set_state(compare_id, state_name, NULL, state);
+	set_state(comparison_id, state_name, NULL, state);
 
 	FOR_EACH_PTR(left_vsl, vs) {
 		save_link_var_sym(vs->var, vs->sym, state_name);
@@ -1492,7 +1495,7 @@
 			comparison,
 			right, right_name, right_vsl);
 
-	set_state(compare_id, state_name, NULL, state);
+	set_state(comparison_id, state_name, NULL, state);
 	save_link(left, state_name);
 	save_link(right, state_name);
 
@@ -1609,7 +1612,7 @@
 	links = state->data;
 
 	FOR_EACH_PTR(links, tmp) {
-		state = get_state(compare_id, tmp, NULL);
+		state = get_state(comparison_id, tmp, NULL);
 		if (!state || !state->data)
 			continue;
 		data = state->data;
@@ -1678,7 +1681,7 @@
 	}
 
 	snprintf(buf, sizeof(buf), "%s vs %s", one, two);
-	state = get_state(compare_id, buf, NULL);
+	state = get_state(comparison_id, buf, NULL);
 	if (state)
 		ret = state_to_comparison(state);
 
@@ -1784,7 +1787,7 @@
 	}
 
 	snprintf(buf, sizeof(buf), "%s vs %s", one, two);
-	sm = get_sm_state(compare_id, buf, NULL);
+	sm = get_sm_state(comparison_id, buf, NULL);
 	if (!sm)
 		goto free;
 
@@ -1825,7 +1828,7 @@
 	links = state->data;
 
 	FOR_EACH_PTR(links, tmp) {
-		sm = get_sm_state(compare_id, tmp, NULL);
+		sm = get_sm_state(comparison_id, tmp, NULL);
 		if (!sm)
 			continue;
 		// FIXME have to compare name with vsl
@@ -1849,7 +1852,7 @@
 	links = state->data;
 
 	FOR_EACH_PTR(links, tmp) {
-		sm = get_sm_state(compare_id, tmp, NULL);
+		sm = get_sm_state(comparison_id, tmp, NULL);
 		if (!sm)
 			continue;
 		if (!strchr(sm->state->name, '='))
@@ -1879,7 +1882,7 @@
 	links = state->data;
 
 	FOR_EACH_PTR(links, link) {
-		sm = get_sm_state(compare_id, link, NULL);
+		sm = get_sm_state(comparison_id, link, NULL);
 		if (!sm)
 			continue;
 		FOR_EACH_PTR(sm->possible, possible) {
@@ -1924,7 +1927,7 @@
 	links = state->data;
 
 	FOR_EACH_PTR(links, tmp) {
-		state = get_state(compare_id, tmp, NULL);
+		state = get_state(comparison_id, tmp, NULL);
 		if (!state || !state->data)
 			continue;
 		data = state->data;
@@ -2173,7 +2176,7 @@
 
 			if (strstr(link, " orig"))
 				continue;
-			sm = get_sm_state(compare_id, link, NULL);
+			sm = get_sm_state(comparison_id, link, NULL);
 			if (!sm)
 				continue;
 			data = sm->state->data;
@@ -2228,7 +2231,7 @@
 
 	links = link_sm->state->data;
 	FOR_EACH_PTR(links, link) {
-		compare_sm = get_sm_state(compare_id, link, NULL);
+		compare_sm = get_sm_state(comparison_id, link, NULL);
 		if (!compare_sm)
 			continue;
 		data = compare_sm->state->data;
@@ -2321,7 +2324,7 @@
 			continue;
 		links = tmp->state->data;
 		FOR_EACH_PTR(links, link) {
-			sm = get_sm_state(compare_id, link, NULL);
+			sm = get_sm_state(comparison_id, link, NULL);
 			if (!sm)
 				continue;
 			data = sm->state->data;
@@ -2539,7 +2542,7 @@
 		goto free;
 
 	snprintf(buf, sizeof(buf), "%s vs %s", left_name, right_name);
-	state = get_state(compare_id, buf, NULL);
+	state = get_state(comparison_id, buf, NULL);
 	if (!state)
 		goto free;
 	state_op = state_to_comparison(state);
@@ -2573,7 +2576,7 @@
 
 	links = link_state->data;
 	FOR_EACH_PTR(links, link) {
-		sm = get_sm_state(compare_id, link, NULL);
+		sm = get_sm_state(comparison_id, link, NULL);
 		if (!sm)
 			continue;
 		data = sm->state->data;
@@ -2595,12 +2598,12 @@
 
 void register_comparison(int id)
 {
-	compare_id = id;
-	set_dynamic_states(compare_id);
+	comparison_id = id;
+	set_dynamic_states(comparison_id);
 	add_hook(&save_start_states, AFTER_DEF_HOOK);
-	add_unmatched_state_hook(compare_id, unmatched_comparison);
-	add_pre_merge_hook(compare_id, &pre_merge_hook);
-	add_merge_hook(compare_id, &merge_compare_states);
+	add_unmatched_state_hook(comparison_id, unmatched_comparison);
+	add_pre_merge_hook(comparison_id, &pre_merge_hook);
+	add_merge_hook(comparison_id, &merge_compare_states);
 	add_hook(&free_data, AFTER_FUNC_HOOK);
 	add_hook(&match_call_info, FUNCTION_CALL_HOOK);
 	add_split_return_callback(&print_return_comparison);
@@ -2639,9 +2642,60 @@
 	set_up_link_functions(inc_dec_id, inc_dec_link_id);
 }
 
+static struct sm_state *clone_partial_sm(struct sm_state *sm, int comparison)
+{
+	struct compare_data *data;
+	struct sm_state *clone;
+	struct stree *stree;
+
+	data = sm->state->data;
+
+	clone = clone_sm(sm);
+	clone->state = alloc_compare_state(data->left, data->left_var, data->left_vsl,
+					   comparison,
+					   data->right, data->right_var, data->right_vsl);
+	free_slist(&clone->possible);
+	add_possible_sm(clone, clone);
+
+	stree = clone_stree(sm->pool);
+	overwrite_sm_state_stree(&stree, clone);
+	clone->pool = stree;
+
+	return clone;
+}
+
+static void create_fake_history(struct sm_state *sm, int op,
+			       struct state_list **true_stack,
+			       struct state_list **false_stack)
+{
+	struct sm_state *true_sm, *false_sm;
+	struct compare_data *data;
+	int true_comparison;
+	int false_comparison;
+
+	data = sm->state->data;
+
+	if (is_merged(sm) || sm->left || sm->right)
+		return;
+
+	true_comparison = comparison_intersection(data->comparison, op);
+	false_comparison = comparison_intersection(data->comparison, negate_comparison(op));
+
+	true_sm = clone_partial_sm(sm, true_comparison);
+	false_sm = clone_partial_sm(sm, false_comparison);
+
+	sm->merged = 1;
+	sm->left = true_sm;
+	sm->right = false_sm;
+
+	add_ptr_list(true_stack, true_sm);
+	add_ptr_list(false_stack, false_sm);
+}
+
 static void filter_by_sm(struct sm_state *sm, int op,
 		       struct state_list **true_stack,
-		       struct state_list **false_stack)
+		       struct state_list **false_stack,
+		       bool *useful)
 {
 	struct compare_data *data;
 	int is_true = 0;
@@ -2650,7 +2704,7 @@
 	if (!sm)
 		return;
 	data = sm->state->data;
-	if (!data || data->comparison == UNKNOWN_COMPARISON)
+	if (!data)
 		goto split;
 	if (data->comparison == IMPOSSIBLE_COMPARISON)
 		return;
@@ -2667,6 +2721,11 @@
 	if (data->comparison == comparison_intersection(data->comparison, negate_comparison(op)))
 		is_false = 1;
 
+	if (!is_true && !is_false && !is_merged(sm)) {
+		create_fake_history(sm, op, true_stack, false_stack);
+		return;
+	}
+
 	if (debug_implied()) {
 		sm_msg("%s: %s: op = '%s' negated '%s'. true_intersect = '%s' false_insersect = '%s' sm = '%s'",
 		       __func__,
@@ -2678,13 +2737,14 @@
 		       show_sm(sm));
 	}
 
+	*useful = true;
 	if (is_true)
 		add_ptr_list(true_stack, sm);
 	if (is_false)
 		add_ptr_list(false_stack, sm);
 split:
-	filter_by_sm(sm->left, op, true_stack, false_stack);
-	filter_by_sm(sm->right, op, true_stack, false_stack);
+	filter_by_sm(sm->left, op, true_stack, false_stack, useful);
+	filter_by_sm(sm->right, op, true_stack, false_stack, useful);
 }
 
 struct sm_state *comparison_implication_hook(struct expression *expr,
@@ -2695,6 +2755,7 @@
 	char *left, *right;
 	int op;
 	static char buf[256];
+	bool useful = false;
 
 	if (expr->type != EXPR_COMPARE)
 		return NULL;
@@ -2718,14 +2779,14 @@
 	}
 
 	snprintf(buf, sizeof(buf), "%s vs %s", left, right);
-	sm = get_sm_state(compare_id, buf, NULL);
+	sm = get_sm_state(comparison_id, buf, NULL);
 	if (!sm)
 		return NULL;
 	if (!sm->merged)
 		return NULL;
 
-	filter_by_sm(sm, op, true_stack, false_stack);
-	if (!*true_stack && !*false_stack)
+	filter_by_sm(sm, op, true_stack, false_stack, &useful);
+	if (!useful)
 		return NULL;
 
 	if (debug_implied())
--- old/usr/src/tools/smatch/src/smatch_container_of.c	Tue Jan  7 08:01:53 2020
+++ new/usr/src/tools/smatch/src/smatch_container_of.c	Tue Jan  7 08:01:53 2020
@@ -22,11 +22,6 @@
 static int my_id;
 static int param_id;
 
-static struct stree *used_stree;
-static struct stree_stack *saved_stack;
-
-STATE(used);
-
 int get_param_from_container_of(struct expression *expr)
 {
 	struct expression *param_expr;
@@ -84,121 +79,6 @@
 	return sval.value;
 }
 
-static int get_container_arg(struct symbol *sym)
-{
-	struct expression *__mptr;
-	int param;
-
-	if (!sym || !sym->ident)
-		return -1;
-
-	__mptr = get_assigned_expr_name_sym(sym->ident->name, sym);
-	param = get_param_from_container_of(__mptr);
-
-	return param;
-}
-
-static int get_container_offset(struct symbol *sym)
-{
-	struct expression *__mptr;
-	int offset;
-
-	if (!sym || !sym->ident)
-		return -1;
-
-	__mptr = get_assigned_expr_name_sym(sym->ident->name, sym);
-	offset = get_offset_from_container_of(__mptr);
-
-	return offset;
-}
-
-static char *get_container_name_sm(struct sm_state *sm, int offset)
-{
-	static char buf[256];
-	const char *name;
-
-	name = get_param_name(sm);
-	if (!name)
-		return NULL;
-
-	if (name[0] == '$')
-		snprintf(buf, sizeof(buf), "$(-%d)%s", offset, name + 1);
-	else if (name[0] == '*' || name[1] == '$')
-		snprintf(buf, sizeof(buf), "*$(-%d)%s", offset, name + 2);
-	else
-		return NULL;
-
-	return buf;
-}
-
-static void get_state_hook(int owner, const char *name, struct symbol *sym)
-{
-	int arg;
-
-	if (!option_info)
-		return;
-	if (__in_fake_assign)
-		return;
-
-	arg = get_container_arg(sym);
-	if (arg >= 0)
-		set_state_stree(&used_stree, my_id, name, sym, &used);
-}
-
-static void set_param_used(struct expression *call, struct expression *arg, char *key, char *unused)
-{
-	struct symbol *sym;
-	char *name;
-	int arg_nr;
-
-	name = get_variable_from_key(arg, key, &sym);
-	if (!name || !sym)
-		goto free;
-
-	arg_nr = get_container_arg(sym);
-	if (arg_nr >= 0)
-		set_state(my_id, name, sym, &used);
-free:
-	free_string(name);
-}
-
-static void process_states(void)
-{
-	struct sm_state *tmp;
-	int arg, offset;
-	const char *name;
-
-	FOR_EACH_SM(used_stree, tmp) {
-		arg = get_container_arg(tmp->sym);
-		offset = get_container_offset(tmp->sym);
-		if (arg < 0 || offset < 0)
-			continue;
-		name = get_container_name_sm(tmp, offset);
-		if (!name)
-			continue;
-		sql_insert_return_implies(CONTAINER, arg, name, "");
-	} END_FOR_EACH_SM(tmp);
-
-	free_stree(&used_stree);
-}
-
-static void match_function_def(struct symbol *sym)
-{
-	free_stree(&used_stree);
-}
-
-static void match_save_states(struct expression *expr)
-{
-	push_stree(&saved_stack, used_stree);
-	used_stree = NULL;
-}
-
-static void match_restore_states(struct expression *expr)
-{
-	free_stree(&used_stree);
-	used_stree = pop_stree(&saved_stack);
-}
-
 static void print_returns_container_of(int return_id, char *return_ranges, struct expression *expr)
 {
 	int offset;
@@ -221,34 +101,6 @@
 			key, value);
 }
 
-static void returns_container_of(struct expression *expr, int param, char *key, char *value)
-{
-	struct expression *call, *arg;
-	int offset;
-	char buf[64];
-
-	if (expr->type != EXPR_ASSIGNMENT || expr->op != '=')
-		return;
-	call = strip_expr(expr->right);
-	if (call->type != EXPR_CALL)
-		return;
-	if (param != -1)
-		return;
-	param = atoi(key);
-	offset = atoi(value);
-
-	arg = get_argument_from_call_expr(call->args, param);
-	if (!arg)
-		return;
-	if (arg->type != EXPR_SYMBOL)
-		return;
-	param = get_param_num(arg);
-	if (param < 0)
-		return;
-	snprintf(buf, sizeof(buf), "$(%d)", offset);
-	sql_insert_return_implies(CONTAINER, param, buf, "");
-}
-
 static int get_deref_count(struct expression *expr)
 {
 	int cnt = 0;
@@ -299,21 +151,21 @@
 	return n;
 }
 
-static char *get_shared_str(struct expression *container, struct expression *expr)
+static char *get_shared_str(struct expression *expr, struct expression *container)
 {
 	struct expression *one, *two;
-	int cont, exp, min, ret, n;
+	int exp, cont, min, ret, n;
 	static char buf[48];
 
-	cont = get_deref_count(container);
 	exp = get_deref_count(expr);
-	if (cont < 0 || exp < 0)
+	cont = get_deref_count(container);
+	if (exp < 0 || cont < 0)
 		return NULL;
 
-	min = (cont < exp) ? cont : exp;
+	min = (exp < cont) ? exp : cont;
 	while (min >= 0) {
-		one = get_partial_deref(container, cont - min);
-		two = get_partial_deref(expr, exp - min);
+		one = get_partial_deref(expr, exp - min);
+		two = get_partial_deref(container, cont - min);
 		if (expr_equiv(one, two))
 			goto found;
 		min--;
@@ -322,11 +174,11 @@
 	return NULL;
 
 found:
-	ret = partial_deref_to_offset_str(container, cont - min, '-', buf, sizeof(buf));
+	ret = partial_deref_to_offset_str(expr, exp - min, '-', buf, sizeof(buf));
 	if (ret < 0)
 		return NULL;
 	n = ret;
-	ret = partial_deref_to_offset_str(expr, exp - min, '+', buf + ret, sizeof(buf) - ret);
+	ret = partial_deref_to_offset_str(container, cont - min, '+', buf + ret, sizeof(buf) - ret);
 	if (ret < 0)
 		return NULL;
 	n += ret;
@@ -336,50 +188,90 @@
 	return buf;
 }
 
+static char *get_stored_container_name(struct expression *container,
+				       struct expression *expr)
+{
+	struct smatch_state *state;
+	static char buf[64];
+	char *p;
+	int param;
+
+	if (!container || container->type != EXPR_SYMBOL)
+		return NULL;
+	if (!expr || expr->type != EXPR_SYMBOL)
+		return NULL;
+	state = get_state_expr(param_id, expr);
+	if (!state)
+		return NULL;
+
+	snprintf(buf, sizeof(buf), "%s", state->name);
+	p = strchr(buf, '|');
+	if (!p)
+		return NULL;
+	*p = '\0';
+	param = atoi(p + 2);
+	if (get_param_sym_from_num(param) == container->symbol)
+		return buf;
+	return NULL;
+}
+
 char *get_container_name(struct expression *container, struct expression *expr)
 {
 	struct symbol *container_sym, *sym;
 	struct expression *tmp;
 	static char buf[64];
-	char *shared;
+	char *ret, *shared;
 	bool star;
 	int cnt;
 
-	container_sym = expr_to_sym(container);
+	expr = strip_expr(expr);
+	container = strip_expr(container);
+
+	ret = get_stored_container_name(container, expr);
+	if (ret)
+		return ret;
+
 	sym = expr_to_sym(expr);
-	if (container_sym && container_sym == sym)
+	container_sym = expr_to_sym(container);
+	if (sym && sym == container_sym)
 		goto found;
 
 	cnt = 0;
-	while ((tmp = get_assigned_expr(expr))) {
-		expr = tmp;
+	while ((tmp = get_assigned_expr(container))) {
+		container = strip_expr(tmp);
 		if (cnt++ > 3)
 			break;
 	}
 
 	cnt = 0;
-	while ((tmp = get_assigned_expr(container))) {
-		container = tmp;
+	while ((tmp = get_assigned_expr(expr))) {
+		expr = strip_expr(tmp);
 		if (cnt++ > 3)
 			break;
 	}
 
 found:
-	expr = strip_expr(expr);
-	star = true;
-	if (expr->type == EXPR_PREOP && expr->op == '&') {
-		expr = strip_expr(expr->unop);
+
+	if (container->type == EXPR_DEREF)
+		star = true;
+	else
 		star = false;
-	}
 
-	container_sym = expr_to_sym(container);
-	if (!container_sym)
-		return NULL;
+	if (container->type == EXPR_PREOP && container->op == '&')
+		container = strip_expr(container->unop);
+	if (expr->type == EXPR_PREOP && expr->op == '&')
+		expr = strip_expr(expr->unop);
+
 	sym = expr_to_sym(expr);
-	if (!sym || container_sym != sym)
+	if (!sym)
 		return NULL;
+	container_sym = expr_to_sym(container);
+	if (!container_sym || sym != container_sym)
+		return NULL;
 
-	shared = get_shared_str(container, expr);
+	shared = get_shared_str(expr, container);
+	if (!shared)
+		return NULL;
 	if (star)
 		snprintf(buf, sizeof(buf), "*(%s)", shared);
 	else
@@ -388,11 +280,31 @@
 	return buf;
 }
 
+static bool is_fn_ptr(struct expression *expr)
+{
+	struct symbol *type;
+
+	if (!expr)
+		return false;
+	if (expr->type != EXPR_SYMBOL && expr->type != EXPR_DEREF)
+		return false;
+
+	type = get_type(expr);
+	if (!type || type->type != SYM_PTR)
+		return false;
+	type = get_real_base_type(type);
+	if (!type || type->type != SYM_FN)
+		return false;
+	return true;
+}
+
 static void match_call(struct expression *call)
 {
-	struct expression *fn, *arg;
+	struct expression *fn, *arg, *tmp;
+	bool found = false;
+	int fn_param, param;
+	char buf[32];
 	char *name;
-	int param;
 
 	/*
 	 * We're trying to link the function with the parameter.  There are a
@@ -414,17 +326,47 @@
 	FOR_EACH_PTR(call->args, arg) {
 		param++;
 
-		name = get_container_name(fn, arg);
+		name = get_container_name(arg, fn);
 		if (!name)
 			continue;
 
+		found = true;
 		sql_insert_caller_info(call, CONTAINER, param, name, "$(-1)");
 	} END_FOR_EACH_PTR(arg);
+
+	if (found)
+		return;
+
+	fn_param = -1;
+	FOR_EACH_PTR(call->args, arg) {
+		fn_param++;
+		if (!is_fn_ptr(arg))
+			continue;
+		param = -1;
+		FOR_EACH_PTR(call->args, tmp) {
+			param++;
+
+			/* the function isn't it's own container */
+			if (arg == tmp)
+				continue;
+
+			name = get_container_name(tmp, arg);
+			if (!name)
+				continue;
+
+			snprintf(buf, sizeof(buf), "$%d", param);
+			sql_insert_caller_info(call, CONTAINER, fn_param, name, buf);
+			return;
+		} END_FOR_EACH_PTR(tmp);
+	} END_FOR_EACH_PTR(arg);
 }
 
 static void db_passed_container(const char *name, struct symbol *sym, char *key, char *value)
 {
-	set_state(param_id, name, sym, alloc_state_str(key));
+	char buf[64];
+
+	snprintf(buf, sizeof(buf), "%s|%s", key, value);
+	set_state(param_id, name, sym, alloc_state_str(buf));
 }
 
 struct db_info {
@@ -495,7 +437,7 @@
 	}
 
 	member = get_member_from_offset(arg, offset);
-	if (!member)
+	if (!member || !member->ident)
 		return NULL;
 
 	snprintf(fullname, sizeof(fullname), "%s->%s", name, member->ident->name);
@@ -605,16 +547,27 @@
 {
 	mtag_t cur_tag, container_tag, arg_tag;
 	int container_offset, arg_offset;
-	char *p = (char *)info;
 	struct sm_state *sm;
 	struct stree *stree;
+	char *p, *cont;
+	char copy[64];
 	bool star = 0;
 
+	snprintf(copy, sizeof(copy), "%s", info);
+	p = strchr(copy, '|');
+	if (!p)
+		return;
+	*p = '\0';
+	cont = p + 1;
+	p = copy;
 	if (p[0] == '*') {
 		star = 1;
 		p += 2;
 	}
 
+	if (strcmp(cont, "$(-1)") != 0)
+		return;
+
 	if (!get_toplevel_mtag(cur_func_sym, &cur_tag))
 		return;
 
@@ -675,19 +628,7 @@
 {
 	my_id = id;
 
-	add_hook(&match_function_def, FUNC_DEF_HOOK);
-
-	add_get_state_hook(&get_state_hook);
-
-	add_hook(&match_save_states, INLINE_FN_START);
-	add_hook(&match_restore_states, INLINE_FN_END);
-
-	select_return_implies_hook(CONTAINER, &set_param_used);
-	all_return_states_hook(&process_states);
-
 	add_split_return_callback(&print_returns_container_of);
-	select_return_states_hook(CONTAINER, &returns_container_of);
-
 	add_hook(&match_call, FUNCTION_CALL_HOOK);
 }
 
--- old/usr/src/tools/smatch/src/smatch_data/db/create_db.sh	Tue Jan  7 08:01:54 2020
+++ new/usr/src/tools/smatch/src/smatch_data/db/create_db.sh	Tue Jan  7 08:01:54 2020
@@ -52,5 +52,6 @@
 echo "delete from function_ptr where rowid not in (select min(rowid) from function_ptr group by file, function, ptr, searchable);" | sqlite3 $db_file
 
 ${bin_dir}/apply_return_fixes.sh -p=${PROJ} $db_file
+${bin_dir}/insert_manual_states.pl ${PROJ} $db_file
 
 mv $db_file smatch_db.sqlite
--- old/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh	Tue Jan  7 08:01:54 2020
+++ new/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh	Tue Jan  7 08:01:54 2020
@@ -180,6 +180,11 @@
 /* The problem is that parsing big function pointers is hard. */
 delete from return_states where function = 'vfs_get_tree' and type = 1024;
 
+/* Locking stuff goes here.  */
+update return_states set parameter = -1, key = '\$' where function = 'ipmi_ssif_lock_cond' and type = 8020 and parameter = 1;
+update return_states set parameter = 1, key = '\$->tree->tree_lock' where function = 'hfs_find_init' and type = 8020 and parameter = 0;
+delete from return_states where function = '__oom_kill_process' and type = 8021;
+
 EOF
 
 # fixme: this is totally broken
--- /dev/null	Tue Jan  7 08:01:54 2020
+++ new/usr/src/tools/smatch/src/smatch_data/db/insert_manual_states.pl	Tue Jan  7 08:01:54 2020
@@ -0,0 +1,93 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+use File::Basename;
+use DBI;
+
+my $bin_dir = dirname($0);
+my $project = shift;
+my $db_file = shift;
+if (!defined($db_file)) {
+    print "usage:  $0 <project> <db_file>\n";
+    exit(1);
+}
+my $insertions = "$bin_dir/$project.insert.return_states";
+
+my $db = DBI->connect("dbi:SQLite:$db_file", "", "", {AutoCommit => 0});
+$db->do("PRAGMA cache_size = 800000");
+$db->do("PRAGMA journal_mode = OFF");
+$db->do("PRAGMA count_changes = OFF");
+$db->do("PRAGMA temp_store = MEMORY");
+$db->do("PRAGMA locking = EXCLUSIVE");
+
+sub insert_record($$$$$$$)
+{
+    my $file = shift;
+    my $func = shift;
+    my $ret = shift;
+    my $type = shift;
+    my $param = shift;
+    my $key = shift;
+    my $value = shift;
+
+#    print "file = '$file' func = '$func' ret = $ret\n";
+#    print "type = $type param = $param, key = $key, value = '$value'\n";
+#    print "select file, return_id, return, static from return_states where function = '$func' and return = '$ret' and type = 0;'\n";
+
+    my $sth;
+    if ($file ne '') {
+        $sth = $db->prepare("select file, return_id, static from return_states where file = ? and function = ? and return = ? and type = 0;");
+        $sth->execute($file, $func, $ret);
+    } else {
+        $sth = $db->prepare("select file, return_id, static from return_states where function = ? and return = ? and type = 0;");
+        $sth->execute($func, $ret);
+    }
+
+    my $insert = $db->prepare("insert into return_states values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);");
+    while (my @row = $sth->fetchrow_array()) {
+        my $file = $row[0];
+        my $return_id = $row[1];
+        my $static = $row[2];
+
+        $insert->execute($file, $func, 0, $return_id, $ret, $static, $type, $param, $key, $value);
+    }
+}
+
+my ($ret, $insert, $file, $func, $type, $param, $key, $value);
+
+open(FILE, "<$insertions");
+while (<FILE>) {
+
+    if ($_ =~ /^\s*#/) {
+        next;
+    }
+
+    ($ret, $insert) = split(/\|/, $_);
+
+    if ($ret =~ /(.+),\W*(.+),\W*"(.*)"/) {
+        $file = $1;
+        $func = $2;
+        $ret = $3;
+    } elsif ($ret =~ /(.+),\W*"(.*)"/) {
+        $file = "";
+        $func = $1;
+        $ret = $2;
+    } else {
+        next;
+    }
+
+    ($type, $param, $key, $value) = split(/,/, $insert);
+
+    $type = int($type);
+    $param = int($param);
+    $key =~ s/^["\s]+|["\s]+$//g;
+    $value =~ s/^["\s]+|["\s]+$//g;
+    chomp($value);
+
+    insert_record($file, $func, $ret, $type, $param, $key, $value);
+}
+close(FILE);
+
+$db->commit();
+$db->disconnect();
--- /dev/null	Tue Jan  7 08:01:54 2020
+++ new/usr/src/tools/smatch/src/smatch_data/db/kernel.insert.return_states	Tue Jan  7 08:01:54 2020
@@ -0,0 +1,12 @@
+# The format to insert is:
+# file (optional), function, "return values" | type, param, "key", "value"
+#
+
+mlx5_cmd_comp_handler, "" | 8021, -2, "*sem", ""
+smc_connect_abort, "1-s32max[==$1]"                | 8021, -2, "smc_client_lgr_pending", ""
+smc_connect_abort, "s32min-(-12),(-10)-(-1)[==$1]" | 8021, -2, "smc_client_lgr_pending", ""
+smc_connect_abort, "1-s32max[==$1]"                | 8021, -2, "smc_server_lgr_pending", ""
+smc_connect_abort, "s32min-(-12),(-10)-(-1)[==$1]" | 8021, -2, "smc_server_lgr_pending", ""
+dlfb_submit_urb, "0" | 8021, 0, "$->urbs.limit_sem", ""
+hfs_find_exit, "" | 8021, 0, "$->tree->tree_lock", ""
+btrfs_tree_unlock, "" | 8021, 0, "$->lock", ""
--- old/usr/src/tools/smatch/src/smatch_data/illumos_kernel.skipped_functions	Tue Jan  7 08:01:54 2020
+++ new/usr/src/tools/smatch/src/smatch_data/illumos_kernel.skipped_functions	Tue Jan  7 08:01:54 2020
@@ -12,3 +12,4 @@
 sadb_common_add
 segvn_fault_vnodepages
 tcp_input_data
+zfs_setattr
--- old/usr/src/tools/smatch/src/smatch_data/kernel.no_inline_functions	Tue Jan  7 08:01:54 2020
+++ new/usr/src/tools/smatch/src/smatch_data/kernel.no_inline_functions	Tue Jan  7 08:01:54 2020
@@ -12,3 +12,5 @@
 atomic_set
 atomic_read
 notifier_to_errno
+ipmi_ssif_lock_cond
+smc_connect_abort
--- old/usr/src/tools/smatch/src/smatch_db.c	Tue Jan  7 08:01:54 2020
+++ new/usr/src/tools/smatch/src/smatch_db.c	Tue Jan  7 08:01:54 2020
@@ -27,8 +27,12 @@
 struct sqlite3 *mem_db;
 struct sqlite3 *cache_db;
 
+int debug_db;
+
 static int return_id;
 
+static void call_return_state_hooks(struct expression *expr);
+
 #define SQLITE_CACHE_PAGES 1000
 
 struct def_callback {
@@ -105,7 +109,7 @@
 
 	for (i = 0; i < argc; i++) {
 		if (i != 0)
-			printf(", ");
+			sm_printf(", ");
 		sm_printf("%s", argv[i]);
 	}
 	sm_printf("\n");
@@ -120,7 +124,7 @@
 	if (!db)
 		return;
 
-	if (option_debug) {
+	if (option_debug || debug_db) {
 		sm_msg("%s", sql);
 		if (strncasecmp(sql, "select", strlen("select")) == 0)
 			sqlite3_exec(db, sql, print_sql_output, NULL, NULL);
@@ -801,6 +805,61 @@
 	}
 }
 
+char *sm_to_arg_name(struct expression *expr, struct sm_state *sm)
+{
+	struct symbol *sym;
+	const char *sm_name;
+	char *name;
+	bool is_address = false;
+	bool add_star = false;
+	char buf[256];
+	char *ret = NULL;
+	int len;
+
+	expr = strip_expr(expr);
+	if (!expr)
+		return NULL;
+
+	if (expr->type == EXPR_PREOP && expr->op == '&') {
+		expr = strip_expr(expr->unop);
+		is_address = true;
+	}
+
+	name = expr_to_var_sym(expr, &sym);
+	if (!name || !sym)
+		goto free;
+	if (sym != sm->sym)
+		goto free;
+
+	sm_name = sm->name;
+	add_star = false;
+	if (sm_name[0] == '*') {
+		add_star = true;
+		sm_name++;
+	}
+
+	len = strlen(name);
+	if (strncmp(name, sm_name, len) != 0)
+		goto free;
+	if (sm_name[len] == '\0') {
+		snprintf(buf, sizeof(buf), "%s%s$",
+			 add_star ? "*" : "", is_address ? "*" : "");
+	} else {
+		if (sm_name[len] != '.' && sm_name[len] != '-')
+			goto free;
+		if (sm_name[len] == '-')
+			len++;
+		// FIXME does is_address really imply that sm_name[len] == '-'
+		snprintf(buf, sizeof(buf), "%s$->%s", add_star ? "*" : "",
+			 sm_name + len);
+	}
+
+	ret = alloc_sname(buf);
+free:
+	free_string(name);
+	return ret;
+}
+
 static void print_struct_members(struct expression *call, struct expression *expr, int param, int offset, struct stree *stree,
 	void (*callback)(struct expression *call, int param, char *printed_name, struct sm_state *sm))
 {
@@ -1417,13 +1476,19 @@
 	sql_insert_return_states(return_id, return_ranges, INTERNAL, -1, "", function_signature());
 }
 
-static void call_return_state_hooks_conditional(struct expression *expr)
+static bool call_return_state_hooks_conditional(struct expression *expr)
 {
-	struct returned_state_callback *cb;
-	struct range_list *rl;
-	const char *return_ranges;
 	int final_pass_orig = final_pass;
+	static int recurse;
 
+	if (recurse >= 2)
+		return false;
+	if (!expr ||
+	    (expr->type != EXPR_CONDITIONAL && expr->type != EXPR_SELECT))
+		return false;
+
+	recurse++;
+
 	__push_fake_cur_stree();
 
 	final_pass = 0;
@@ -1430,28 +1495,18 @@
 	__split_whole_condition(expr->conditional);
 	final_pass = final_pass_orig;
 
-	return_ranges = get_return_ranges_str(expr->cond_true ?: expr->conditional, &rl);
+	call_return_state_hooks(expr->cond_true ?: expr->conditional);
 
-	set_state(RETURN_ID, "return_ranges", NULL, alloc_estate_rl(rl));
-
-	return_id++;
-	FOR_EACH_PTR(returned_state_callbacks, cb) {
-		cb->callback(return_id, (char *)return_ranges, expr->cond_true);
-	} END_FOR_EACH_PTR(cb);
-
 	__push_true_states();
 	__use_false_states();
 
-	return_ranges = get_return_ranges_str(expr->cond_false, &rl);
-	set_state(RETURN_ID, "return_ranges", NULL, alloc_estate_rl(rl));
+	call_return_state_hooks(expr->cond_false);
 
-	return_id++;
-	FOR_EACH_PTR(returned_state_callbacks, cb) {
-		cb->callback(return_id, (char *)return_ranges, expr->cond_false);
-	} END_FOR_EACH_PTR(cb);
-
 	__merge_true_states();
 	__free_fake_cur_stree();
+
+	recurse--;
+	return true;
 }
 
 static void call_return_state_hooks_compare(struct expression *expr)
@@ -1536,8 +1591,12 @@
 	FOR_EACH_PTR(sm->possible, tmp) {
 		if (tmp->merged)
 			continue;
+		if (ptr_in_list(tmp, already_handled))
+			continue;
+		add_ptr_list(&already_handled, tmp);
 		nr_possible++;
 	} END_FOR_EACH_PTR(tmp);
+	free_slist(&already_handled);
 	nr_states = get_db_state_count();
 	if (nr_states * nr_possible >= 2000)
 		return 0;
@@ -1593,6 +1652,9 @@
 {
 	struct sm_state *tmp;
 
+	if (!type_signed(estate_type(sm->state)))
+		return false;
+
 	FOR_EACH_PTR(sm->possible, tmp) {
 		if (!estate_rl(tmp->state))
 			continue;
@@ -1604,7 +1666,7 @@
 	return false;
 }
 
-static bool has_possible_zero_null(struct sm_state *sm)
+static bool has_separate_zero_null(struct sm_state *sm)
 {
 	struct sm_state *tmp;
 	sval_t sval;
@@ -1626,8 +1688,8 @@
 	struct range_list *rl;
 	const char *return_ranges;
 	struct range_list *ret_rl;
+	bool separate_zero;
 	int undo;
-	bool has_zero;
 
 	/* We're going to print the states 3 times */
 	if (get_db_state_count() > 10000 / 3)
@@ -1635,8 +1697,6 @@
 
 	if (!get_implied_rl(expr, &rl) || !rl)
 		return 0;
-	if (is_whole_rl(rl) || is_whole_rl_non_zero(rl))
-		return 0;
 	/* Forget about INT_MAX and larger */
 	if (rl_max(rl).value <= 0)
 		return 0;
@@ -1648,9 +1708,9 @@
 		return 0;
 	if (!has_possible_negative(sm))
 		return 0;
-	has_zero = has_possible_zero_null(sm);
+	separate_zero = has_separate_zero_null(sm);
 
-	if (!assume(compare_expression(expr, has_zero ? '>' : SPECIAL_GTE, zero_expr())))
+	if (!assume(compare_expression(expr, separate_zero ? '>' : SPECIAL_GTE, zero_expr())))
 		return 0;
 
 	return_id++;
@@ -1662,7 +1722,7 @@
 
 	end_assume();
 
-	if (has_zero) {
+	if (separate_zero) {
 		undo = assume(compare_expression(expr, SPECIAL_EQUAL, zero_expr()));
 
 		return_id++;
@@ -1719,7 +1779,7 @@
 		return 0;
 	if (estate_min(state).value == 0 && estate_max(state).value == 0)
 		return 0;
-	if (!has_possible_zero_null(sm))
+	if (!has_separate_zero_null(sm))
 		return 0;
 
 	nr_states = get_db_state_count();
@@ -1761,6 +1821,37 @@
 	return 1;
 }
 
+static bool is_kernel_success_fail(struct sm_state *sm)
+{
+	struct sm_state *tmp;
+	struct range_list *rl;
+	bool has_zero = false;
+	bool has_neg = false;
+
+	if (!type_signed(estate_type(sm->state)))
+		return false;
+
+	FOR_EACH_PTR(sm->possible, tmp) {
+		rl = estate_rl(tmp->state);
+		if (!rl)
+			return false;
+		if (rl_min(rl).value == 0 && rl_max(rl).value == 0) {
+			has_zero = true;
+			continue;
+		}
+		has_neg = true;
+		if (rl_min(rl).value >= -4095 && rl_max(rl).value < 0)
+			continue;
+		if (strcmp(tmp->state->name, "s32min-(-1)") == 0)
+			continue;
+		if (strcmp(tmp->state->name, "s32min-(-1),1-s32max") == 0)
+			continue;
+		return false;
+	} END_FOR_EACH_PTR(tmp);
+
+	return has_zero && has_neg;
+}
+
 static int call_return_state_hooks_split_success_fail(struct expression *expr)
 {
 	struct sm_state *sm;
@@ -1777,7 +1868,7 @@
 		return 0;
 
 	nr_states = get_db_state_count();
-	if (nr_states > 1500)
+	if (nr_states > 2000)
 		return 0;
 
 	sm = get_sm_state_expr(SMATCH_EXTRA, expr);
@@ -1785,18 +1876,13 @@
 		return 0;
 	if (ptr_list_size((struct ptr_list *)sm->possible) == 1)
 		return 0;
+	if (!is_kernel_success_fail(sm))
+		return 0;
 
 	rl = estate_rl(sm->state);
 	if (!rl)
 		return 0;
 
-	if (rl_min(rl).value < -4095 || rl_min(rl).value >= 0)
-		return 0;
-	if (rl_max(rl).value != 0)
-		return 0;
-	if (!has_possible_zero_null(sm))
-		return 0;
-
 	__push_fake_cur_stree();
 
 	final_pass = 0;
@@ -1842,15 +1928,6 @@
 	return 0;
 }
 
-static int is_conditional(struct expression *expr)
-{
-	if (!expr)
-		return 0;
-	if (expr->type == EXPR_CONDITIONAL || expr->type == EXPR_SELECT)
-		return 1;
-	return 0;
-}
-
 static int splitable_function_call(struct expression *expr)
 {
 	struct sm_state *sm;
@@ -1908,7 +1985,6 @@
 	const char *return_ranges;
 	struct sm_state *tmp;
 	int ret = 0;
-	int nr_possible, nr_states;
 	struct state_list *already_handled = NULL;
 
 	if (!sm || !sm->merged)
@@ -1917,12 +1993,6 @@
 	if (too_many_possible(sm))
 		return 0;
 
-	/* bail if it gets too complicated */
-	nr_possible = ptr_list_size((struct ptr_list *)sm->possible);
-	nr_states = get_db_state_count();
-	if (nr_states * nr_possible >= 2000)
-		return 0;
-
 	FOR_EACH_PTR(sm->possible, tmp) {
 		if (tmp->merged)
 			continue;
@@ -1961,6 +2031,10 @@
 	sm = get_sm_state(SMATCH_EXTRA, start_sm->name, start_sm->sym);
 	if (!sm || estate_get_single_value(sm->state, &sval))
 		return 0;
+
+	if (get_db_state_count() * 2 >= 2000)
+		return 0;
+
 	return split_on_bool_sm(sm, expr);
 }
 
@@ -1968,9 +2042,7 @@
 {
 	struct symbol *arg;
 	struct sm_state *sm;
-	sval_t zero = {
-		.type = &ulong_ctype,
-	};
+	int nr_possible;
 
 	/* function must only take one pointer */
 	if (ptr_list_size((struct ptr_list *)cur_func_sym->ctype.base_type->arguments) != 1)
@@ -1987,9 +2059,13 @@
 	if (!sm)
 		return 0;
 
-	if (!rl_has_sval(estate_rl(sm->state), zero))
+	if (!has_separate_zero_null(sm))
 		return 0;
 
+	nr_possible = ptr_list_size((struct ptr_list *)sm->possible);
+	if (get_db_state_count() * nr_possible >= 2000)
+		return 0;
+
 	return split_on_bool_sm(sm, expr);
 }
 
@@ -2038,8 +2114,7 @@
 	    (is_condition(expr) || is_boolean(expr))) {
 		call_return_state_hooks_compare(expr);
 		return;
-	} else if (is_conditional(expr)) {
-		call_return_state_hooks_conditional(expr);
+	} else if (call_return_state_hooks_conditional(expr)) {
 		return;
 	} else if (call_return_state_hooks_split_possible(expr)) {
 		return;
@@ -2492,7 +2567,7 @@
 {
 	char buf[256];
 	char *tmp;
-	bool add_star = false;
+	int star_cnt = 0;
 
 	if (!arg)
 		return NULL;
@@ -2516,18 +2591,23 @@
 		}
 	}
 
-	if (key[0] == '*') {
-		add_star = true;
+	while (key[0] == '*') {
+		star_cnt++;
 		key++;
 	}
 
+	if (arg->type == EXPR_PREOP && arg->op == '&' && star_cnt) {
+		arg = strip_expr(arg->unop);
+		star_cnt--;
+	}
+
 	if (arg->type == EXPR_PREOP && arg->op == '&') {
 		arg = strip_expr(arg->unop);
 		tmp = expr_to_var_sym(arg, sym);
 		if (!tmp)
 			return NULL;
-		snprintf(buf, sizeof(buf), "%s%s.%s",
-			 add_star ? "*" : "", tmp, key + 3);
+		snprintf(buf, sizeof(buf), "%.*s%s.%s",
+			 star_cnt, "**********", tmp, key + 3);
 		return alloc_string(buf);
 	}
 
@@ -2534,7 +2614,7 @@
 	tmp = expr_to_var_sym(arg, sym);
 	if (!tmp)
 		return NULL;
-	snprintf(buf, sizeof(buf), "%s%s%s", add_star ? "*" : "", tmp, key + 1);
+	snprintf(buf, sizeof(buf), "%.*s%s%s", star_cnt, "**********", tmp, key + 1);
 	free_string(tmp);
 	return alloc_string(buf);
 }
@@ -2550,26 +2630,29 @@
 
 const char *state_name_to_param_name(const char *state_name, const char *param_name)
 {
+	int star_cnt = 0;
 	int name_len;
-	static char buf[256];
-	bool add_star = false;
+	char buf[256];
 
 	name_len = strlen(param_name);
 
-	if (state_name[0] == '*') {
-		add_star = true;
+	while (state_name[0] == '*') {
+		star_cnt++;
 		state_name++;
 	}
 
+	/* ten out of ten stars! */
+	if (star_cnt > 10)
+		return NULL;
+
 	if (strcmp(state_name, param_name) == 0) {
-		snprintf(buf, sizeof(buf), "%s$", add_star ? "*" : "");
+		snprintf(buf, sizeof(buf), "%.*s$", star_cnt, "**********");
 		return alloc_sname(buf);
 	}
 
 	if (state_name[name_len] == '-' && /* check for '-' from "->" */
 	    strncmp(state_name, param_name, name_len) == 0) {
-		snprintf(buf, sizeof(buf), "%s$%s",
-			 add_star ? "*" : "", state_name + name_len);
+		snprintf(buf, sizeof(buf), "%.*s$%s", star_cnt, "**********", state_name + name_len);
 		return alloc_sname(buf);
 	}
 	return NULL;
--- old/usr/src/tools/smatch/src/smatch_expressions.c	Tue Jan  7 08:01:55 2020
+++ new/usr/src/tools/smatch/src/smatch_expressions.c	Tue Jan  7 08:01:55 2020
@@ -85,6 +85,8 @@
 
 struct expression *deref_expression(struct expression *expr)
 {
+	if (expr->type == EXPR_BINOP)
+		expr = preop_expression(expr, '(');
 	return preop_expression(expr, '*');
 }
 
@@ -162,6 +164,17 @@
 	return ret;
 }
 
+struct expression *call_expression(struct expression *fn, struct expression_list *args)
+{
+	struct expression *expr;
+
+	expr = alloc_tmp_expression(fn->pos, EXPR_CALL);
+	expr->fn = fn;
+	expr->args = args;
+
+	return expr;
+}
+
 static struct expression *get_expression_from_base_and_str(struct expression *base, const char *addition)
 {
 	struct expression *ret = NULL;
--- old/usr/src/tools/smatch/src/smatch_extra.c	Tue Jan  7 08:01:55 2020
+++ new/usr/src/tools/smatch/src/smatch_extra.c	Tue Jan  7 08:01:55 2020
@@ -194,6 +194,11 @@
 {
 	struct expression *assigned;
 
+	/*
+	 * Imagine we have an assignment: "foo = &addr;" then the other name
+	 * of "*foo" is addr.
+	 */
+
 	if (name[0] != '*')
 		return NULL;
 	if (strcmp(name + 1, sym->ident->name) != 0)
@@ -254,9 +259,10 @@
 
 	/*
 	 * Just prepend the name with a different name/sym and return that.
-	 * For example, if we set "foo->bar = bar;" then we clamp "bar->baz",
-	 * that also clamps "foo->bar->baz".
-	 *
+	 * For example, if we set "foo->bar = bar;" then the other name
+	 * for "bar->baz" is "foo->bar->baz".  Or if we have "foo = bar;" then
+	 * the other name for "bar" is "foo".  A third option is if we have
+	 * "foo = bar;" then another name for "*bar" is "*foo".
 	 */
 
 	FOR_EACH_MY_SM(check_assigned_expr_id, __get_cur_stree(), sm) {
@@ -272,7 +278,17 @@
 found:
 	if (!use_stack && name[tmp->symbol->ident->len] != '-')
 		return NULL;
-	snprintf(buf, sizeof(buf), "%s%s", sm->name, name + tmp->symbol->ident->len);
+
+	if (name[0] == '*' && strcmp(name + 1, tmp->symbol_name->name) == 0)
+		snprintf(buf, sizeof(buf), "*%s", sm->name);
+	else if (name[tmp->symbol->ident->len] == '-' ||
+		 name[tmp->symbol->ident->len] == '.')
+		snprintf(buf, sizeof(buf), "%s%s", sm->name, name + tmp->symbol->ident->len);
+	else if (strcmp(name, tmp->symbol_name->name) == 0)
+		snprintf(buf, sizeof(buf), "%s", sm->name);
+	else
+		return NULL;
+
 	*new_sym = sm->sym;
 	return alloc_string(buf);
 }
@@ -1086,11 +1102,6 @@
 		goto free;
 	}
 
-	if (is_pointer(right) && get_address_rl(right, &rl)) {
-		state = alloc_estate_rl(rl);
-		goto done;
-	}
-
 	if (get_implied_value(right, &sval)) {
 		state = alloc_estate_sval(sval_cast(left_type, sval));
 		goto done;
--- old/usr/src/tools/smatch/src/smatch_extra.h	Tue Jan  7 08:01:55 2020
+++ new/usr/src/tools/smatch/src/smatch_extra.h	Tue Jan  7 08:01:55 2020
@@ -214,6 +214,7 @@
 struct expression *symbol_expression(struct symbol *sym);
 struct expression *string_expression(char *str);
 struct expression *compare_expression(struct expression *left, int op, struct expression *right);
+struct expression *call_expression(struct expression *fn, struct expression_list *args);
 struct expression *unknown_value_expression(struct expression *expr);
 int is_fake_call(struct expression *expr);
 struct expression *gen_expression_from_name_sym(const char *name, struct symbol *sym);
--- old/usr/src/tools/smatch/src/smatch_function_hooks.c	Tue Jan  7 08:01:55 2020
+++ new/usr/src/tools/smatch/src/smatch_function_hooks.c	Tue Jan  7 08:01:55 2020
@@ -1078,6 +1078,7 @@
 	value = argv[5];
 
 	if (db_info->prev_return_id != -1 && type == INTERNAL) {
+		call_ranged_return_hooks(db_info);
 		stree = __pop_fake_cur_stree();
 		if (!db_info->cull)
 			merge_fake_stree(&db_info->stree, stree);
@@ -1108,9 +1109,20 @@
 	ret_range = cast_rl(get_type(db_info->expr), ret_range);
 
 	if (type == INTERNAL) {
+		struct smatch_state *state;
+
 		set_state(-1, "unnull_path", NULL, &true_state);
 		__add_return_comparison(strip_expr(db_info->expr), ret_str);
 		__add_return_to_param_mapping(db_info->expr, ret_str);
+		/*
+		 * We want to store the return values so that we can split the strees
+		 * in smatch_db.c.  This uses set_state() directly because it's not a
+		 * real smatch_extra state.
+		 */
+		snprintf(buf, sizeof(buf), "return %p", db_info->expr);
+		state = alloc_estate_rl(ret_range);
+		set_state(SMATCH_EXTRA, buf, NULL, state);
+		store_return_state(db_info, ret_str, state);
 	}
 
 	FOR_EACH_PTR(db_return_states_list, tmp) {
@@ -1118,13 +1130,6 @@
 			tmp->callback(db_info->expr, param, key, value);
 	} END_FOR_EACH_PTR(tmp);
 
-	/*
-	 * We want to store the return values so that we can split the strees
-	 * in smatch_db.c.  This uses set_state() directly because it's not a
-	 * real smatch_extra state.
-	 */
-	snprintf(buf, sizeof(buf), "return %p", db_info->expr);
-	set_state(SMATCH_EXTRA, buf, NULL, alloc_estate_rl(ret_range));
 
 	return 0;
 }
@@ -1148,6 +1153,7 @@
 	__unnullify_path();
 	sql_select_return_states("return_id, return, type, parameter, key, value",
 			expr, db_return_states_callback, &db_info);
+	call_ranged_return_hooks(&db_info);
 	stree = __pop_fake_cur_stree();
 	if (!db_info.cull)
 		merge_fake_stree(&db_info.stree, stree);
--- old/usr/src/tools/smatch/src/smatch_helper.c	Tue Jan  7 08:01:55 2020
+++ new/usr/src/tools/smatch/src/smatch_helper.c	Tue Jan  7 08:01:55 2020
@@ -299,6 +299,17 @@
 		append(buf, tmp, len);
 		return;
 	}
+	case EXPR_FVALUE: {
+		sval_t sval = {};
+		char tmp[25];
+
+		*complicated = 1;
+		if (!get_value(expr, &sval))
+			return;
+		snprintf(tmp, 25, "%s", sval_to_numstr(sval));
+		append(buf, tmp, len);
+		return;
+	}
 	case EXPR_STRING:
 		append(buf, "\"", len);
 		if (expr->string)
@@ -1066,6 +1077,22 @@
 	return get_param_num_from_sym(sym);
 }
 
+struct symbol *get_param_sym_from_num(int num)
+{
+	struct symbol *sym;
+	int i;
+
+	if (!cur_func_sym)
+		return NULL;
+
+	i = 0;
+	FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, sym) {
+		if (i++ == num)
+			return sym;
+	} END_FOR_EACH_PTR(sym);
+	return NULL;
+}
+
 int ms_since(struct timeval *start)
 {
 	struct timeval end;
--- old/usr/src/tools/smatch/src/smatch_implied.c	Tue Jan  7 08:01:55 2020
+++ new/usr/src/tools/smatch/src/smatch_implied.c	Tue Jan  7 08:01:55 2020
@@ -142,10 +142,13 @@
 		return 0;
 
 	if (rl_intersection(true_rl, false_rl)) {
-		sm_perror("parsing (%s (%s) %s %s)",
-			sm->name, sm->state->name, show_special(comparison), show_rl(rl));
-		sm_msg("true_rl = %s false_rl = %s intersection = %s",
-		       show_rl(true_rl), show_rl(false_rl), show_rl(rl_intersection(true_rl, false_rl)));
+		/*
+		 * Normally these won't intersect, but one exception is when
+		 * we're dealing with mtags.  We have a long list of mtags and
+		 * then negate the list.  Now it's over the limit for mtag list
+		 * length and we squash it down to 4096-ptr_max.  So then it's
+		 * possible for the true and false rl to overlap.
+		 */
 		return 0;
 	}
 
@@ -807,6 +810,31 @@
 		return handle_zero_comparison(expr, implied_true, implied_false);
 }
 
+static int handled_by_parsed_conditions(struct expression *expr,
+					struct stree **implied_true,
+					struct stree **implied_false)
+{
+	struct state_list *true_stack = NULL;
+	struct state_list *false_stack = NULL;
+	struct stree *pre_stree;
+	struct sm_state *sm;
+
+	sm = parsed_condition_implication_hook(expr, &true_stack, &false_stack);
+	if (!sm)
+		return 0;
+
+	pre_stree = clone_stree(__get_cur_stree());
+
+	*implied_true = filter_stack(sm, pre_stree, false_stack, true_stack);
+	*implied_false = filter_stack(sm, pre_stree, true_stack, false_stack);
+
+	free_stree(&pre_stree);
+	free_slist(&true_stack);
+	free_slist(&false_stack);
+
+	return 1;
+}
+
 static int handled_by_stored_conditions(struct expression *expr,
 					struct stree **implied_true,
 					struct stree **implied_false)
@@ -837,14 +865,15 @@
 static struct stree *extra_saved_implied_true;
 static struct stree *extra_saved_implied_false;
 
-static void separate_extra_states(struct stree **implied_true,
-				  struct stree **implied_false)
+static void separate_implication_states(struct stree **implied_true,
+					 struct stree **implied_false,
+					 int owner)
 {
 	struct sm_state *sm;
 
-	/* We process extra states later to preserve the implications. */
+	/* We process these states later to preserve the implications. */
 	FOR_EACH_SM(*implied_true, sm) {
-		if (sm->owner == SMATCH_EXTRA)
+		if (sm->owner == owner)
 			overwrite_sm_state_stree(&extra_saved_implied_true, sm);
 	} END_FOR_EACH_SM(sm);
 	FOR_EACH_SM(extra_saved_implied_true, sm) {
@@ -852,7 +881,7 @@
 	} END_FOR_EACH_SM(sm);
 
 	FOR_EACH_SM(*implied_false, sm) {
-		if (sm->owner == SMATCH_EXTRA)
+		if (sm->owner == owner)
 			overwrite_sm_state_stree(&extra_saved_implied_false, sm);
 	} END_FOR_EACH_SM(sm);
 	FOR_EACH_SM(extra_saved_implied_false, sm) {
@@ -864,11 +893,16 @@
 			  struct stree **implied_true,
 			  struct stree **implied_false)
 {
-	if (handled_by_comparison_hook(expr, implied_true, implied_false))
+	if (handled_by_parsed_conditions(expr, implied_true, implied_false))
 		return;
 
+	if (handled_by_comparison_hook(expr, implied_true, implied_false)) {
+		separate_implication_states(implied_true, implied_false, comparison_id);
+		return;
+	}
+
 	if (handled_by_extra_states(expr, implied_true, implied_false)) {
-		separate_extra_states(implied_true, implied_false);
+		separate_implication_states(implied_true, implied_false, SMATCH_EXTRA);
 		return;
 	}
 
@@ -1138,8 +1172,8 @@
 	add_hook(&save_implications_hook, CONDITION_HOOK);
 	add_hook(&set_implied_states, CONDITION_HOOK);
 	add_hook(&__extra_match_condition, CONDITION_HOOK);
-	add_hook(&set_extra_implied_states, CONDITION_HOOK);
 	add_hook(&__comparison_match_condition, CONDITION_HOOK);
+	add_hook(&set_extra_implied_states, CONDITION_HOOK);
 	add_hook(&__stored_condition, CONDITION_HOOK);
 	add_hook(&match_end_func, END_FUNC_HOOK);
 }
--- old/usr/src/tools/smatch/src/smatch_math.c	Tue Jan  7 08:01:56 2020
+++ new/usr/src/tools/smatch/src/smatch_math.c	Tue Jan  7 08:01:56 2020
@@ -1398,6 +1398,9 @@
 	case EXPR_VALUE:
 		sval = sval_from_val(expr, expr->value);
 		break;
+	case EXPR_FVALUE:
+		sval = sval_from_fval(expr, expr->fvalue);
+		break;
 	case EXPR_PREOP:
 		handle_preop_rl(expr, implied, recurse_cnt, &rl, &sval);
 		break;
--- /dev/null	Tue Jan  7 08:01:56 2020
+++ new/usr/src/tools/smatch/src/smatch_parsed_conditions.c	Tue Jan  7 08:01:56 2020
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2019 Oracle.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
+ */
+
+/*
+ * The problem here is something like this:
+ *
+ * return (blah() || whatever()) ? NULL : some_function();
+ *
+ * When we are parsing this what happens is that we first parse all the
+ * expressions "(blah() || whatever()) ? NULL : some_function();" and then
+ * we parse the return statement.
+ *
+ * When we parse the return statement, we say "Oh, this is a conditional.  Let's
+ * get all the implications for true and false."  But because
+ * "(blah() || whatever())" is a function pointer, that means there aren't any
+ * implications.
+ *
+ * So what this module does is it ties the implications to the expression
+ * pointer so that we can retreive them easily.  It's similar to Smatch stored
+ * implications but it doesn't save condition, it saves the pointer.
+ *
+ * We ignore pre loop conditions which Smatch parses twice.
+ *
+ */
+
+#include "smatch.h"
+#include "smatch_slist.h"
+
+static int my_id;
+
+STATE(true_path);
+STATE(false_path);
+
+void record_condition(struct expression *expr)
+{
+	char name[32];
+	sval_t val;
+
+	if (get_value(expr, &val))
+		return;
+
+	if (__in_pre_condition)
+		return;
+
+	snprintf(name, sizeof(name), "condition %p", expr);
+	set_true_false_states(my_id, name, NULL, &true_path, &false_path);
+}
+
+void register_parsed_conditions(int id)
+{
+	my_id = id;
+	add_hook(&record_condition, CONDITION_HOOK);
+}
+
+static void filter_by_sm(struct sm_state *sm,
+		       struct state_list **true_stack,
+		       struct state_list **false_stack)
+{
+	if (!sm)
+		return;
+
+	if (sm->state == &true_path)
+		add_ptr_list(true_stack, sm);
+	else if (sm->state == &false_path)
+		add_ptr_list(false_stack, sm);
+
+	if (sm->merged) {
+		filter_by_sm(sm->left, true_stack, false_stack);
+		filter_by_sm(sm->right, true_stack, false_stack);
+	}
+}
+
+struct sm_state *parsed_condition_implication_hook(struct expression *expr,
+				struct state_list **true_stack,
+				struct state_list **false_stack)
+{
+	struct state_list *tmp_true = NULL;
+	struct state_list *tmp_false = NULL;
+	struct sm_state *sm, *tmp;
+	char name[32];
+
+	snprintf(name, sizeof(name), "condition %p", expr);
+
+	sm = get_sm_state(my_id, name, NULL);
+	if (!sm)
+		return NULL;
+	if (!sm->merged)
+		return NULL;
+
+	filter_by_sm(sm, &tmp_true, &tmp_false);
+	if (!tmp_true && !tmp_false)
+		return NULL;
+
+	FOR_EACH_PTR(tmp_true, tmp) {
+		add_ptr_list(true_stack, tmp);
+	} END_FOR_EACH_PTR(tmp);
+
+	FOR_EACH_PTR(tmp_false, tmp) {
+		add_ptr_list(false_stack, tmp);
+	} END_FOR_EACH_PTR(tmp);
+
+	free_slist(&tmp_true);
+	free_slist(&tmp_false);
+
+	return sm;
+}
+
--- old/usr/src/tools/smatch/src/smatch_ranges.c	Tue Jan  7 08:01:56 2020
+++ new/usr/src/tools/smatch/src/smatch_ranges.c	Tue Jan  7 08:01:56 2020
@@ -134,6 +134,9 @@
 	unsigned long long mask;
 	int bits = type_bits(type);
 
+	if (type_is_fp(min.type) && !type_is_fp(type))
+		return 0;
+
 	if (bits >= type_bits(min.type))
 		return 0;
 
@@ -432,6 +435,13 @@
 	const char *start = c;
 	sval_t ret;
 
+	if (type == &float_ctype)
+		return sval_type_fval(type, strtof(start, (char **)endp));
+	else if (type == &double_ctype)
+		return sval_type_fval(type, strtod(start, (char **)endp));
+	else if (type == &ldouble_ctype)
+		return sval_type_fval(type, strtold(start, (char **)endp));
+
 	if (!strncmp(start, "max", 3)) {
 		ret = sval_type_max(type);
 		c += 3;
@@ -564,7 +574,7 @@
 				break;
 		}
 		if (*c != '-') {
-			sm_msg("debug XXX: trouble parsing %s c = %s", str, c);
+			sm_debug("XXX: trouble parsing %s c = %s", str, c);
 			break;
 		}
 		c++;
--- old/usr/src/tools/smatch/src/smatch_scripts/build_generic_data.sh	Tue Jan  7 08:01:56 2020
+++ new/usr/src/tools/smatch/src/smatch_scripts/build_generic_data.sh	Tue Jan  7 08:01:56 2020
@@ -59,7 +59,7 @@
 	KERNEL_CROSS_COMPILE="CROSS_COMPILE=$CROSS_COMPILE"
 fi
 
-make $KERNEL_ARCH $KERNEL_CROSS_COMPILE -j${NR_CPU} CHECK="$BIN_DIR/smatch --call-tree --info --param-mapper --spammy --file-output" $TARGET
+make $KERNEL_ARCH $KERNEL_CROSS_COMPILE -j${NR_CPU} CHECK="$BIN_DIR/smatch --call-tree --info --spammy --file-output" $TARGET
 
 find -name \*.c.smatch -exec cat \{\} \; -exec rm \{\} \; > smatch_warns.txt
 
--- old/usr/src/tools/smatch/src/smatch_scripts/build_kernel_data.sh	Tue Jan  7 08:01:56 2020
+++ new/usr/src/tools/smatch/src/smatch_scripts/build_kernel_data.sh	Tue Jan  7 08:01:56 2020
@@ -36,7 +36,7 @@
 fi
 
 BUILD_STATUS=0
-$SCRIPT_DIR/test_kernel.sh --call-tree --info --param-mapper --spammy --data=$DATA_DIR || BUILD_STATUS=$?
+$SCRIPT_DIR/test_kernel.sh --call-tree --info --spammy --data=$DATA_DIR || BUILD_STATUS=$?
 
 for i in $SCRIPT_DIR/gen_* ; do
 	$i smatch_warns.txt -p=kernel
--- old/usr/src/tools/smatch/src/smatch_scripts/gen_rosenberg_funcs.sh	Tue Jan  7 08:01:56 2020
+++ new/usr/src/tools/smatch/src/smatch_scripts/gen_rosenberg_funcs.sh	Tue Jan  7 08:01:56 2020
@@ -25,6 +25,7 @@
 ${bin_dir}/trace_params.pl $file nla_put 3 >> $tmp
 ${bin_dir}/trace_params.pl $file snd_timer_user_append_to_tqueue 1 >> $tmp
 ${bin_dir}/trace_params.pl $file __send_signal 1 >> $tmp
+${bin_dir}/trace_params.pl $file usb_bulk_msg 2 >> $tmp
 
 cat $tmp | sort -u > $tmp2
 mv $tmp2 $tmp
--- old/usr/src/tools/smatch/src/smatch_scripts/kpatch.sh	Tue Jan  7 08:01:56 2020
+++ new/usr/src/tools/smatch/src/smatch_scripts/kpatch.sh	Tue Jan  7 08:01:56 2020
@@ -99,8 +99,10 @@
 
 git commit $AMEND -F $MSG_FILE
 
-to_addr=$(./scripts/get_maintainer.pl -f --noroles --norolestats $fullname | head -n 1)
-cc_addr=$(./scripts/get_maintainer.pl -f --noroles --norolestats $fullname | tail -n +2 | \
+git format-patch HEAD^ --stdout >> $MSG_FILE
+
+to_addr=$(./scripts/get_maintainer.pl --noroles --norolestats $MSG_FILE | head -n 1)
+cc_addr=$(./scripts/get_maintainer.pl --noroles --norolestats $MSG_FILE | tail -n +2 | \
     perl -ne 's/\n$/, /; print')
 cc_addr="$cc_addr, kernel-janitors@vger.kernel.org"
 
@@ -118,3 +120,4 @@
 read unused
 
 mutt -H $MAIL_FILE
+rm -f $MSG_FILE
--- old/usr/src/tools/smatch/src/smatch_states.c	Tue Jan  7 08:01:56 2020
+++ new/usr/src/tools/smatch/src/smatch_states.c	Tue Jan  7 08:01:56 2020
@@ -1095,6 +1095,15 @@
 	struct stree *implied_stree;
 
 	stree = pop_stree(&switch_stack);
+	if (!stree) {
+		/*
+		 * If the cur_stree was NULL before the start of the switch
+		 * statement then we don't want to unnullify it.
+		 *
+		 */
+		push_stree(&switch_stack, stree);
+		return;
+	}
 	implied_stree = __implied_case_stree(switch_expr, case_rl, &remaining_cases, &stree);
 	merge_stree(&cur_stree, implied_stree);
 	free_stree(&implied_stree);
--- old/usr/src/tools/smatch/src/smatch_sval.c	Tue Jan  7 08:01:57 2020
+++ new/usr/src/tools/smatch/src/smatch_sval.c	Tue Jan  7 08:01:57 2020
@@ -74,6 +74,15 @@
 	return ret;
 }
 
+sval_t sval_type_fval(struct symbol *type, long double fval)
+{
+	sval_t ret;
+
+	ret.type = &ldouble_ctype;
+	ret.ldvalue = fval;
+	return sval_cast(type, ret);
+}
+
 sval_t sval_from_val(struct expression *expr, long long val)
 {
 	sval_t ret;
@@ -85,6 +94,17 @@
 	return ret;
 }
 
+sval_t sval_from_fval(struct expression *expr, long double fval)
+{
+	sval_t ret;
+
+	ret.type = &ldouble_ctype;
+	ret.ldvalue = fval;
+	ret = sval_cast(get_type(expr), ret);
+
+	return ret;
+}
+
 int sval_is_ptr(sval_t sval)
 {
 	if (!sval.type)
@@ -92,6 +112,11 @@
 	return (sval.type->type == SYM_PTR || sval.type->type == SYM_ARRAY);
 }
 
+bool sval_is_fp(sval_t sval)
+{
+	return type_is_fp(sval.type);
+}
+
 int sval_unsigned(sval_t sval)
 {
 	if (is_ptr_type(sval.type))
@@ -134,10 +159,25 @@
 	return !sval_is_negative(sval);
 }
 
+static bool fp_is_min(sval_t sval)
+{
+	if (sval.type == &float_ctype)
+		return sval.fvalue == -FLT_MAX;
+	if (sval.type == &double_ctype)
+		return sval.dvalue == -DBL_MAX;
+	if (sval.type == &ldouble_ctype)
+		return sval.ldvalue == -LDBL_MAX;
+	sm_perror("%s: bad type: '%s'", __func__, type_to_str(sval.type));
+	return false;
+}
+
 int sval_is_min(sval_t sval)
 {
 	sval_t min = sval_type_min(sval.type);
 
+	if (sval_is_fp(sval))
+		return fp_is_min(sval);
+
 	if (sval_unsigned(sval)) {
 		if (sval.uvalue == 0)
 			return 1;
@@ -147,10 +187,25 @@
 	return (sval.value <= min.value);
 }
 
+static bool fp_is_max(sval_t sval)
+{
+	if (sval.type == &float_ctype)
+		return sval.fvalue == FLT_MAX;
+	if (sval.type == &double_ctype)
+		return sval.dvalue == DBL_MAX;
+	if (sval.type == &ldouble_ctype)
+		return sval.ldvalue == LDBL_MAX;
+	sm_perror("%s: bad type: '%s'", __func__, type_to_str(sval.type));
+	return false;
+}
+
 int sval_is_max(sval_t sval)
 {
 	sval_t max = sval_type_max(sval.type);
 
+	if (sval_is_fp(sval))
+		return fp_is_max(sval);
+
 	if (sval_unsigned(sval))
 		return (sval.uvalue >= max.value);
 	return (sval.value >= max.value);
@@ -160,6 +215,10 @@
 {
 	if (sval_is_min(sval))
 		return 1;
+
+	if (sval_is_fp(sval))
+		return 0;
+
 	if (sval_signed(sval) && sval.value == SHRT_MIN)
 		return 1;
 	if (sval_signed(sval) && sval.value == INT_MIN)
@@ -173,6 +232,10 @@
 {
 	if (sval_is_max(sval))
 		return 1;
+
+	if (sval_is_fp(sval))
+		return 0;
+
 	if (sval.uvalue == SHRT_MAX)
 		return 1;
 	if (sval.uvalue == INT_MAX)
@@ -193,6 +256,9 @@
 
 int sval_is_negative_min(sval_t sval)
 {
+	if (sval_is_fp(sval))
+		return 0;
+
 	if (!sval_is_negative(sval))
 		return 0;
 	return sval_is_min(sval);
@@ -254,6 +320,13 @@
 
 int sval_fits(struct symbol *type, sval_t sval)
 {
+	/* everything fits into floating point */
+	if (type_is_fp(type))
+		return 1;
+	/* floating points don't fit into int */
+	if (type_is_fp(sval.type))
+		return 0;
+
 	if (sval_too_low(type, sval))
 		return 0;
 	if (sval_too_high(type, sval))
@@ -261,6 +334,71 @@
 	return 1;
 }
 
+static sval_t cast_to_fp(struct symbol *type, sval_t sval)
+{
+	sval_t ret = {};
+
+	ret.type = type;
+	if (type == &float_ctype) {
+		if (!sval_is_fp(sval)) {
+			if (sval_unsigned(sval))
+				ret.fvalue = sval.uvalue;
+			else
+				ret.fvalue = sval.value;
+		} else if (sval.type == &float_ctype)
+			ret.fvalue = sval.fvalue;
+		else if (sval.type == &double_ctype)
+			ret.fvalue = sval.dvalue;
+		else
+			ret.fvalue = sval.ldvalue;
+	} else if (type == &double_ctype) {
+		if (!sval_is_fp(sval)) {
+			if (sval_unsigned(sval))
+				ret.dvalue = sval.uvalue;
+			else
+				ret.dvalue = sval.value;
+		} else if (sval.type == &float_ctype)
+			ret.dvalue = sval.fvalue;
+		else if (sval.type == &double_ctype)
+			ret.dvalue = sval.dvalue;
+		else
+			ret.dvalue = sval.ldvalue;
+	} else if (type == &ldouble_ctype) {
+		if (!sval_is_fp(sval)) {
+			if (sval_unsigned(sval))
+				ret.ldvalue = (long double)sval.uvalue;
+			else
+				ret.ldvalue = (long double)sval.value;
+		} else if (sval.type == &float_ctype)
+			ret.ldvalue = sval.fvalue;
+		else if (sval.type == &double_ctype)
+			ret.ldvalue = sval.dvalue;
+		else
+			ret.ldvalue = sval.ldvalue;
+	} else {
+		sm_perror("%s: bad type: %s", __func__, type_to_str(type));
+	}
+
+	return ret;
+}
+
+static sval_t cast_from_fp(struct symbol *type, sval_t sval)
+{
+	sval_t ret = {};
+
+	ret.type = &llong_ctype;
+	if (sval.type == &float_ctype)
+		ret.value = sval.fvalue;
+	else if (sval.type == &double_ctype)
+		ret.value = sval.dvalue;
+	else if (sval.type == &ldouble_ctype)
+		ret.value = sval.ldvalue;
+	else
+		sm_perror("%s: bad type: %s", __func__, type_to_str(type));
+
+	return sval_cast(type, ret);
+}
+
 sval_t sval_cast(struct symbol *type, sval_t sval)
 {
 	sval_t ret;
@@ -268,6 +406,11 @@
 	if (!type)
 		type = &int_ctype;
 
+	if (type_is_fp(type))
+		return cast_to_fp(type, sval);
+	if (type_is_fp(sval.type))
+		return cast_from_fp(type, sval);
+
 	ret.type = type;
 	switch (sval_bits(ret)) {
 	case 1:
@@ -626,10 +769,29 @@
 	return fls_mask(sval.uvalue);
 }
 
+static char *fp_to_str(sval_t sval)
+{
+	char buf[32];
+
+	if (sval.type == &float_ctype)
+		snprintf(buf, sizeof(buf), "%f", sval.fvalue);
+	else if (sval.type == &double_ctype)
+		snprintf(buf, sizeof(buf), "%e", sval.dvalue);
+	else if (sval.type == &ldouble_ctype) {
+		snprintf(buf, sizeof(buf), "%Lf", sval.ldvalue);
+	} else
+		snprintf(buf, sizeof(buf), "nan");
+
+	return alloc_sname(buf);
+}
+
 const char *sval_to_str(sval_t sval)
 {
-	char buf[30];
+	char buf[32];
 
+	if (sval_is_fp(sval))
+		return fp_to_str(sval);
+
 	if (sval_is_ptr(sval) && sval.value == valid_ptr_max)
 		return "ptr_max";
 	if (sval_unsigned(sval) && sval.value == ULLONG_MAX)
@@ -671,7 +833,7 @@
 	    !is_ptr_type(sval.type))
 		return sval_to_str(sval);
 
-	if (sval.uvalue >= -4905ULL) {
+	if (!sval_is_fp(sval) && sval.uvalue >= -4905ULL) {
 		snprintf(buf, sizeof(buf), "(%lld)", sval.value);
 		return alloc_sname(buf);
 	}
@@ -683,6 +845,9 @@
 {
 	char buf[30];
 
+	if (type_is_fp(sval.type))
+		return fp_to_str(sval);
+
 	if (sval_unsigned(sval))
 		snprintf(buf, sizeof(buf), "%llu", sval.value);
 	else if (sval.value < 0)
--- old/usr/src/tools/smatch/src/smatch_type.c	Tue Jan  7 08:01:57 2020
+++ new/usr/src/tools/smatch/src/smatch_type.c	Tue Jan  7 08:01:57 2020
@@ -77,6 +77,24 @@
 	if (!right)
 		return NULL;
 
+	if (type_is_fp(left)) {
+		if (type_is_fp(right)) {
+			if (type_bits(left) > type_bits(right))
+				return left;
+			return right;
+		}
+		return left;
+	}
+
+	if (type_is_fp(right)) {
+		if (type_is_fp(left)) {
+			if (type_bits(right) > type_bits(left))
+				return right;
+			return left;
+		}
+		return right;
+	}
+
 	if (expr->op == '-' &&
 	    (is_ptr_type(left) && is_ptr_type(right)))
 		return ssize_t_ctype;
@@ -143,10 +161,30 @@
 	return get_real_base_type(sym);
 }
 
+static struct symbol *handle__builtin_choose_expr(struct expression *expr)
+{
+	struct expression *const_expr, *expr1, *expr2;
+	sval_t sval;
+
+	const_expr = get_argument_from_call_expr(expr->args, 0);
+	expr1 = get_argument_from_call_expr(expr->args, 1);
+	expr2 = get_argument_from_call_expr(expr->args, 2);
+
+	if (!get_value(const_expr, &sval) || !expr1 || !expr2)
+		return NULL;
+	if (sval.value)
+		return get_type(expr1);
+	else
+		return get_type(expr2);
+}
+
 static struct symbol *get_return_type(struct expression *expr)
 {
 	struct symbol *tmp;
 
+	if (sym_name_is("__builtin_choose_expr", expr->fn))
+		return handle__builtin_choose_expr(expr);
+
 	tmp = get_type(expr->fn);
 	if (!tmp)
 		return NULL;
@@ -403,10 +441,27 @@
 	return 0;
 }
 
+static sval_t fp_max(struct symbol *type)
+{
+	sval_t ret = { .type = type };
+
+	if (type == &float_ctype)
+		ret.fvalue = FLT_MAX;
+	else if (type == &double_ctype)
+		ret.dvalue = DBL_MAX;
+	else
+		ret.ldvalue = LDBL_MAX;
+
+	return ret;
+}
+
 sval_t sval_type_max(struct symbol *base_type)
 {
 	sval_t ret;
 
+	if (type_is_fp(base_type))
+		return fp_max(base_type);
+
 	if (!base_type || !type_bits(base_type))
 		base_type = &llong_ctype;
 	ret.type = base_type;
@@ -415,10 +470,27 @@
 	return ret;
 }
 
+static sval_t fp_min(struct symbol *type)
+{
+	sval_t ret = { .type = type };
+
+	if (type == &float_ctype)
+		ret.fvalue = -FLT_MAX;
+	else if (type == &double_ctype)
+		ret.dvalue = -DBL_MAX;
+	else
+		ret.ldvalue = -LDBL_MAX;
+
+	return ret;
+}
+
 sval_t sval_type_min(struct symbol *base_type)
 {
 	sval_t ret;
 
+	if (type_is_fp(base_type))
+		return fp_min(base_type);
+
 	if (!base_type || !type_bits(base_type))
 		base_type = &llong_ctype;
 	ret.type = base_type;
--- /dev/null	Tue Jan  7 08:01:57 2020
+++ new/usr/src/tools/smatch/src/validation/sm_float1.c	Tue Jan  7 08:01:57 2020
@@ -0,0 +1,20 @@
+#include "check_debug.h"
+
+float frob(float x)
+{
+	return x;
+}
+
+int main(int argc, char *argv[])
+{
+	__smatch_implied((long long)frob(3.14));
+}
+
+/*
+ * check-name: smatch floating point #1
+ * check-command: smatch -I.. sm_float1.c
+ *
+ * check-output-start
+sm_float1.c:10 main() implied: frob(3.140000e+00) = '3'
+ * check-output-end
+ */
