Merge branch '2021-10-06-assorted-improvements'
[platform/kernel/u-boot.git] / scripts / checkpatch.pl
index bc51fff..5696d3a 100755 (executable)
@@ -23,6 +23,9 @@ my $V = '0.32';
 use Getopt::Long qw(:config no_auto_abbrev);
 
 my $quiet = 0;
+my $verbose = 0;
+my %verbose_messages = ();
+my %verbose_emitted = ();
 my $tree = 1;
 my $chk_signoff = 1;
 my $chk_patch = 1;
@@ -43,6 +46,8 @@ my $list_types = 0;
 my $fix = 0;
 my $fix_inplace = 0;
 my $root;
+my $gitroot = $ENV{'GIT_DIR'};
+$gitroot = ".git" if !defined($gitroot);
 my %debug;
 my %camelcase = ();
 my %use_type = ();
@@ -59,9 +64,15 @@ my $spelling_file = "$D/spelling.txt";
 my $codespell = 0;
 my $codespellfile = "/usr/share/codespell/dictionary.txt";
 my $conststructsfile = "$D/const_structs.checkpatch";
-my $typedefsfile = "";
+my $u_boot = 0;
+my $docsfile = "$D/../doc/develop/checkpatch.rst";
+my $typedefsfile;
 my $color = "auto";
-my $allow_c99_comments = 1;
+my $allow_c99_comments = 1; # Can be overridden by --ignore C99_COMMENT_TOLERANCE
+# git output parsing needs US English output, so first set backtick child process LANGUAGE
+my $git_command ='export LANGUAGE=en_US.UTF-8; git';
+my $tabsize = 8;
+my ${CONFIG_} = "CONFIG_";
 
 sub help {
        my ($exitcode) = @_;
@@ -72,6 +83,7 @@ Version: $V
 
 Options:
   -q, --quiet                quiet
+  -v, --verbose              verbose mode
   --no-tree                  run without a kernel tree
   --no-signoff               do not check for 'Signed-off-by' line
   --patch                    treat FILE as patchfile (default)
@@ -98,6 +110,7 @@ Options:
                              if exceeded, warn on patches
                              requires --strict for use with --file
   --min-conf-desc-length=n   set the min description length, if shorter, warn
+  --tab-size=n               set the number of spaces for tab (default $tabsize)
   --root=PATH                PATH to the kernel tree root
   --no-summary               suppress the per-file summary
   --mailback                 only produce a report in case of warnings/errors
@@ -123,6 +136,9 @@ Options:
   --typedefsfile             Read additional types from this file
   --color[=WHEN]             Use colors 'always', 'never', or only when output
                              is a terminal ('auto'). Default is 'auto'.
+  --u-boot                   Run additional checks for U-Boot
+  --kconfig-prefix=WORD      use WORD as a prefix for Kconfig symbols (default
+                             ${CONFIG_})
   -h, --help, --version      display this help and exit
 
 When FILE is - read standard input.
@@ -149,15 +165,51 @@ sub list_types {
        my $text = <$script>;
        close($script);
 
-       my @types = ();
+       my %types = ();
        # Also catch when type or level is passed through a variable
-       for ($text =~ /(?:(?:\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) {
-               push (@types, $_);
+       while ($text =~ /(?:(\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) {
+               if (defined($1)) {
+                       if (exists($types{$2})) {
+                               $types{$2} .= ",$1" if ($types{$2} ne $1);
+                       } else {
+                               $types{$2} = $1;
+                       }
+               } else {
+                       $types{$2} = "UNDETERMINED";
+               }
        }
-       @types = sort(uniq(@types));
+
        print("#\tMessage type\n\n");
-       foreach my $type (@types) {
+       if ($color) {
+               print(" ( Color coding: ");
+               print(RED . "ERROR" . RESET);
+               print(" | ");
+               print(YELLOW . "WARNING" . RESET);
+               print(" | ");
+               print(GREEN . "CHECK" . RESET);
+               print(" | ");
+               print("Multiple levels / Undetermined");
+               print(" )\n\n");
+       }
+
+       foreach my $type (sort keys %types) {
+               my $orig_type = $type;
+               if ($color) {
+                       my $level = $types{$type};
+                       if ($level eq "ERROR") {
+                               $type = RED . $type . RESET;
+                       } elsif ($level eq "WARN") {
+                               $type = YELLOW . $type . RESET;
+                       } elsif ($level eq "CHK") {
+                               $type = GREEN . $type . RESET;
+                       }
+               }
                print(++$count . "\t" . $type . "\n");
+               if ($verbose && exists($verbose_messages{$orig_type})) {
+                       my $message = $verbose_messages{$orig_type};
+                       $message =~ s/\n/\n\t/g;
+                       print("\t" . $message . "\n\n");
+               }
        }
 
        exit($exitcode);
@@ -189,6 +241,46 @@ if (-f $conf) {
        unshift(@ARGV, @conf_args) if @conf_args;
 }
 
+sub load_docs {
+       open(my $docs, '<', "$docsfile")
+           or warn "$P: Can't read the documentation file $docsfile $!\n";
+
+       my $type = '';
+       my $desc = '';
+       my $in_desc = 0;
+
+       while (<$docs>) {
+               chomp;
+               my $line = $_;
+               $line =~ s/\s+$//;
+
+               if ($line =~ /^\s*\*\*(.+)\*\*$/) {
+                       if ($desc ne '') {
+                               $verbose_messages{$type} = trim($desc);
+                       }
+                       $type = $1;
+                       $desc = '';
+                       $in_desc = 1;
+               } elsif ($in_desc) {
+                       if ($line =~ /^(?:\s{4,}|$)/) {
+                               $line =~ s/^\s{4}//;
+                               $desc .= $line;
+                               $desc .= "\n";
+                       } else {
+                               $verbose_messages{$type} = trim($desc);
+                               $type = '';
+                               $desc = '';
+                               $in_desc = 0;
+                       }
+               }
+       }
+
+       if ($desc ne '') {
+               $verbose_messages{$type} = trim($desc);
+       }
+       close($docs);
+}
+
 # Perl's Getopt::Long allows options to take optional arguments after a space.
 # Prevent --color by itself from consuming other arguments
 foreach (@ARGV) {
@@ -199,6 +291,7 @@ foreach (@ARGV) {
 
 GetOptions(
        'q|quiet+'      => \$quiet,
+       'v|verbose!'    => \$verbose,
        'tree!'         => \$tree,
        'signoff!'      => \$chk_signoff,
        'patch!'        => \$chk_patch,
@@ -215,6 +308,7 @@ GetOptions(
        'list-types!'   => \$list_types,
        'max-line-length=i' => \$max_line_length,
        'min-conf-desc-length=i' => \$min_conf_desc_length,
+       'tab-size=i'    => \$tabsize,
        'root=s'        => \$root,
        'summary!'      => \$summary,
        'mailback!'     => \$mailback,
@@ -227,15 +321,33 @@ GetOptions(
        'codespell!'    => \$codespell,
        'codespellfile=s'       => \$codespellfile,
        'typedefsfile=s'        => \$typedefsfile,
+       'u-boot'        => \$u_boot,
        'color=s'       => \$color,
        'no-color'      => \$color,     #keep old behaviors of -nocolor
        'nocolor'       => \$color,     #keep old behaviors of -nocolor
+       'kconfig-prefix=s'      => \${CONFIG_},
        'h|help'        => \$help,
        'version'       => \$help
 ) or help(1);
 
 help(0) if ($help);
 
+die "$P: --git cannot be used with --file or --fix\n" if ($git && ($file || $fix));
+die "$P: --verbose cannot be used with --terse\n" if ($verbose && $terse);
+
+if ($color =~ /^[01]$/) {
+       $color = !$color;
+} elsif ($color =~ /^always$/i) {
+       $color = 1;
+} elsif ($color =~ /^never$/i) {
+       $color = 0;
+} elsif ($color =~ /^auto$/i) {
+       $color = (-t STDOUT);
+} else {
+       die "$P: Invalid color mode: $color\n";
+}
+
+load_docs() if ($verbose);
 list_types(0) if ($list_types);
 
 $fix = 1 if ($fix_inplace);
@@ -255,17 +367,8 @@ if ($#ARGV < 0) {
        push(@ARGV, '-');
 }
 
-if ($color =~ /^[01]$/) {
-       $color = !$color;
-} elsif ($color =~ /^always$/i) {
-       $color = 1;
-} elsif ($color =~ /^never$/i) {
-       $color = 0;
-} elsif ($color =~ /^auto$/i) {
-       $color = (-t STDOUT);
-} else {
-       die "Invalid color mode: $color\n";
-}
+# skip TAB size 1 to avoid additional checks on $tabsize - 1
+die "$P: Invalid TAB size: $tabsize\n" if ($tabsize < 2);
 
 sub hash_save_array_words {
        my ($hashRef, $arrayRef) = @_;
@@ -366,6 +469,7 @@ our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeIni
 # We need \b after 'init' otherwise 'initconst' will cause a false positive in a check
 our $Attribute = qr{
                        const|
+                       volatile|
                        __percpu|
                        __nocast|
                        __safe|
@@ -470,8 +574,19 @@ our $logFunctions = qr{(?x:
        seq_vprintf|seq_printf|seq_puts
 )};
 
+our $allocFunctions = qr{(?x:
+       (?:(?:devm_)?
+               (?:kv|k|v)[czm]alloc(?:_array)?(?:_node)? |
+               kstrdup(?:_const)? |
+               kmemdup(?:_nul)?) |
+       (?:\w+)?alloc_skb(?:_ip_align)? |
+                               # dev_alloc_skb/netdev_alloc_skb, et al
+       dma_alloc_coherent
+)};
+
 our $signature_tags = qr{(?xi:
        Signed-off-by:|
+       Co-developed-by:|
        Acked-by:|
        Tested-by:|
        Reviewed-by:|
@@ -481,6 +596,88 @@ our $signature_tags = qr{(?xi:
        Cc:
 )};
 
+our $tracing_logging_tags = qr{(?xi:
+       [=-]*> |
+       <[=-]* |
+       \[ |
+       \] |
+       start |
+       called |
+       entered |
+       entry |
+       enter |
+       in |
+       inside |
+       here |
+       begin |
+       exit |
+       end |
+       done |
+       leave |
+       completed |
+       out |
+       return |
+       [\.\!:\s]*
+)};
+
+sub edit_distance_min {
+       my (@arr) = @_;
+       my $len = scalar @arr;
+       if ((scalar @arr) < 1) {
+               # if underflow, return
+               return;
+       }
+       my $min = $arr[0];
+       for my $i (0 .. ($len-1)) {
+               if ($arr[$i] < $min) {
+                       $min = $arr[$i];
+               }
+       }
+       return $min;
+}
+
+sub get_edit_distance {
+       my ($str1, $str2) = @_;
+       $str1 = lc($str1);
+       $str2 = lc($str2);
+       $str1 =~ s/-//g;
+       $str2 =~ s/-//g;
+       my $len1 = length($str1);
+       my $len2 = length($str2);
+       # two dimensional array storing minimum edit distance
+       my @distance;
+       for my $i (0 .. $len1) {
+               for my $j (0 .. $len2) {
+                       if ($i == 0) {
+                               $distance[$i][$j] = $j;
+                       } elsif ($j == 0) {
+                               $distance[$i][$j] = $i;
+                       } elsif (substr($str1, $i-1, 1) eq substr($str2, $j-1, 1)) {
+                               $distance[$i][$j] = $distance[$i - 1][$j - 1];
+                       } else {
+                               my $dist1 = $distance[$i][$j - 1]; #insert distance
+                               my $dist2 = $distance[$i - 1][$j]; # remove
+                               my $dist3 = $distance[$i - 1][$j - 1]; #replace
+                               $distance[$i][$j] = 1 + edit_distance_min($dist1, $dist2, $dist3);
+                       }
+               }
+       }
+       return $distance[$len1][$len2];
+}
+
+sub find_standard_signature {
+       my ($sign_off) = @_;
+       my @standard_signature_tags = (
+               'Signed-off-by:', 'Co-developed-by:', 'Acked-by:', 'Tested-by:',
+               'Reviewed-by:', 'Reported-by:', 'Suggested-by:'
+       );
+       foreach my $signature (@standard_signature_tags) {
+               return $signature if (get_edit_distance($sign_off, $signature) <= 2);
+       }
+
+       return "";
+}
+
 our @typeListMisordered = (
        qr{char\s+(?:un)?signed},
        qr{int\s+(?:(?:un)?signed\s+)?short\s},
@@ -569,6 +766,8 @@ our @mode_permission_funcs = (
        ["__ATTR", 2],
 );
 
+my $word_pattern = '\b[A-Z]?[a-z]{2,}\b';
+
 #Create a search pattern for all these functions to speed up a loop below
 our $mode_perms_search = "";
 foreach my $entry (@mode_permission_funcs) {
@@ -577,6 +776,27 @@ foreach my $entry (@mode_permission_funcs) {
 }
 $mode_perms_search = "(?:${mode_perms_search})";
 
+our %deprecated_apis = (
+       "synchronize_rcu_bh"                    => "synchronize_rcu",
+       "synchronize_rcu_bh_expedited"          => "synchronize_rcu_expedited",
+       "call_rcu_bh"                           => "call_rcu",
+       "rcu_barrier_bh"                        => "rcu_barrier",
+       "synchronize_sched"                     => "synchronize_rcu",
+       "synchronize_sched_expedited"           => "synchronize_rcu_expedited",
+       "call_rcu_sched"                        => "call_rcu",
+       "rcu_barrier_sched"                     => "rcu_barrier",
+       "get_state_synchronize_sched"           => "get_state_synchronize_rcu",
+       "cond_synchronize_sched"                => "cond_synchronize_rcu",
+);
+
+#Create a search pattern for all these strings to speed up a loop below
+our $deprecated_apis_search = "";
+foreach my $entry (keys %deprecated_apis) {
+       $deprecated_apis_search .= '|' if ($deprecated_apis_search ne "");
+       $deprecated_apis_search .= $entry;
+}
+$deprecated_apis_search = "(?:${deprecated_apis_search})";
+
 our $mode_perms_world_writable = qr{
        S_IWUGO         |
        S_IWOTH         |
@@ -716,7 +936,7 @@ sub read_words {
                                next;
                        }
 
-                       $$wordsRef .= '|' if ($$wordsRef ne "");
+                       $$wordsRef .= '|' if (defined $$wordsRef);
                        $$wordsRef .= $line;
                }
                close($file);
@@ -726,16 +946,18 @@ sub read_words {
        return 0;
 }
 
-my $const_structs = "";
-read_words(\$const_structs, $conststructsfile)
-    or warn "No structs that should be const will be found - file '$conststructsfile': $!\n";
+my $const_structs;
+if (show_type("CONST_STRUCT")) {
+       read_words(\$const_structs, $conststructsfile)
+           or warn "No structs that should be const will be found - file '$conststructsfile': $!\n";
+}
 
-my $typeOtherTypedefs = "";
-if (length($typedefsfile)) {
+if (defined($typedefsfile)) {
+       my $typeOtherTypedefs;
        read_words(\$typeOtherTypedefs, $typedefsfile)
            or warn "No additional types will be considered - file '$typedefsfile': $!\n";
+       $typeTypedefs .= '|' . $typeOtherTypedefs if (defined $typeOtherTypedefs);
 }
-$typeTypedefs .= '|' . $typeOtherTypedefs if ($typeOtherTypedefs ne "");
 
 sub build_types {
        my $mods = "(?x:  \n" . join("|\n  ", (@modifierList, @modifierListFile)) . "\n)";
@@ -774,12 +996,12 @@ sub build_types {
                  }x;
        $Type   = qr{
                        $NonptrType
-                       (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)?
+                       (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4}
                        (?:\s+$Inline|\s+$Modifier)*
                  }x;
        $TypeMisordered = qr{
                        $NonptrTypeMisordered
-                       (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)?
+                       (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4}
                        (?:\s+$Inline|\s+$Modifier)*
                  }x;
        $Declare        = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type};
@@ -800,10 +1022,16 @@ our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)};
 our $declaration_macros = qr{(?x:
        (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(|
        (?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(|
-       (?:$Storage\s+)?${Type}\s+uninitialized_var\s*\(|
        (?:SKCIPHER_REQUEST|SHASH_DESC|AHASH_REQUEST)_ON_STACK\s*\(
 )};
 
+our %allow_repeated_words = (
+       add => '',
+       added => '',
+       bad => '',
+       be => '',
+);
+
 sub deparenthesize {
        my ($string) = @_;
        return "" if (!defined($string));
@@ -844,20 +1072,24 @@ sub seed_camelcase_file {
        }
 }
 
+our %maintained_status = ();
+
 sub is_maintained_obsolete {
        my ($filename) = @_;
 
        return 0 if (!$tree || !(-e "$root/scripts/get_maintainer.pl"));
 
-       my $status = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`;
+       if (!exists($maintained_status{$filename})) {
+               $maintained_status{$filename} = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`;
+       }
 
-       return $status =~ /obsolete/i;
+       return $maintained_status{$filename} =~ /obsolete/i;
 }
 
 sub is_SPDX_License_valid {
        my ($license) = @_;
 
-       return 1 if (!$tree || which("python") eq "" || !(-e "$root/scripts/spdxcheck.py") || !(-e "$root/.git"));
+       return 1 if (!$tree || which("python") eq "" || !(-e "$root/scripts/spdxcheck.py") || !(-e "$gitroot"));
 
        my $root_path = abs_path($root);
        my $status = `cd "$root_path"; echo "$license" | python scripts/spdxcheck.py -`;
@@ -875,8 +1107,8 @@ sub seed_camelcase_includes {
 
        $camelcase_seeded = 1;
 
-       if (-e ".git") {
-               my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`;
+       if (-e "$gitroot") {
+               my $git_last_include_commit = `${git_command} log --no-merges --pretty=format:"%h%n" -1 -- include`;
                chomp $git_last_include_commit;
                $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit";
        } else {
@@ -903,8 +1135,8 @@ sub seed_camelcase_includes {
                return;
        }
 
-       if (-e ".git") {
-               $files = `git ls-files "include/*.h"`;
+       if (-e "$gitroot") {
+               $files = `${git_command} ls-files "include/*.h"`;
                @include_files = split('\n', $files);
        }
 
@@ -923,18 +1155,28 @@ sub seed_camelcase_includes {
        }
 }
 
+sub git_is_single_file {
+       my ($filename) = @_;
+
+       return 0 if ((which("git") eq "") || !(-e "$gitroot"));
+
+       my $output = `${git_command} ls-files -- $filename 2>/dev/null`;
+       my $count = $output =~ tr/\n//;
+       return $count eq 1 && $output =~ m{^${filename}$};
+}
+
 sub git_commit_info {
        my ($commit, $id, $desc) = @_;
 
-       return ($id, $desc) if ((which("git") eq "") || !(-e ".git"));
+       return ($id, $desc) if ((which("git") eq "") || !(-e "$gitroot"));
 
-       my $output = `git log --no-color --format='%H %s' -1 $commit 2>&1`;
+       my $output = `${git_command} log --no-color --format='%H %s' -1 $commit 2>&1`;
        $output =~ s/^\s*//gm;
        my @lines = split("\n", $output);
 
        return ($id, $desc) if ($#lines < 0);
 
-       if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous\./) {
+       if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous/) {
 # Maybe one day convert this block of bash into something that returns
 # all matching commit ids, but it's very slow...
 #
@@ -965,7 +1207,7 @@ my $fixlinenr = -1;
 
 # If input is git commits, extract all commits from the commit expressions.
 # For example, HEAD-3 means we need check 'HEAD, HEAD~1, HEAD~2'.
-die "$P: No git repository found\n" if ($git && !-e ".git");
+die "$P: No git repository found\n" if ($git && !-e "$gitroot");
 
 if ($git) {
        my @commits = ();
@@ -978,7 +1220,7 @@ if ($git) {
                } else {
                        $git_range = "-1 $commit_expr";
                }
-               my $lines = `git log --no-color --no-merges --pretty=format:'%H %s' $git_range`;
+               my $lines = `${git_command} log --no-color --no-merges --pretty=format:'%H %s' $git_range`;
                foreach my $line (split(/\n/, $lines)) {
                        $line =~ /^([0-9a-fA-F]{40,40}) (.*)$/;
                        next if (!defined($1) || !defined($2));
@@ -993,8 +1235,12 @@ if ($git) {
 }
 
 my $vname;
+$allow_c99_comments = !defined $ignore_type{"C99_COMMENT_TOLERANCE"};
 for my $filename (@ARGV) {
        my $FILE;
+       my $is_git_file = git_is_single_file($filename);
+       my $oldfile = $file;
+       $file = 1 if ($is_git_file);
        if ($git) {
                open($FILE, '-|', "git format-patch -M --stdout -1 $filename") ||
                        die "$P: $filename: git format-patch failed - $!\n";
@@ -1017,6 +1263,7 @@ for my $filename (@ARGV) {
        while (<$FILE>) {
                chomp;
                push(@rawlines, $_);
+               $vname = qq("$1") if ($filename eq '-' && $_ =~ m/^Subject:\s+(.+)/i);
        }
        close($FILE);
 
@@ -1038,6 +1285,7 @@ for my $filename (@ARGV) {
        @modifierListFile = ();
        @typeListFile = ();
        build_types();
+       $file = $oldfile if ($is_git_file);
 }
 
 if (!$quiet) {
@@ -1083,6 +1331,8 @@ sub parse_email {
        my ($formatted_email) = @_;
 
        my $name = "";
+       my $quoted = "";
+       my $name_comment = "";
        my $address = "";
        my $comment = "";
 
@@ -1113,42 +1363,76 @@ sub parse_email {
                }
        }
 
-       $name = trim($name);
-       $name =~ s/^\"|\"$//g;
+       # Extract comments from names excluding quoted parts
+       # "John D. (Doe)" - Do not extract
+       if ($name =~ s/\"(.+)\"//) {
+               $quoted = $1;
+       }
+       while ($name =~ s/\s*($balanced_parens)\s*/ /) {
+               $name_comment .= trim($1);
+       }
+       $name =~ s/^[ \"]+|[ \"]+$//g;
+       $name = trim("$quoted $name");
+
        $address = trim($address);
        $address =~ s/^\<|\>$//g;
+       $comment = trim($comment);
 
        if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
                $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
                $name = "\"$name\"";
        }
 
-       return ($name, $address, $comment);
+       return ($name, $name_comment, $address, $comment);
 }
 
 sub format_email {
-       my ($name, $address) = @_;
+       my ($name, $name_comment, $address, $comment) = @_;
 
        my $formatted_email;
 
-       $name = trim($name);
-       $name =~ s/^\"|\"$//g;
+       $name =~ s/^[ \"]+|[ \"]+$//g;
        $address = trim($address);
+       $address =~ s/(?:\.|\,|\")+$//; ##trailing commas, dots or quotes
 
        if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
                $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
                $name = "\"$name\"";
        }
 
+       $name_comment = trim($name_comment);
+       $name_comment = " $name_comment" if ($name_comment ne "");
+       $comment = trim($comment);
+       $comment = " $comment" if ($comment ne "");
+
        if ("$name" eq "") {
                $formatted_email = "$address";
        } else {
-               $formatted_email = "$name <$address>";
+               $formatted_email = "$name$name_comment <$address>";
        }
-
+       $formatted_email .= "$comment";
        return $formatted_email;
 }
 
+sub reformat_email {
+       my ($email) = @_;
+
+       my ($email_name, $name_comment, $email_address, $comment) = parse_email($email);
+       return format_email($email_name, $name_comment, $email_address, $comment);
+}
+
+sub same_email_addresses {
+       my ($email1, $email2) = @_;
+
+       my ($email1_name, $name1_comment, $email1_address, $comment1) = parse_email($email1);
+       my ($email2_name, $name2_comment, $email2_address, $comment2) = parse_email($email2);
+
+       return $email1_name eq $email2_name &&
+              $email1_address eq $email2_address &&
+              $name1_comment eq $name2_comment &&
+              $comment1 eq $comment2;
+}
+
 sub which {
        my ($bin) = @_;
 
@@ -1182,7 +1466,7 @@ sub expand_tabs {
                if ($c eq "\t") {
                        $res .= ' ';
                        $n++;
-                       for (; ($n % 8) != 0; $n++) {
+                       for (; ($n % $tabsize) != 0; $n++) {
                                $res .= ' ';
                        }
                        next;
@@ -1611,8 +1895,16 @@ sub ctx_statement_level {
 sub ctx_locate_comment {
        my ($first_line, $end_line) = @_;
 
+       # If c99 comment on the current line, or the line before or after
+       my ($current_comment) = ($rawlines[$end_line - 1] =~ m@^\+.*(//.*$)@);
+       return $current_comment if (defined $current_comment);
+       ($current_comment) = ($rawlines[$end_line - 2] =~ m@^[\+ ].*(//.*$)@);
+       return $current_comment if (defined $current_comment);
+       ($current_comment) = ($rawlines[$end_line] =~ m@^[\+ ].*(//.*$)@);
+       return $current_comment if (defined $current_comment);
+
        # Catch a comment on the end of the line itself.
-       my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@);
+       ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@);
        return $current_comment if (defined $current_comment);
 
        # Look through the context and try and figure out if there is a
@@ -2006,7 +2298,16 @@ sub report {
                splice(@lines, 1, 1);
                $output = join("\n", @lines);
        }
-       $output = (split('\n', $output))[0] . "\n" if ($terse);
+
+       if ($terse) {
+               $output = (split('\n', $output))[0] . "\n";
+       }
+
+       if ($verbose && exists($verbose_messages{$type}) &&
+           !exists($verbose_emitted{$type})) {
+               $output .= $verbose_messages{$type} . "\n\n";
+               $verbose_emitted{$type} = 1;
+       }
 
        push(our @report, $output);
 
@@ -2195,7 +2496,7 @@ sub string_find_replace {
 sub tabify {
        my ($leading) = @_;
 
-       my $source_indent = 8;
+       my $source_indent = $tabsize;
        my $max_spaces_before_tab = $source_indent - 1;
        my $spaces_to_tab = " " x $source_indent;
 
@@ -2237,6 +2538,133 @@ sub pos_last_openparen {
        return length(expand_tabs(substr($line, 0, $last_openparen))) + 1;
 }
 
+sub get_raw_comment {
+       my ($line, $rawline) = @_;
+       my $comment = '';
+
+       for my $i (0 .. (length($line) - 1)) {
+               if (substr($line, $i, 1) eq "$;") {
+                       $comment .= substr($rawline, $i, 1);
+               }
+       }
+
+       return $comment;
+}
+
+# Args:
+#   line: Patch line to check
+#   auto: Auto variable name, e.g. "per_child_auto"
+#   suffix: Suffix to expect on member, e.g. "_priv"
+#   warning: Warning name, e.g. "PRIV_AUTO"
+sub u_boot_struct_name {
+       my ($line, $auto, $suffix, $warning, $herecurr) = @_;
+
+       # Use _priv as a suffix for the device-private data struct
+       if ($line =~ /^\+\s*\.${auto}\s*=\s*sizeof\(struct\((\w+)\).*/) {
+               my $struct_name = $1;
+               if ($struct_name !~ /^\w+${suffix}/) {
+                       WARN($warning,
+                                "struct \'$struct_name\' should have a ${suffix} suffix\n"
+                                . $herecurr);
+               }
+       }
+}
+
+# Checks specific to U-Boot
+sub u_boot_line {
+       my ($realfile, $line, $rawline, $herecurr) = @_;
+
+       # ask for a test if a new uclass ID is added
+       if ($realfile =~ /uclass-id.h/ && $line =~ /^\+/) {
+               WARN("NEW_UCLASS",
+                    "Possible new uclass - make sure to add a sandbox driver, plus a test in test/dm/<name>.c\n" . $herecurr);
+       }
+
+       # try to get people to use the livetree API
+       if ($line =~ /^\+.*fdtdec_/) {
+               WARN("LIVETREE",
+                    "Use the livetree API (dev_read_...)\n" . $herecurr);
+       }
+
+       # add tests for new commands
+       if ($line =~ /^\+.*do_($Ident)\(struct cmd_tbl.*/) {
+               WARN("CMD_TEST",
+                    "Possible new command - make sure you add a test\n" . $herecurr);
+       }
+
+       # use if instead of #if
+       if ($realfile =~ /\.c$/ && $line =~ /^\+#if.*CONFIG.*/) {
+               WARN("PREFER_IF",
+                    "Use 'if (IS_ENABLED(CONFIG...))' instead of '#if or #ifdef' where possible\n" . $herecurr);
+       }
+
+       # prefer strl(cpy|cat) over strn(cpy|cat)
+       if ($line =~ /\bstrn(cpy|cat)\s*\(/) {
+               WARN("STRL",
+                    "strl$1 is preferred over strn$1 because it always produces a nul-terminated string\n" . $herecurr);
+       }
+
+       # use defconfig to manage CONFIG_CMD options
+       if ($line =~ /\+\s*#\s*(define|undef)\s+(CONFIG_CMD\w*)\b/) {
+               ERROR("DEFINE_CONFIG_CMD",
+                     "All commands are managed by Kconfig\n" . $herecurr);
+       }
+
+       # Don't put common.h and dm.h in header files
+       if ($realfile =~ /\.h$/ && $rawline =~ /^\+#include\s*<(common|dm)\.h>*/) {
+               ERROR("BARRED_INCLUDE_IN_HDR",
+                     "Avoid including common.h and dm.h in header files\n" . $herecurr);
+       }
+
+       # Do not disable fdt / initrd relocation
+       if ($rawline =~ /.*(fdt|initrd)_high=0xffffffff/) {
+               ERROR("DISABLE_FDT_OR_INITRD_RELOC",
+                    "fdt or initrd relocation disabled at boot time\n" . $herecurr);
+       }
+
+       # make sure 'skip_board_fixup' is not
+       if ($rawline =~ /.*skip_board_fixup.*/) {
+               ERROR("SKIP_BOARD_FIXUP",
+                    "Avoid setting skip_board_fixup env variable\n" . $herecurr);
+       }
+
+       # Do not use CONFIG_ prefix in CONFIG_IS_ENABLED() calls
+       if ($line =~ /^\+.*CONFIG_IS_ENABLED\(CONFIG_\w*\).*/) {
+               ERROR("CONFIG_IS_ENABLED_CONFIG",
+                     "CONFIG_IS_ENABLED() takes values without the CONFIG_ prefix\n" . $herecurr);
+       }
+
+       # Use _priv as a suffix for the device-private data struct
+       if ($line =~ /^\+\s*\.priv_auto\s*=\s*sizeof\(struct\((\w+)\).*/) {
+               my $struct_name = $1;
+               if ($struct_name !~ /^\w+_priv/) {
+                       WARN("PRIV_AUTO", "struct \'$struct_name\' should have a _priv suffix");
+               }
+       }
+
+       # Check struct names for the 'auto' members of struct driver
+       u_boot_struct_name($line, "priv_auto", "_priv", "PRIV_AUTO", $herecurr);
+       u_boot_struct_name($line, "plat_auto", "_plat", "PLAT_AUTO", $herecurr);
+       u_boot_struct_name($line, "per_child_auto", "_priv", "CHILD_PRIV_AUTO", $herecurr);
+       u_boot_struct_name($line, "per_child_plat_auto", "_plat",
+               "CHILD_PLAT_AUTO", $herecurr);
+
+       # Now the ones for struct uclass, skipping those in common with above
+       u_boot_struct_name($line, "per_device_auto", "_priv",
+               "DEVICE_PRIV_AUTO", $herecurr);
+       u_boot_struct_name($line, "per_device_plat_auto", "_plat",
+               "DEVICE_PLAT_AUTO", $herecurr);
+}
+
+sub exclude_global_initialisers {
+       my ($realfile) = @_;
+
+       # Do not check for BPF programs (tools/testing/selftests/bpf/progs/*.c, samples/bpf/*_kern.c, *.bpf.c).
+       return $realfile =~ m@^tools/testing/selftests/bpf/progs/.*\.c$@ ||
+               $realfile =~ m@^samples/bpf/.*_kern\.c$@ ||
+               $realfile =~ m@/bpf/.*\.bpf\.c$@;
+}
+
 sub process {
        my $filename = shift;
 
@@ -2255,10 +2683,12 @@ sub process {
        my $signoff = 0;
        my $author = '';
        my $authorsignoff = 0;
+       my $author_sob = '';
        my $is_patch = 0;
        my $is_binding_patch = -1;
        my $in_header_lines = $file ? 0 : 1;
        my $in_commit_log = 0;          #Scanning lines before patch
+       my $has_patch_separator = 0;    #Found a --- line
        my $has_commit_log = 0;         #Encountered lines before patch
        my $commit_log_lines = 0;       #Number of commit log lines
        my $commit_log_possible_stack_dump = 0;
@@ -2317,7 +2747,7 @@ sub process {
 
                if ($rawline=~/^\+\+\+\s+(\S+)/) {
                        $setup_docs = 0;
-                       if ($1 =~ m@Documentation/admin-guide/kernel-parameters.rst$@) {
+                       if ($1 =~ m@Documentation/admin-guide/kernel-parameters.txt$@) {
                                $setup_docs = 1;
                        }
                        #next;
@@ -2398,6 +2828,7 @@ sub process {
                $sline =~ s/$;/ /g;     #with comments as spaces
 
                my $rawline = $rawlines[$linenr - 1];
+               my $raw_comment = get_raw_comment($line, $rawline);
 
 # check if it's a mode change, rename or start of a patch
                if (!$in_commit_log &&
@@ -2516,7 +2947,7 @@ sub process {
                                if (($last_binding_patch != -1) &&
                                    ($last_binding_patch ^ $is_binding_patch)) {
                                        WARN("DT_SPLIT_BINDING_PATCH",
-                                            "DT binding docs and includes should be a separate patch. See: Documentation/devicetree/bindings/submitting-patches.txt\n");
+                                            "DT binding docs and includes should be a separate patch. See: Documentation/devicetree/bindings/submitting-patches.rst\n");
                                }
                        }
 
@@ -2545,8 +2976,8 @@ sub process {
 
 # Check if the commit log has what seems like a diff which can confuse patch
                if ($in_commit_log && !$commit_log_has_diff &&
-                   (($line =~ m@^\s+diff\b.*a/[\w/]+@ &&
-                     $line =~ m@^\s+diff\b.*a/([\w/]+)\s+b/$1\b@) ||
+                   (($line =~ m@^\s+diff\b.*a/([\w/]+)@ &&
+                     $line =~ m@^\s+diff\b.*a/[\w/]+\s+b/$1\b@) ||
                     $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ ||
                     $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) {
                        ERROR("DIFF_IN_COMMIT_MSG",
@@ -2567,23 +2998,60 @@ sub process {
 # Check the patch for a From:
                if (decode("MIME-Header", $line) =~ /^From:\s*(.*)/) {
                        $author = $1;
+                       my $curline = $linenr;
+                       while(defined($rawlines[$curline]) && ($rawlines[$curline++] =~ /^[ \t]\s*(.*)/)) {
+                               $author .= $1;
+                       }
                        $author = encode("utf8", $author) if ($line =~ /=\?utf-8\?/i);
                        $author =~ s/"//g;
+                       $author = reformat_email($author);
                }
 
 # Check the patch for a signoff:
-               if ($line =~ /^\s*signed-off-by:/i) {
+               if ($line =~ /^\s*signed-off-by:\s*(.*)/i) {
                        $signoff++;
                        $in_commit_log = 0;
-                       if ($author ne '') {
-                               my $l = $line;
-                               $l =~ s/"//g;
-                               if ($l =~ /^\s*signed-off-by:\s*\Q$author\E/i) {
-                                   $authorsignoff = 1;
+                       if ($author ne ''  && $authorsignoff != 1) {
+                               if (same_email_addresses($1, $author)) {
+                                       $authorsignoff = 1;
+                               } else {
+                                       my $ctx = $1;
+                                       my ($email_name, $email_comment, $email_address, $comment1) = parse_email($ctx);
+                                       my ($author_name, $author_comment, $author_address, $comment2) = parse_email($author);
+
+                                       if ($email_address eq $author_address && $email_name eq $author_name) {
+                                               $author_sob = $ctx;
+                                               $authorsignoff = 2;
+                                       } elsif ($email_address eq $author_address) {
+                                               $author_sob = $ctx;
+                                               $authorsignoff = 3;
+                                       } elsif ($email_name eq $author_name) {
+                                               $author_sob = $ctx;
+                                               $authorsignoff = 4;
+
+                                               my $address1 = $email_address;
+                                               my $address2 = $author_address;
+
+                                               if ($address1 =~ /(\S+)\+\S+(\@.*)/) {
+                                                       $address1 = "$1$2";
+                                               }
+                                               if ($address2 =~ /(\S+)\+\S+(\@.*)/) {
+                                                       $address2 = "$1$2";
+                                               }
+                                               if ($address1 eq $address2) {
+                                                       $authorsignoff = 5;
+                                               }
+                                       }
                                }
                        }
                }
 
+# Check for patch separator
+               if ($line =~ /^---$/) {
+                       $has_patch_separator = 1;
+                       $in_commit_log = 0;
+               }
+
 # Check if MAINTAINERS is being updated.  If so, there's probably no need to
 # emit the "does MAINTAINERS need updating?" message on file add/move/delete
                if ($line =~ /^\s*MAINTAINERS\s*\|/) {
@@ -2600,8 +3068,17 @@ sub process {
                        my $ucfirst_sign_off = ucfirst(lc($sign_off));
 
                        if ($sign_off !~ /$signature_tags/) {
-                               WARN("BAD_SIGN_OFF",
-                                    "Non-standard signature: $sign_off\n" . $herecurr);
+                               my $suggested_signature = find_standard_signature($sign_off);
+                               if ($suggested_signature eq "") {
+                                       WARN("BAD_SIGN_OFF",
+                                            "Non-standard signature: $sign_off\n" . $herecurr);
+                               } else {
+                                       if (WARN("BAD_SIGN_OFF",
+                                                "Non-standard signature: '$sign_off' - perhaps '$suggested_signature'?\n" . $herecurr) &&
+                                           $fix) {
+                                               $fixed[$fixlinenr] =~ s/$sign_off/$suggested_signature/;
+                                       }
+                               }
                        }
                        if (defined $space_before && $space_before ne "") {
                                if (WARN("BAD_SIGN_OFF",
@@ -2629,8 +3106,8 @@ sub process {
                                }
                        }
 
-                       my ($email_name, $email_address, $comment) = parse_email($email);
-                       my $suggested_email = format_email(($email_name, $email_address));
+                       my ($email_name, $name_comment, $email_address, $comment) = parse_email($email);
+                       my $suggested_email = format_email(($email_name, $name_comment, $email_address, $comment));
                        if ($suggested_email eq "") {
                                ERROR("BAD_SIGN_OFF",
                                      "Unrecognized email address: '$email'\n" . $herecurr);
@@ -2640,11 +3117,77 @@ sub process {
                                $dequoted =~ s/" </ </;
                                # Don't force email to have quotes
                                # Allow just an angle bracketed address
-                               if ("$dequoted$comment" ne $email &&
-                                   "<$email_address>$comment" ne $email &&
-                                   "$suggested_email$comment" ne $email) {
+                               if (!same_email_addresses($email, $suggested_email)) {
+                                       if (WARN("BAD_SIGN_OFF",
+                                                "email address '$email' might be better as '$suggested_email'\n" . $herecurr) &&
+                                           $fix) {
+                                               $fixed[$fixlinenr] =~ s/\Q$email\E/$suggested_email/;
+                                       }
+                               }
+
+                               # Address part shouldn't have comments
+                               my $stripped_address = $email_address;
+                               $stripped_address =~ s/\([^\(\)]*\)//g;
+                               if ($email_address ne $stripped_address) {
+                                       if (WARN("BAD_SIGN_OFF",
+                                                "address part of email should not have comments: '$email_address'\n" . $herecurr) &&
+                                           $fix) {
+                                               $fixed[$fixlinenr] =~ s/\Q$email_address\E/$stripped_address/;
+                                       }
+                               }
+
+                               # Only one name comment should be allowed
+                               my $comment_count = () = $name_comment =~ /\([^\)]+\)/g;
+                               if ($comment_count > 1) {
                                        WARN("BAD_SIGN_OFF",
-                                            "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr);
+                                            "Use a single name comment in email: '$email'\n" . $herecurr);
+                               }
+
+
+                               # stable@vger.kernel.org or stable@kernel.org shouldn't
+                               # have an email name. In addition comments should strictly
+                               # begin with a #
+                               if ($email =~ /^.*stable\@(?:vger\.)?kernel\.org/i) {
+                                       if (($comment ne "" && $comment !~ /^#.+/) ||
+                                           ($email_name ne "")) {
+                                               my $cur_name = $email_name;
+                                               my $new_comment = $comment;
+                                               $cur_name =~ s/[a-zA-Z\s\-\"]+//g;
+
+                                               # Remove brackets enclosing comment text
+                                               # and # from start of comments to get comment text
+                                               $new_comment =~ s/^\((.*)\)$/$1/;
+                                               $new_comment =~ s/^\[(.*)\]$/$1/;
+                                               $new_comment =~ s/^[\s\#]+|\s+$//g;
+
+                                               $new_comment = trim("$new_comment $cur_name") if ($cur_name ne $new_comment);
+                                               $new_comment = " # $new_comment" if ($new_comment ne "");
+                                               my $new_email = "$email_address$new_comment";
+
+                                               if (WARN("BAD_STABLE_ADDRESS_STYLE",
+                                                        "Invalid email format for stable: '$email', prefer '$new_email'\n" . $herecurr) &&
+                                                   $fix) {
+                                                       $fixed[$fixlinenr] =~ s/\Q$email\E/$new_email/;
+                                               }
+                                       }
+                               } elsif ($comment ne "" && $comment !~ /^(?:#.+|\(.+\))$/) {
+                                       my $new_comment = $comment;
+
+                                       # Extract comment text from within brackets or
+                                       # c89 style /*...*/ comments
+                                       $new_comment =~ s/^\[(.*)\]$/$1/;
+                                       $new_comment =~ s/^\/\*(.*)\*\/$/$1/;
+
+                                       $new_comment = trim($new_comment);
+                                       $new_comment =~ s/^[^\w]$//; # Single lettered comment with non word character is usually a typo
+                                       $new_comment = "($new_comment)" if ($new_comment ne "");
+                                       my $new_email = format_email($email_name, $name_comment, $email_address, $new_comment);
+
+                                       if (WARN("BAD_SIGN_OFF",
+                                                "Unexpected content after email: '$email', should be: '$new_email'\n" . $herecurr) &&
+                                           $fix) {
+                                               $fixed[$fixlinenr] =~ s/\Q$email\E/$new_email/;
+                                       }
                                }
                        }
 
@@ -2658,6 +3201,24 @@ sub process {
                        } else {
                                $signatures{$sig_nospace} = 1;
                        }
+
+# Check Co-developed-by: immediately followed by Signed-off-by: with same name and email
+                       if ($sign_off =~ /^co-developed-by:$/i) {
+                               if ($email eq $author) {
+                                       WARN("BAD_SIGN_OFF",
+                                             "Co-developed-by: should not be used to attribute nominal patch author '$author'\n" . "$here\n" . $rawline);
+                               }
+                               if (!defined $lines[$linenr]) {
+                                       WARN("BAD_SIGN_OFF",
+                                            "Co-developed-by: must be immediately followed by Signed-off-by:\n" . "$here\n" . $rawline);
+                               } elsif ($rawlines[$linenr] !~ /^\s*signed-off-by:\s*(.*)/i) {
+                                       WARN("BAD_SIGN_OFF",
+                                            "Co-developed-by: must be immediately followed by Signed-off-by:\n" . "$here\n" . $rawline . "\n" .$rawlines[$linenr]);
+                               } elsif ($1 ne $email) {
+                                       WARN("BAD_SIGN_OFF",
+                                            "Co-developed-by and Signed-off-by: name/email do not match \n" . "$here\n" . $rawline . "\n" .$rawlines[$linenr]);
+                               }
+                       }
                }
 
 # Check email subject for common tools that don't need to be mentioned
@@ -2667,10 +3228,13 @@ sub process {
                             "A patch subject line should describe the change not the tool that found it\n" . $herecurr);
                }
 
-# Check for unwanted Gerrit info
-               if ($in_commit_log && $line =~ /^\s*change-id:/i) {
-                       ERROR("GERRIT_CHANGE_ID",
-                             "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr);
+# Check for Gerrit Change-Ids not in any patch context
+               if ($realfile eq '' && !$has_patch_separator && $line =~ /^\s*change-id:/i) {
+                       if (ERROR("GERRIT_CHANGE_ID",
+                                 "Remove Gerrit Change-Id's before submitting upstream\n" . $herecurr) &&
+                           $fix) {
+                               fix_delete_line($fixlinenr, $rawline);
+                       }
                }
 
 # Check if the commit log is in a possible stack dump
@@ -2678,8 +3242,10 @@ sub process {
                    ($line =~ /^\s*(?:WARNING:|BUG:)/ ||
                     $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ ||
                                        # timestamp
-                    $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/)) {
-                                       # stack dump address
+                    $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/) ||
+                    $line =~ /^(?:\s+\w+:\s+[0-9a-fA-F]+){3,3}/ ||
+                    $line =~ /^\s*\#\d+\s*\[[0-9a-fA-F]+\]\s*\w+ at [0-9a-fA-F]+/) {
+                                       # stack dump address styles
                        $commit_log_possible_stack_dump = 1;
                }
 
@@ -2690,8 +3256,8 @@ sub process {
                                        # file delta changes
                      $line =~ /^\s*(?:[\w\.\-]+\/)++[\w\.\-]+:/ ||
                                        # filename then :
-                     $line =~ /^\s*(?:Fixes:|Link:)/i ||
-                                       # A Fixes: or Link: line
+                     $line =~ /^\s*(?:Fixes:|Link:|$signature_tags)/i ||
+                                       # A Fixes: or Link: line or signature tag line
                      $commit_log_possible_stack_dump)) {
                        WARN("COMMIT_LOG_LONG_LINE",
                             "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr);
@@ -2704,9 +3270,18 @@ sub process {
                        $commit_log_possible_stack_dump = 0;
                }
 
+# Check for lines starting with a #
+               if ($in_commit_log && $line =~ /^#/) {
+                       if (WARN("COMMIT_COMMENT_SYMBOL",
+                                "Commit log lines starting with '#' are dropped by git as comments\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/^/ /;
+                       }
+               }
+
 # Check for git id commit length and improperly formed commit descriptions
                if ($in_commit_log && !$commit_log_possible_stack_dump &&
-                   $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink):/i &&
+                   $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink|base-commit):/i &&
                    $line !~ /^This reverts commit [0-9a-f]{7,40}/ &&
                    ($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i ||
                     ($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i &&
@@ -2775,6 +3350,14 @@ sub process {
                             "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr);
                }
 
+# Check for adding new DT bindings not in schema format
+               if (!$in_commit_log &&
+                   ($line =~ /^new file mode\s*\d+\s*$/) &&
+                   ($realfile =~ m@^Documentation/devicetree/bindings/.*\.txt$@)) {
+                       WARN("DT_SCHEMA_BINDING_PATCH",
+                            "DT bindings should be in DT schema format. See: Documentation/devicetree/bindings/writing-schema.rst\n");
+               }
+
 # Check for wrappage within a valid hunk of the file
                if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) {
                        ERROR("CORRUPTED_PATCH",
@@ -2836,21 +3419,89 @@ sub process {
 # Check for various typo / spelling mistakes
                if (defined($misspellings) &&
                    ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) {
-                       while ($rawline =~ /(?:^|[^a-z@])($misspellings)(?:\b|$|[^a-z@])/gi) {
+                       while ($rawline =~ /(?:^|[^\w\-'`])($misspellings)(?:[^\w\-'`]|$)/gi) {
                                my $typo = $1;
+                               my $blank = copy_spacing($rawline);
+                               my $ptr = substr($blank, 0, $-[1]) . "^" x length($typo);
+                               my $hereptr = "$hereline$ptr\n";
                                my $typo_fix = $spelling_fix{lc($typo)};
                                $typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/);
                                $typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/);
                                my $msg_level = \&WARN;
                                $msg_level = \&CHK if ($file);
                                if (&{$msg_level}("TYPO_SPELLING",
-                                                 "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) &&
+                                                 "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $hereptr) &&
                                    $fix) {
                                        $fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/;
                                }
                        }
                }
 
+# check for invalid commit id
+               if ($in_commit_log && $line =~ /(^fixes:|\bcommit)\s+([0-9a-f]{6,40})\b/i) {
+                       my $id;
+                       my $description;
+                       ($id, $description) = git_commit_info($2, undef, undef);
+                       if (!defined($id)) {
+                               WARN("UNKNOWN_COMMIT_ID",
+                                    "Unknown commit id '$2', maybe rebased or not pulled?\n" . $herecurr);
+                       }
+               }
+
+# check for repeated words separated by a single space
+# avoid false positive from list command eg, '-rw-r--r-- 1 root root'
+               if (($rawline =~ /^\+/ || $in_commit_log) &&
+                   $rawline !~ /[bcCdDlMnpPs\?-][rwxsStT-]{9}/) {
+                       pos($rawline) = 1 if (!$in_commit_log);
+                       while ($rawline =~ /\b($word_pattern) (?=($word_pattern))/g) {
+
+                               my $first = $1;
+                               my $second = $2;
+                               my $start_pos = $-[1];
+                               my $end_pos = $+[2];
+                               if ($first =~ /(?:struct|union|enum)/) {
+                                       pos($rawline) += length($first) + length($second) + 1;
+                                       next;
+                               }
+
+                               next if (lc($first) ne lc($second));
+                               next if ($first eq 'long');
+
+                               # check for character before and after the word matches
+                               my $start_char = '';
+                               my $end_char = '';
+                               $start_char = substr($rawline, $start_pos - 1, 1) if ($start_pos > ($in_commit_log ? 0 : 1));
+                               $end_char = substr($rawline, $end_pos, 1) if ($end_pos < length($rawline));
+
+                               next if ($start_char =~ /^\S$/);
+                               next if (index(" \t.,;?!", $end_char) == -1);
+
+                               # avoid repeating hex occurrences like 'ff ff fe 09 ...'
+                               if ($first =~ /\b[0-9a-f]{2,}\b/i) {
+                                       next if (!exists($allow_repeated_words{lc($first)}));
+                               }
+
+                               if (WARN("REPEATED_WORD",
+                                        "Possible repeated word: '$first'\n" . $herecurr) &&
+                                   $fix) {
+                                       $fixed[$fixlinenr] =~ s/\b$first $second\b/$first/;
+                               }
+                       }
+
+                       # if it's a repeated word on consecutive lines in a comment block
+                       if ($prevline =~ /$;+\s*$/ &&
+                           $prevrawline =~ /($word_pattern)\s*$/) {
+                               my $last_word = $1;
+                               if ($rawline =~ /^\+\s*\*\s*$last_word /) {
+                                       if (WARN("REPEATED_WORD",
+                                                "Possible repeated word: '$last_word'\n" . $hereprev) &&
+                                           $fix) {
+                                               $fixed[$fixlinenr] =~ s/(\+\s*\*\s*)$last_word /$1/;
+                                       }
+                               }
+                       }
+               }
+
 # ignore non-hunk lines and lines being removed
                next if (!$hunk_line || $line =~ /^-/);
 
@@ -2909,11 +3560,7 @@ sub process {
 
                                if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) {
                                        $is_start = 1;
-                               } elsif ($lines[$ln - 1] =~ /^\+\s*(?:help|---help---)\s*$/) {
-                                       if ($lines[$ln - 1] =~ "---help---") {
-                                               WARN("CONFIG_DESCRIPTION",
-                                                    "prefer 'help' over '---help---' for new help texts\n" . $herecurr);
-                                       }
+                               } elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) {
                                        $length = -1;
                                }
 
@@ -2940,22 +3587,44 @@ sub process {
                        #print "is_start<$is_start> is_end<$is_end> length<$length>\n";
                }
 
-# check for MAINTAINERS entries that don't have the right form
-               if ($realfile =~ /^MAINTAINERS$/ &&
-                   $rawline =~ /^\+[A-Z]:/ &&
-                   $rawline !~ /^\+[A-Z]:\t\S/) {
-                       if (WARN("MAINTAINERS_STYLE",
-                                "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) &&
-                           $fix) {
-                               $fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/;
+# check MAINTAINERS entries
+               if ($realfile =~ /^MAINTAINERS$/) {
+# check MAINTAINERS entries for the right form
+                       if ($rawline =~ /^\+[A-Z]:/ &&
+                           $rawline !~ /^\+[A-Z]:\t\S/) {
+                               if (WARN("MAINTAINERS_STYLE",
+                                        "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) &&
+                                   $fix) {
+                                       $fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/;
+                               }
+                       }
+# check MAINTAINERS entries for the right ordering too
+                       my $preferred_order = 'MRLSWQBCPTFXNK';
+                       if ($rawline =~ /^\+[A-Z]:/ &&
+                           $prevrawline =~ /^[\+ ][A-Z]:/) {
+                               $rawline =~ /^\+([A-Z]):\s*(.*)/;
+                               my $cur = $1;
+                               my $curval = $2;
+                               $prevrawline =~ /^[\+ ]([A-Z]):\s*(.*)/;
+                               my $prev = $1;
+                               my $prevval = $2;
+                               my $curindex = index($preferred_order, $cur);
+                               my $previndex = index($preferred_order, $prev);
+                               if ($curindex < 0) {
+                                       WARN("MAINTAINERS_STYLE",
+                                            "Unknown MAINTAINERS entry type: '$cur'\n" . $herecurr);
+                               } else {
+                                       if ($previndex >= 0 && $curindex < $previndex) {
+                                               WARN("MAINTAINERS_STYLE",
+                                                    "Misordered MAINTAINERS entry - list '$cur:' before '$prev:'\n" . $hereprev);
+                                       } elsif ((($prev eq 'F' && $cur eq 'F') ||
+                                                 ($prev eq 'X' && $cur eq 'X')) &&
+                                                ($prevval cmp $curval) > 0) {
+                                               WARN("MAINTAINERS_STYLE",
+                                                    "Misordered MAINTAINERS entry - list file patterns in alphabetic order\n" . $hereprev);
+                                       }
+                               }
                        }
-               }
-
-# discourage the use of boolean for type definition attributes of Kconfig options
-               if ($realfile =~ /Kconfig/ &&
-                   $line =~ /^\+\s*\bboolean\b/) {
-                       WARN("CONFIG_TYPE_BOOLEAN",
-                            "Use of boolean is deprecated, please use bool instead.\n" . $herecurr);
                }
 
                if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) &&
@@ -2980,7 +3649,7 @@ sub process {
                        my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g;
 
                        my $dt_path = $root . "/Documentation/devicetree/bindings/";
-                       my $vp_file = $dt_path . "vendor-prefixes.txt";
+                       my $vp_file = $dt_path . "vendor-prefixes.yaml";
 
                        foreach my $compat (@compats) {
                                my $compat2 = $compat;
@@ -2995,7 +3664,7 @@ sub process {
 
                                next if $compat !~ /^([a-zA-Z0-9\-]+)\,/;
                                my $vendor = $1;
-                               `grep -Eq "^$vendor\\b" $vp_file`;
+                               `grep -Eq "\\"\\^\Q$vendor\E,\\.\\*\\":" $vp_file`;
                                if ( $? >> 8 ) {
                                        WARN("UNDOCUMENTED_DT_STRING",
                                             "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr);
@@ -3013,29 +3682,62 @@ sub process {
                                        $comment = '/*';
                                } elsif ($realfile =~ /\.(c|dts|dtsi)$/) {
                                        $comment = '//';
-                               } elsif (($checklicenseline == 2) || $realfile =~ /\.(sh|pl|py|awk|tc)$/) {
+                               } elsif (($checklicenseline == 2) || $realfile =~ /\.(sh|pl|py|awk|tc|yaml)$/) {
                                        $comment = '#';
                                } elsif ($realfile =~ /\.rst$/) {
                                        $comment = '..';
                                }
 
+# check SPDX comment style for .[chsS] files
+                               if ($realfile =~ /\.[chsS]$/ &&
+                                   $rawline =~ /SPDX-License-Identifier:/ &&
+                                   $rawline !~ m@^\+\s*\Q$comment\E\s*@) {
+                                       WARN("SPDX_LICENSE_TAG",
+                                            "Improper SPDX comment style for '$realfile', please use '$comment' instead\n" . $herecurr);
+                               }
+
                                if ($comment !~ /^$/ &&
-                                   $rawline !~ /^\+\Q$comment\E SPDX-License-Identifier: /) {
-                                        WARN("SPDX_LICENSE_TAG",
-                                             "Missing or malformed SPDX-License-Identifier tag in line $checklicenseline\n" . $herecurr);
+                                   $rawline !~ m@^\+\Q$comment\E SPDX-License-Identifier: @) {
+                                       WARN("SPDX_LICENSE_TAG",
+                                            "Missing or malformed SPDX-License-Identifier tag in line $checklicenseline\n" . $herecurr);
                                } elsif ($rawline =~ /(SPDX-License-Identifier: .*)/) {
-                                        my $spdx_license = $1;
-                                        if (!is_SPDX_License_valid($spdx_license)) {
-                                                 WARN("SPDX_LICENSE_TAG",
-                                                      "'$spdx_license' is not supported in LICENSES/...\n" . $herecurr);
-                                        }
+                                       my $spdx_license = $1;
+                                       if (!is_SPDX_License_valid($spdx_license)) {
+                                               WARN("SPDX_LICENSE_TAG",
+                                                    "'$spdx_license' is not supported in LICENSES/...\n" . $herecurr);
+                                       }
+                                       if ($realfile =~ m@^Documentation/devicetree/bindings/@ &&
+                                           not $spdx_license =~ /GPL-2\.0.*BSD-2-Clause/) {
+                                               my $msg_level = \&WARN;
+                                               $msg_level = \&CHK if ($file);
+                                               if (&{$msg_level}("SPDX_LICENSE_TAG",
+
+                                                                 "DT binding documents should be licensed (GPL-2.0-only OR BSD-2-Clause)\n" . $herecurr) &&
+                                                   $fix) {
+                                                       $fixed[$fixlinenr] =~ s/SPDX-License-Identifier: .*/SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)/;
+                                               }
+                                       }
                                }
                        }
                }
 
+# check for embedded filenames
+               if ($rawline =~ /^\+.*\Q$realfile\E/) {
+                       WARN("EMBEDDED_FILENAME",
+                            "It's generally not useful to have the filename in the file\n" . $herecurr);
+               }
+
 # check we are in a valid source file if not then ignore this hunk
                next if ($realfile !~ /\.(h|c|s|S|sh|dtsi|dts)$/);
 
+# check for using SPDX-License-Identifier on the wrong line number
+               if ($realline != $checklicenseline &&
+                   $rawline =~ /\bSPDX-License-Identifier:/ &&
+                   substr($line, @-, @+ - @-) eq "$;" x (@+ - @-)) {
+                       WARN("SPDX_LICENSE_TAG",
+                            "Misplaced SPDX-License-Identifier tag - use line $checklicenseline instead\n" . $herecurr);
+               }
+
 # line length limit (with some exclusions)
 #
 # There are a few types of lines that may extend beyond $max_line_length:
@@ -3102,15 +3804,29 @@ sub process {
 
 # check for adding lines without a newline.
                if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) {
-                       WARN("MISSING_EOF_NEWLINE",
-                            "adding a line without newline at end of file\n" . $herecurr);
+                       if (WARN("MISSING_EOF_NEWLINE",
+                                "adding a line without newline at end of file\n" . $herecurr) &&
+                           $fix) {
+                               fix_delete_line($fixlinenr+1, "No newline at end of file");
+                       }
+               }
+
+# check for .L prefix local symbols in .S files
+               if ($realfile =~ /\.S$/ &&
+                   $line =~ /^\+\s*(?:[A-Z]+_)?SYM_[A-Z]+_(?:START|END)(?:_[A-Z_]+)?\s*\(\s*\.L/) {
+                       WARN("AVOID_L_PREFIX",
+                            "Avoid using '.L' prefixed local symbol names for denoting a range of code via 'SYM_*_START/END' annotations; see Documentation/asm-annotations.rst\n" . $herecurr);
+               }
+
+               if ($u_boot) {
+                       u_boot_line($realfile, $line, $rawline, $herecurr);
                }
 
 # check we are in a valid source file C or perl if not then ignore this hunk
                next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/);
 
 # at the beginning of a line any tabs must come first and anything
-# more than 8 must use tabs.
+# more than $tabsize must use tabs.
                if ($rawline =~ /^\+\s* \t\s*\S/ ||
                    $rawline =~ /^\+\s*        \s*/) {
                        my $herevet = "$here\n" . cat_vet($rawline) . "\n";
@@ -3129,7 +3845,7 @@ sub process {
                                "please, no space before tabs\n" . $herevet) &&
                            $fix) {
                                while ($fixed[$fixlinenr] =~
-                                          s/(^\+.*) {8,8}\t/$1\t\t/) {}
+                                          s/(^\+.*) {$tabsize,$tabsize}\t/$1\t\t/) {}
                                while ($fixed[$fixlinenr] =~
                                           s/(^\+.*) +\t/$1\t/) {}
                        }
@@ -3137,25 +3853,39 @@ sub process {
 
 # check for assignments on the start of a line
                if ($sline =~ /^\+\s+($Assignment)[^=]/) {
-                       CHK("ASSIGNMENT_CONTINUATIONS",
-                           "Assignment operator '$1' should be on the previous line\n" . $hereprev);
+                       my $operator = $1;
+                       if (CHK("ASSIGNMENT_CONTINUATIONS",
+                               "Assignment operator '$1' should be on the previous line\n" . $hereprev) &&
+                           $fix && $prevrawline =~ /^\+/) {
+                               # add assignment operator to the previous line, remove from current line
+                               $fixed[$fixlinenr - 1] .= " $operator";
+                               $fixed[$fixlinenr] =~ s/\Q$operator\E\s*//;
+                       }
                }
 
 # check for && or || at the start of a line
                if ($rawline =~ /^\+\s*(&&|\|\|)/) {
-                       CHK("LOGICAL_CONTINUATIONS",
-                           "Logical continuations should be on the previous line\n" . $hereprev);
+                       my $operator = $1;
+                       if (CHK("LOGICAL_CONTINUATIONS",
+                               "Logical continuations should be on the previous line\n" . $hereprev) &&
+                           $fix && $prevrawline =~ /^\+/) {
+                               # insert logical operator at last non-comment, non-whitepsace char on previous line
+                               $prevline =~ /[\s$;]*$/;
+                               my $line_end = substr($prevrawline, $-[0]);
+                               $fixed[$fixlinenr - 1] =~ s/\Q$line_end\E$/ $operator$line_end/;
+                               $fixed[$fixlinenr] =~ s/\Q$operator\E\s*//;
+                       }
                }
 
 # check indentation starts on a tab stop
                if ($perl_version_ok &&
                    $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$)|$Declare\s*$Ident\s*[;=])/) {
                        my $indent = length($1);
-                       if ($indent % 8) {
+                       if ($indent % $tabsize) {
                                if (WARN("TABSTOP",
                                         "Statements should start on a tabstop\n" . $herecurr) &&
                                    $fix) {
-                                       $fixed[$fixlinenr] =~ s@(^\+\t+) +@$1 . "\t" x ($indent/8)@e;
+                                       $fixed[$fixlinenr] =~ s@(^\+\t+) +@$1 . "\t" x ($indent/$tabsize)@e;
                                }
                        }
                }
@@ -3173,8 +3903,8 @@ sub process {
                                my $newindent = $2;
 
                                my $goodtabindent = $oldindent .
-                                       "\t" x ($pos / 8) .
-                                       " "  x ($pos % 8);
+                                       "\t" x ($pos / $tabsize) .
+                                       " "  x ($pos % $tabsize);
                                my $goodspaceindent = $oldindent . " "  x $pos;
 
                                if ($newindent ne $goodtabindent &&
@@ -3212,7 +3942,7 @@ sub process {
                if ($realfile =~ m@^(drivers/net/|net/)@ &&
                    $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ &&
                    $rawline =~ /^\+[ \t]*\*/ &&
-                   $realline > 2) {
+                   $realline > 3) { # Do not warn about the initial copyright comment block after SPDX-License-Identifier
                        WARN("NETWORKING_BLOCK_COMMENT_STYLE",
                             "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev);
                }
@@ -3294,43 +4024,48 @@ sub process {
                }
 
 # check for missing blank lines after declarations
-               if ($sline =~ /^\+\s+\S/ &&                     #Not at char 1
-                       # actual declarations
-                   ($prevline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
+# (declarations must have the same indentation and not be at the start of line)
+               if (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/) {
+                       # use temporaries
+                       my $sl = $sline;
+                       my $pl = $prevline;
+                       # remove $Attribute/$Sparse uses to simplify comparisons
+                       $sl =~ s/\b(?:$Attribute|$Sparse)\b//g;
+                       $pl =~ s/\b(?:$Attribute|$Sparse)\b//g;
+                       if (($pl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
                        # function pointer declarations
-                    $prevline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ ||
+                            $pl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ ||
                        # foo bar; where foo is some local typedef or #define
-                    $prevline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ ||
+                            $pl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ ||
                        # known declaration macros
-                    $prevline =~ /^\+\s+$declaration_macros/) &&
+                            $pl =~ /^\+\s+$declaration_macros/) &&
                        # for "else if" which can look like "$Ident $Ident"
-                   !($prevline =~ /^\+\s+$c90_Keywords\b/ ||
+                           !($pl =~ /^\+\s+$c90_Keywords\b/ ||
                        # other possible extensions of declaration lines
-                     $prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ ||
+                             $pl =~ /(?:$Compare|$Assignment|$Operators)\s*$/ ||
                        # not starting a section or a macro "\" extended line
-                     $prevline =~ /(?:\{\s*|\\)$/) &&
+                             $pl =~ /(?:\{\s*|\\)$/) &&
                        # looks like a declaration
-                   !($sline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
+                           !($sl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
                        # function pointer declarations
-                     $sline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ ||
+                             $sl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ ||
                        # foo bar; where foo is some local typedef or #define
-                     $sline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ ||
+                             $sl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ ||
                        # known declaration macros
-                     $sline =~ /^\+\s+$declaration_macros/ ||
+                             $sl =~ /^\+\s+$declaration_macros/ ||
                        # start of struct or union or enum
-                     $sline =~ /^\+\s+(?:static\s+)?(?:const\s+)?(?:union|struct|enum|typedef)\b/ ||
+                             $sl =~ /^\+\s+(?:static\s+)?(?:const\s+)?(?:union|struct|enum|typedef)\b/ ||
                        # start or end of block or continuation of declaration
-                     $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ ||
+                             $sl =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ ||
                        # bitfield continuation
-                     $sline =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ ||
+                             $sl =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ ||
                        # other possible extensions of declaration lines
-                     $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/) &&
-                       # indentation of previous and current line are the same
-                   (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/)) {
-                       if (WARN("LINE_SPACING",
-                                "Missing a blank line after declarations\n" . $hereprev) &&
-                           $fix) {
-                               fix_insert_line($fixlinenr, "\+");
+                             $sl =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/)) {
+                               if (WARN("LINE_SPACING",
+                                        "Missing a blank line after declarations\n" . $hereprev) &&
+                                   $fix) {
+                                       fix_insert_line($fixlinenr, "\+");
+                               }
                        }
                }
 
@@ -3383,12 +4118,16 @@ sub process {
                }
 
 # check indentation of a line with a break;
-# if the previous line is a goto or return and is indented the same # of tabs
+# if the previous line is a goto, return or break
+# and is indented the same # of tabs
                if ($sline =~ /^\+([\t]+)break\s*;\s*$/) {
                        my $tabs = $1;
-                       if ($prevline =~ /^\+$tabs(?:goto|return)\b/) {
-                               WARN("UNNECESSARY_BREAK",
-                                    "break is not useful after a goto or return\n" . $hereprev);
+                       if ($prevline =~ /^\+$tabs(goto|return|break)\b/) {
+                               if (WARN("UNNECESSARY_BREAK",
+                                        "break is not useful after a $1\n" . $hereprev) &&
+                                   $fix) {
+                                       fix_delete_line($fixlinenr, $rawline);
+                               }
                        }
                }
 
@@ -3645,11 +4384,11 @@ sub process {
                        #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n";
 
                        if ($check && $s ne '' &&
-                           (($sindent % 8) != 0 ||
+                           (($sindent % $tabsize) != 0 ||
                             ($sindent < $indent) ||
                             ($sindent == $indent &&
                              ($s !~ /^\s*(?:\}|\{|else\b)/)) ||
-                            ($sindent > $indent + 8))) {
+                            ($sindent > $indent + $tabsize))) {
                                WARN("SUSPECT_CODE_INDENT",
                                     "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n");
                        }
@@ -3671,6 +4410,17 @@ sub process {
 #ignore lines not being added
                next if ($line =~ /^[^\+]/);
 
+# check for self assignments used to avoid compiler warnings
+# e.g.:        int foo = foo, *bar = NULL;
+#      struct foo bar = *(&(bar));
+               if ($line =~ /^\+\s*(?:$Declare)?([A-Za-z_][A-Za-z\d_]*)\s*=/) {
+                       my $var = $1;
+                       if ($line =~ /^\+\s*(?:$Declare)?$var\s*=\s*(?:$var|\*\s*\(?\s*&\s*\(?\s*$var\s*\)?\s*\)?)\s*[;,]/) {
+                               WARN("SELF_ASSIGNMENT",
+                                    "Do not use self-assignments to avoid compiler warnings\n" . $herecurr);
+                       }
+               }
+
 # check for dereferences that span multiple lines
                if ($prevline =~ /^\+.*$Lval\s*(?:\.|->)\s*$/ &&
                    $line =~ /^\+\s*(?!\#\s*(?!define\s+|if))\s*$Lval/) {
@@ -3786,8 +4536,7 @@ sub process {
                if (defined $realline_next &&
                    exists $lines[$realline_next - 1] &&
                    !defined $suppress_export{$realline_next} &&
-                   ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ ||
-                    $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) {
+                   ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/)) {
                        # Handle definitions which produce identifiers with
                        # a prefix:
                        #   XXX(foo);
@@ -3814,8 +4563,7 @@ sub process {
                }
                if (!defined $suppress_export{$linenr} &&
                    $prevline =~ /^.\s*$/ &&
-                   ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ ||
-                    $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) {
+                   ($line =~ /EXPORT_SYMBOL.*\((.*)\)/)) {
 #print "FOO B <$lines[$linenr - 1]>\n";
                        $suppress_export{$linenr} = 2;
                }
@@ -3826,7 +4574,8 @@ sub process {
                }
 
 # check for global initialisers.
-               if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/) {
+               if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/ &&
+                   !exclude_global_initialisers($realfile)) {
                        if (ERROR("GLOBAL_INITIALISERS",
                                  "do not initialise globals to $1\n" . $herecurr) &&
                            $fix) {
@@ -3875,14 +4624,23 @@ sub process {
                        WARN("STATIC_CONST_CHAR_ARRAY",
                             "static const char * array should probably be static const char * const\n" .
                                $herecurr);
-               }
+               }
+
+# check for initialized const char arrays that should be static const
+               if ($line =~ /^\+\s*const\s+(char|unsigned\s+char|_*u8|(?:[us]_)?int8_t)\s+\w+\s*\[\s*(?:\w+\s*)?\]\s*=\s*"/) {
+                       if (WARN("STATIC_CONST_CHAR_ARRAY",
+                                "const array should probably be static const\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/(^.\s*)const\b/${1}static const/;
+                       }
+               }
 
 # check for static char foo[] = "bar" declarations.
                if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) {
                        WARN("STATIC_CONST_CHAR_ARRAY",
                             "static char array declaration should probably be static const char\n" .
                                $herecurr);
-               }
+               }
 
 # check for const <foo> const where <foo> is not a pointer or array type
                if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) {
@@ -3896,12 +4654,24 @@ sub process {
                        }
                }
 
+# check for const static or static <non ptr type> const declarations
+# prefer 'static const <foo>' over 'const static <foo>' and 'static <foo> const'
+               if ($sline =~ /^\+\s*const\s+static\s+($Type)\b/ ||
+                   $sline =~ /^\+\s*static\s+($BasicType)\s+const\b/) {
+                       if (WARN("STATIC_CONST",
+                                "Move const after static - use 'static const $1'\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/\bconst\s+static\b/static const/;
+                               $fixed[$fixlinenr] =~ s/\bstatic\s+($BasicType)\s+const\b/static const $1/;
+                       }
+               }
+
 # check for non-global char *foo[] = {"bar", ...} declarations.
                if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) {
                        WARN("STATIC_CONST_CHAR_ARRAY",
                             "char * array declaration might be better as static const\n" .
                                $herecurr);
-               }
+               }
 
 # check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo)
                if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) {
@@ -3917,7 +4687,7 @@ sub process {
                }
 
 # check for function declarations without arguments like "int foo()"
-               if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) {
+               if ($line =~ /(\b$Type\s*$Ident)\s*\(\s*\)/) {
                        if (ERROR("FUNCTION_WITHOUT_ARGS",
                                  "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) &&
                            $fix) {
@@ -4018,25 +4788,23 @@ sub process {
                             "printk() should include KERN_<LEVEL> facility level\n" . $herecurr);
                }
 
-               if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) {
-                       my $orig = $1;
+# prefer variants of (subsystem|netdev|dev|pr)_<level> to printk(KERN_<LEVEL>
+               if ($line =~ /\b(printk(_once|_ratelimited)?)\s*\(\s*KERN_([A-Z]+)/) {
+                       my $printk = $1;
+                       my $modifier = $2;
+                       my $orig = $3;
+                       $modifier = "" if (!defined($modifier));
                        my $level = lc($orig);
                        $level = "warn" if ($level eq "warning");
                        my $level2 = $level;
                        $level2 = "dbg" if ($level eq "debug");
+                       $level .= $modifier;
+                       $level2 .= $modifier;
                        WARN("PREFER_PR_LEVEL",
-                            "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(...  to printk(KERN_$orig ...\n" . $herecurr);
-               }
-
-               if ($line =~ /\bpr_warning\s*\(/) {
-                       if (WARN("PREFER_PR_LEVEL",
-                                "Prefer pr_warn(... to pr_warning(...\n" . $herecurr) &&
-                           $fix) {
-                               $fixed[$fixlinenr] =~
-                                   s/\bpr_warning\b/pr_warn/;
-                       }
+                            "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(...  to $printk(KERN_$orig ...\n" . $herecurr);
                }
 
+# prefer dev_<level> to dev_printk(KERN_<LEVEL>
                if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) {
                        my $orig = $1;
                        my $level = lc($orig);
@@ -4046,6 +4814,12 @@ sub process {
                             "Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr);
                }
 
+# trace_printk should not be used in production code.
+               if ($line =~ /\b(trace_printk|trace_puts|ftrace_vprintk)\s*\(/) {
+                       WARN("TRACE_PRINTK",
+                            "Do not use $1() in production code (this can be ignored if built only with a debug config option)\n" . $herecurr);
+               }
+
 # ENOSYS means "bad syscall nr" and nothing else.  This will have a small
 # number of false positives, but assembly files are not checked, so at
 # least the arch entry code will not trigger this warning.
@@ -4054,6 +4828,17 @@ sub process {
                             "ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr);
                }
 
+# ENOTSUPP is not a standard error code and should be avoided in new patches.
+# Folks usually mean EOPNOTSUPP (also called ENOTSUP), when they type ENOTSUPP.
+# Similarly to ENOSYS warning a small number of false positives is expected.
+               if (!$file && $line =~ /\bENOTSUPP\b/) {
+                       if (WARN("ENOTSUPP",
+                                "ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPP\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/\bENOTSUPP\b/EOPNOTSUPP/;
+                       }
+               }
+
 # function brace can't be on same line, except for #defines of do while,
 # or if closed on same line
                if ($perl_version_ok &&
@@ -4065,7 +4850,7 @@ sub process {
                            $fix) {
                                fix_delete_line($fixlinenr, $rawline);
                                my $fixed_line = $rawline;
-                               $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*){(.*)$/;
+                               $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*)\{(.*)$/;
                                my $line1 = $1;
                                my $line2 = $2;
                                fix_insert_line($fixlinenr, ltrim($line1));
@@ -4476,7 +5261,7 @@ sub process {
                                # A colon needs no spaces before when it is
                                # terminating a case value or a label.
                                } elsif ($opv eq ':C' || $opv eq ':L') {
-                                       if ($ctx =~ /Wx./) {
+                                       if ($ctx =~ /Wx./ and $realfile !~ m@.*\.lds\.h$@) {
                                                if (ERROR("SPACING",
                                                          "space prohibited before that '$op' $at\n" . $hereptr)) {
                                                        $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
@@ -4494,7 +5279,7 @@ sub process {
                                            ($op eq '>' &&
                                             $ca =~ /<\S+\@\S+$/))
                                        {
-                                               $ok = 1;
+                                               $ok = 1;
                                        }
 
                                        # for asm volatile statements
@@ -4560,7 +5345,7 @@ sub process {
 ##                 $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) {
 ##
 ##                     # Remove any bracketed sections to ensure we do not
-##                     # falsly report the parameters of functions.
+##                     # falsely report the parameters of functions.
 ##                     my $ln = $line;
 ##                     while ($ln =~ s/\([^\(\)]*\)//g) {
 ##                     }
@@ -4590,7 +5375,7 @@ sub process {
 
 # closing brace should have a space following it when it has anything
 # on the line
-               if ($line =~ /}(?!(?:,|;|\)))\S/) {
+               if ($line =~ /}(?!(?:,|;|\)|\}))\S/) {
                        if (ERROR("SPACING",
                                  "space required after that close brace '}'\n" . $herecurr) &&
                            $fix) {
@@ -4701,6 +5486,17 @@ sub process {
                        }
                }
 
+# check if a statement with a comma should be two statements like:
+#      foo = bar(),    /* comma should be semicolon */
+#      bar = baz();
+               if (defined($stat) &&
+                   $stat =~ /^\+\s*(?:$Lval\s*$Assignment\s*)?$FuncArg\s*,\s*(?:$Lval\s*$Assignment\s*)?$FuncArg\s*;\s*$/) {
+                       my $cnt = statement_rawlines($stat);
+                       my $herectx = get_stat_here($linenr, $cnt, $here);
+                       WARN("SUSPECT_COMMA_SEMICOLON",
+                            "Possible comma where semicolon could be used\n" . $herectx);
+               }
+
 # return is not a function
                if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) {
                        my $spacing = $1;
@@ -4728,7 +5524,7 @@ sub process {
                    $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) {
                        WARN("RETURN_VOID",
                             "void function return statements are not generally useful\n" . $hereprev);
-               }
+               }
 
 # if statements using unnecessary parentheses - ie: if ((foo == bar))
                if ($perl_version_ok &&
@@ -4821,15 +5617,37 @@ sub process {
                        my ($s, $c) = ($stat, $cond);
 
                        if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) {
-                               ERROR("ASSIGN_IN_IF",
-                                     "do not use assignment in if condition\n" . $herecurr);
+                               if (ERROR("ASSIGN_IN_IF",
+                                         "do not use assignment in if condition\n" . $herecurr) &&
+                                   $fix && $perl_version_ok) {
+                                       if ($rawline =~ /^\+(\s+)if\s*\(\s*(\!)?\s*\(\s*(($Lval)\s*=\s*$LvalOrFunc)\s*\)\s*(?:($Compare)\s*($FuncArg))?\s*\)\s*(\{)?\s*$/) {
+                                               my $space = $1;
+                                               my $not = $2;
+                                               my $statement = $3;
+                                               my $assigned = $4;
+                                               my $test = $8;
+                                               my $against = $9;
+                                               my $brace = $15;
+                                               fix_delete_line($fixlinenr, $rawline);
+                                               fix_insert_line($fixlinenr, "$space$statement;");
+                                               my $newline = "${space}if (";
+                                               $newline .= '!' if defined($not);
+                                               $newline .= '(' if (defined $not && defined($test) && defined($against));
+                                               $newline .= "$assigned";
+                                               $newline .= " $test $against" if (defined($test) && defined($against));
+                                               $newline .= ')' if (defined $not && defined($test) && defined($against));
+                                               $newline .= ')';
+                                               $newline .= " {" if (defined($brace));
+                                               fix_insert_line($fixlinenr + 1, $newline);
+                                       }
+                               }
                        }
 
                        # Find out what is on the end of the line after the
                        # conditional.
                        substr($s, 0, length($c), '');
                        $s =~ s/\n.*//g;
-                       $s =~ s/$;//g;  # Remove any comments
+                       $s =~ s/$;//g;  # Remove any comments
                        if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ &&
                            $c !~ /}\s*while\s*/)
                        {
@@ -4868,7 +5686,7 @@ sub process {
 # if and else should not have general statements after it
                if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) {
                        my $s = $1;
-                       $s =~ s/$;//g;  # Remove any comments
+                       $s =~ s/$;//g;  # Remove any comments
                        if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) {
                                ERROR("TRAILING_STATEMENTS",
                                      "trailing statements should be on next line\n" . $herecurr);
@@ -4940,24 +5758,16 @@ sub process {
                while ($line =~ m{($Constant|$Lval)}g) {
                        my $var = $1;
 
-#gcc binary extension
-                       if ($var =~ /^$Binary$/) {
-                               if (WARN("GCC_BINARY_CONSTANT",
-                                        "Avoid gcc v4.3+ binary constant extension: <$var>\n" . $herecurr) &&
-                                   $fix) {
-                                       my $hexval = sprintf("0x%x", oct($var));
-                                       $fixed[$fixlinenr] =~
-                                           s/\b$var\b/$hexval/;
-                               }
-                       }
-
 #CamelCase
                        if ($var !~ /^$Constant$/ &&
                            $var =~ /[A-Z][a-z]|[a-z][A-Z]/ &&
+#Ignore some autogenerated defines and enum values
+                           $var !~ /^(?:[A-Z]+_){1,5}[A-Z]{1,3}[a-z]/ &&
 #Ignore Page<foo> variants
                            $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ &&
-#Ignore SI style variants like nS, mV and dB (ie: max_uV, regulator_min_uA_show)
-                           $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/ &&
+#Ignore SI style variants like nS, mV and dB
+#(ie: max_uV, regulator_min_uA_show, RANGE_mA_VALUE)
+                           $var !~ /^(?:[a-z0-9_]*|[A-Z0-9_]*)?_?[a-z][A-Z](?:_[a-z0-9_]+|_[A-Z0-9_]+)?$/ &&
 #Ignore some three character SI units explicitly, like MiB and KHz
                            $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) {
                                while ($var =~ m{($Ident)}g) {
@@ -5048,13 +5858,13 @@ sub process {
                        $dstat =~ s/\s*$//s;
 
                        # Flatten any parentheses and braces
-                       while ($dstat =~ s/\([^\(\)]*\)/1/ ||
-                              $dstat =~ s/\{[^\{\}]*\}/1/ ||
-                              $dstat =~ s/.\[[^\[\]]*\]/1/)
+                       while ($dstat =~ s/\([^\(\)]*\)/1u/ ||
+                              $dstat =~ s/\{[^\{\}]*\}/1u/ ||
+                              $dstat =~ s/.\[[^\[\]]*\]/1u/)
                        {
                        }
 
-                       # Flatten any obvious string concatentation.
+                       # Flatten any obvious string concatenation.
                        while ($dstat =~ s/($String)\s*$Ident/$1/ ||
                               $dstat =~ s/$Ident\s*($String)/$1/)
                        {
@@ -5091,6 +5901,7 @@ sub process {
                            $dstat !~ /^\.$Ident\s*=/ &&                                # .foo =
                            $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ &&          # stringification #foo
                            $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ &&       # do {...} while (...); // do {...} while (...)
+                           $dstat !~ /^while\s*$Constant\s*$Constant\s*$/ &&           # while (...) {...}
                            $dstat !~ /^for\s*$Constant$/ &&                            # for (...)
                            $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ &&   # for (...) bar()
                            $dstat !~ /^do\s*{/ &&                                      # do {...
@@ -5132,7 +5943,7 @@ sub process {
                                next if ($arg =~ /\.\.\./);
                                next if ($arg =~ /^type$/i);
                                my $tmp_stmt = $define_stmt;
-                               $tmp_stmt =~ s/\b(typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g;
+                               $tmp_stmt =~ s/\b(__must_be_array|offsetof|sizeof|sizeof_field|__stringify|typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g;
                                $tmp_stmt =~ s/\#+\s*$arg\b//g;
                                $tmp_stmt =~ s/\b$arg\s*\#\#//g;
                                my $use_cnt = () = $tmp_stmt =~ /\b$arg\b/g;
@@ -5409,6 +6220,17 @@ sub process {
                             "Prefer using '\"%s...\", __func__' to using '$context_function', this function's name, in a string\n" . $herecurr);
                }
 
+# check for unnecessary function tracing like uses
+# This does not use $logFunctions because there are many instances like
+# 'dprintk(FOO, "%s()\n", __func__);' which do not match $logFunctions
+               if ($rawline =~ /^\+.*\([^"]*"$tracing_logging_tags{0,3}%s(?:\s*\(\s*\)\s*)?$tracing_logging_tags{0,3}(?:\\n)?"\s*,\s*__func__\s*\)\s*;/) {
+                       if (WARN("TRACING_LOGGING",
+                                "Unnecessary ftrace-like logging - prefer using ftrace\n" . $herecurr) &&
+                           $fix) {
+                                fix_delete_line($fixlinenr, $rawline);
+                       }
+               }
+
 # check for spaces before a quoted newline
                if ($rawline =~ /^.*\".*\s\\n/) {
                        if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE",
@@ -5531,7 +6353,8 @@ sub process {
                        my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0);
 #                      print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n");
 
-                       if ($s =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*(?:devm_)?(?:[kv][czm]alloc(?:_node|_array)?\b|kstrdup|kmemdup|(?:dev_)?alloc_skb)/) {
+                       if ($s =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*$allocFunctions\s*\(/ &&
+                           $s !~ /\b__GFP_NOWARN\b/ ) {
                                WARN("OOM_MESSAGE",
                                     "Possible unnecessary 'out of memory' message\n" . $hereprev);
                        }
@@ -5554,6 +6377,28 @@ sub process {
                             "Avoid logging continuation uses where feasible\n" . $herecurr);
                }
 
+# check for unnecessary use of %h[xudi] and %hh[xudi] in logging functions
+               if (defined $stat &&
+                   $line =~ /\b$logFunctions\s*\(/ &&
+                   index($stat, '"') >= 0) {
+                       my $lc = $stat =~ tr@\n@@;
+                       $lc = $lc + $linenr;
+                       my $stat_real = get_stat_real($linenr, $lc);
+                       pos($stat_real) = index($stat_real, '"');
+                       while ($stat_real =~ /[^\"%]*(%[\#\d\.\*\-]*(h+)[idux])/g) {
+                               my $pspec = $1;
+                               my $h = $2;
+                               my $lineoff = substr($stat_real, 0, $-[1]) =~ tr@\n@@;
+                               if (WARN("UNNECESSARY_MODIFIER",
+                                        "Integer promotion: Using '$h' in '$pspec' is unnecessary\n" . "$here\n$stat_real\n") &&
+                                   $fix && $fixed[$fixlinenr + $lineoff] =~ /^\+/) {
+                                       my $nspec = $pspec;
+                                       $nspec =~ s/h//g;
+                                       $fixed[$fixlinenr + $lineoff] =~ s/\Q$pspec\E/$nspec/;
+                               }
+                       }
+               }
+
 # check for mask then right shift without a parentheses
                if ($perl_version_ok &&
                    $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ &&
@@ -5652,7 +6497,7 @@ sub process {
                        # ignore udelay's < 10, however
                        if (! ($delay < 10) ) {
                                CHK("USLEEP_RANGE",
-                                   "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $herecurr);
+                                   "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst\n" . $herecurr);
                        }
                        if ($delay > 2000) {
                                WARN("LONG_UDELAY",
@@ -5664,7 +6509,7 @@ sub process {
                if ($line =~ /\bmsleep\s*\((\d+)\);/) {
                        if ($1 < 20) {
                                WARN("MSLEEP",
-                                    "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $herecurr);
+                                    "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.rst\n" . $herecurr);
                        }
                }
 
@@ -5712,8 +6557,7 @@ sub process {
                my $barriers = qr{
                        mb|
                        rmb|
-                       wmb|
-                       read_barrier_depends
+                       wmb
                }x;
                my $barrier_stems = qr{
                        mb__before_atomic|
@@ -5754,10 +6598,12 @@ sub process {
                        }
                }
 
-# check for smp_read_barrier_depends and read_barrier_depends
-               if (!$file && $line =~ /\b(smp_|)read_barrier_depends\s*\(/) {
-                       WARN("READ_BARRIER_DEPENDS",
-                            "$1read_barrier_depends should only be used in READ_ONCE or DEC Alpha code\n" . $herecurr);
+# check for data_race without a comment.
+               if ($line =~ /\bdata_race\s*\(/) {
+                       if (!ctx_has_comment($first_line, $linenr)) {
+                               WARN("DATA_RACE",
+                                    "data_race without comment\n" . $herecurr);
+                       }
                }
 
 # check of hardware specific defines
@@ -5799,38 +6645,68 @@ sub process {
                        }
                }
 
-# Check for __attribute__ packed, prefer __packed
-               if ($realfile !~ m@\binclude/uapi/@ &&
-                   $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) {
-                       WARN("PREFER_PACKED",
-                            "__packed is preferred over __attribute__((packed))\n" . $herecurr);
-               }
-
-# Check for __attribute__ aligned, prefer __aligned
-               if ($realfile !~ m@\binclude/uapi/@ &&
-                   $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) {
-                       WARN("PREFER_ALIGNED",
-                            "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr);
-               }
-
-# Check for __attribute__ format(printf, prefer __printf
+# Check for compiler attributes
                if ($realfile !~ m@\binclude/uapi/@ &&
-                   $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) {
-                       if (WARN("PREFER_PRINTF",
-                                "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) &&
-                           $fix) {
-                               $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex;
-
+                   $rawline =~ /\b__attribute__\s*\(\s*($balanced_parens)\s*\)/) {
+                       my $attr = $1;
+                       $attr =~ s/\s*\(\s*(.*)\)\s*/$1/;
+
+                       my %attr_list = (
+                               "alias"                         => "__alias",
+                               "aligned"                       => "__aligned",
+                               "always_inline"                 => "__always_inline",
+                               "assume_aligned"                => "__assume_aligned",
+                               "cold"                          => "__cold",
+                               "const"                         => "__attribute_const__",
+                               "copy"                          => "__copy",
+                               "designated_init"               => "__designated_init",
+                               "externally_visible"            => "__visible",
+                               "format"                        => "printf|scanf",
+                               "gnu_inline"                    => "__gnu_inline",
+                               "malloc"                        => "__malloc",
+                               "mode"                          => "__mode",
+                               "no_caller_saved_registers"     => "__no_caller_saved_registers",
+                               "noclone"                       => "__noclone",
+                               "noinline"                      => "noinline",
+                               "nonstring"                     => "__nonstring",
+                               "noreturn"                      => "__noreturn",
+                               "packed"                        => "__packed",
+                               "pure"                          => "__pure",
+                               "section"                       => "__section",
+                               "used"                          => "__used",
+                               "weak"                          => "__weak"
+                       );
+
+                       while ($attr =~ /\s*(\w+)\s*(${balanced_parens})?/g) {
+                               my $orig_attr = $1;
+                               my $params = '';
+                               $params = $2 if defined($2);
+                               my $curr_attr = $orig_attr;
+                               $curr_attr =~ s/^[\s_]+|[\s_]+$//g;
+                               if (exists($attr_list{$curr_attr})) {
+                                       my $new = $attr_list{$curr_attr};
+                                       if ($curr_attr eq "format" && $params) {
+                                               $params =~ /^\s*\(\s*(\w+)\s*,\s*(.*)/;
+                                               $new = "__$1\($2";
+                                       } else {
+                                               $new = "$new$params";
+                                       }
+                                       if (WARN("PREFER_DEFINED_ATTRIBUTE_MACRO",
+                                                "Prefer $new over __attribute__(($orig_attr$params))\n" . $herecurr) &&
+                                           $fix) {
+                                               my $remove = "\Q$orig_attr\E" . '\s*' . "\Q$params\E" . '(?:\s*,\s*)?';
+                                               $fixed[$fixlinenr] =~ s/$remove//;
+                                               $fixed[$fixlinenr] =~ s/\b__attribute__/$new __attribute__/;
+                                               $fixed[$fixlinenr] =~ s/\}\Q$new\E/} $new/;
+                                               $fixed[$fixlinenr] =~ s/ __attribute__\s*\(\s*\(\s*\)\s*\)//;
+                                       }
+                               }
                        }
-               }
 
-# Check for __attribute__ format(scanf, prefer __scanf
-               if ($realfile !~ m@\binclude/uapi/@ &&
-                   $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) {
-                       if (WARN("PREFER_SCANF",
-                                "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) &&
-                           $fix) {
-                               $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex;
+                       # Check for __attribute__ unused, prefer __always_unused or __maybe_unused
+                       if ($attr =~ /^_*unused/) {
+                               WARN("PREFER_DEFINED_ATTRIBUTE_MACRO",
+                                    "__always_unused or __maybe_unused is preferred over __attribute__((__unused__))\n" . $herecurr);
                        }
                }
 
@@ -5866,18 +6742,18 @@ sub process {
                if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) {
                        my $cast = $1;
                        my $const = $2;
+                       my $suffix = "";
+                       my $newconst = $const;
+                       $newconst =~ s/${Int_type}$//;
+                       $suffix .= 'U' if ($cast =~ /\bunsigned\b/);
+                       if ($cast =~ /\blong\s+long\b/) {
+                           $suffix .= 'LL';
+                       } elsif ($cast =~ /\blong\b/) {
+                           $suffix .= 'L';
+                       }
                        if (WARN("TYPECAST_INT_CONSTANT",
-                                "Unnecessary typecast of c90 int constant\n" . $herecurr) &&
+                                "Unnecessary typecast of c90 int constant - '$cast$const' could be '$const$suffix'\n" . $herecurr) &&
                            $fix) {
-                               my $suffix = "";
-                               my $newconst = $const;
-                               $newconst =~ s/${Int_type}$//;
-                               $suffix .= 'U' if ($cast =~ /\bunsigned\b/);
-                               if ($cast =~ /\blong\s+long\b/) {
-                                       $suffix .= 'LL';
-                               } elsif ($cast =~ /\blong\b/) {
-                                       $suffix .= 'L';
-                               }
                                $fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/;
                        }
                }
@@ -5928,14 +6804,20 @@ sub process {
                        for (my $count = $linenr; $count <= $lc; $count++) {
                                my $specifier;
                                my $extension;
+                               my $qualifier;
                                my $bad_specifier = "";
                                my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0));
                                $fmt =~ s/%%//g;
 
-                               while ($fmt =~ /(\%[\*\d\.]*p(\w))/g) {
+                               while ($fmt =~ /(\%[\*\d\.]*p(\w)(\w*))/g) {
                                        $specifier = $1;
                                        $extension = $2;
-                                       if ($extension !~ /[SsBKRraEhMmIiUDdgVCbGNOx]/) {
+                                       $qualifier = $3;
+                                       if ($extension !~ /[4SsBKRraEehMmIiUDdgVCbGNOxtf]/ ||
+                                           ($extension eq "f" &&
+                                            defined $qualifier && $qualifier !~ /^w/) ||
+                                           ($extension eq "4" &&
+                                            defined $qualifier && $qualifier !~ /^cc/)) {
                                                $bad_specifier = $specifier;
                                                last;
                                        }
@@ -5952,7 +6834,6 @@ sub process {
                                        my $ext_type = "Invalid";
                                        my $use = "";
                                        if ($bad_specifier =~ /p[Ff]/) {
-                                               $ext_type = "Deprecated";
                                                $use = " - use %pS instead";
                                                $use =~ s/pS/ps/ if ($bad_specifier =~ /pf/);
                                        }
@@ -6055,11 +6936,11 @@ sub process {
                        my $max = $7;
                        if ($min eq $max) {
                                WARN("USLEEP_RANGE",
-                                    "usleep_range should not use min == max args; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n");
+                                    "usleep_range should not use min == max args; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n");
                        } elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ &&
                                 $min > $max) {
                                WARN("USLEEP_RANGE",
-                                    "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n");
+                                    "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n");
                        }
                }
 
@@ -6116,8 +6997,7 @@ sub process {
                        if (defined $cond) {
                                substr($s, 0, length($cond), '');
                        }
-                       if ($s =~ /^\s*;/ &&
-                           $function_name ne 'uninitialized_var')
+                       if ($s =~ /^\s*;/)
                        {
                                WARN("AVOID_EXTERNS",
                                     "externs should be avoided in .c files\n" .  $herecurr);
@@ -6178,12 +7058,12 @@ sub process {
 
                        if (!grep(/$name/, @setup_docs)) {
                                CHK("UNDOCUMENTED_SETUP",
-                                   "__setup appears un-documented -- check Documentation/admin-guide/kernel-parameters.rst\n" . $herecurr);
+                                   "__setup appears un-documented -- check Documentation/admin-guide/kernel-parameters.txt\n" . $herecurr);
                        }
                }
 
-# check for pointless casting of kmalloc return
-               if ($line =~ /\*\s*\)\s*[kv][czm]alloc(_node){0,1}\b/) {
+# check for pointless casting of alloc functions
+               if ($line =~ /\*\s*\)\s*$allocFunctions\b/) {
                        WARN("UNNECESSARY_CASTS",
                             "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr);
                }
@@ -6191,7 +7071,7 @@ sub process {
 # alloc style
 # p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...)
                if ($perl_version_ok &&
-                   $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*([kv][mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) {
+                   $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k|v)[mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) {
                        CHK("ALLOC_SIZEOF_STRUCT",
                            "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr);
                }
@@ -6234,7 +7114,7 @@ sub process {
                }
 
 # check for alloc argument mismatch
-               if ($line =~ /\b(kcalloc|kmalloc_array)\s*\(\s*sizeof\b/) {
+               if ($line =~ /\b((?:devm_)?(?:kcalloc|kmalloc_array))\s*\(\s*sizeof\b/) {
                        WARN("ALLOC_ARRAY_ARGS",
                             "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr);
                }
@@ -6260,38 +7140,41 @@ sub process {
                        }
                }
 
+# check for IS_ENABLED() without CONFIG_<FOO> ($rawline for comments too)
+               if ($rawline =~ /\bIS_ENABLED\s*\(\s*(\w+)\s*\)/ && $1 !~ /^${CONFIG_}/) {
+                       WARN("IS_ENABLED_CONFIG",
+                            "IS_ENABLED($1) is normally used as IS_ENABLED(${CONFIG_}$1)\n" . $herecurr);
+               }
+
 # check for #if defined CONFIG_<FOO> || defined CONFIG_<FOO>_MODULE
-               if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(CONFIG_[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) {
+               if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(${CONFIG_}[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) {
                        my $config = $1;
                        if (WARN("PREFER_IS_ENABLED",
-                                "Prefer IS_ENABLED(<FOO>) to CONFIG_<FOO> || CONFIG_<FOO>_MODULE\n" . $herecurr) &&
+                                "Prefer IS_ENABLED(<FOO>) to ${CONFIG_}<FOO> || ${CONFIG_}<FOO>_MODULE\n" . $herecurr) &&
                            $fix) {
                                $fixed[$fixlinenr] = "\+#if IS_ENABLED($config)";
                        }
                }
 
-# check for case / default statements not preceded by break/fallthrough/switch
-               if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) {
-                       my $has_break = 0;
-                       my $has_statement = 0;
-                       my $count = 0;
-                       my $prevline = $linenr;
-                       while ($prevline > 1 && ($file || $count < 3) && !$has_break) {
-                               $prevline--;
-                               my $rline = $rawlines[$prevline - 1];
-                               my $fline = $lines[$prevline - 1];
-                               last if ($fline =~ /^\@\@/);
-                               next if ($fline =~ /^\-/);
-                               next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/);
-                               $has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i);
-                               next if ($fline =~ /^.[\s$;]*$/);
-                               $has_statement = 1;
-                               $count++;
-                               $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|exit\s*\(\b|return\b|goto\b|continue\b)/);
-                       }
-                       if (!$has_break && $has_statement) {
-                               WARN("MISSING_BREAK",
-                                    "Possible switch case/default not preceded by break or fallthrough comment\n" . $herecurr);
+# check for /* fallthrough */ like comment, prefer fallthrough;
+               my @fallthroughs = (
+                       'fallthrough',
+                       '@fallthrough@',
+                       'lint -fallthrough[ \t]*',
+                       'intentional(?:ly)?[ \t]*fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)',
+                       '(?:else,?\s*)?FALL(?:S | |-)?THR(?:OUGH|U|EW)[ \t.!]*(?:-[^\n\r]*)?',
+                       'Fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?',
+                       'fall(?:s | |-)?thr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?',
+                   );
+               if ($raw_comment ne '') {
+                       foreach my $ft (@fallthroughs) {
+                               if ($raw_comment =~ /$ft/) {
+                                       my $msg_level = \&WARN;
+                                       $msg_level = \&CHK if ($file);
+                                       &{$msg_level}("PREFER_FALLTHROUGH",
+                                                     "Prefer 'fallthrough;' over fallthrough comment\n" . $herecurr);
+                                       last;
+                               }
                        }
                }
 
@@ -6354,19 +7237,6 @@ sub process {
                        }
                }
 
-# check for bool bitfields
-               if ($sline =~ /^.\s+bool\s*$Ident\s*:\s*\d+\s*;/) {
-                       WARN("BOOL_BITFIELD",
-                            "Avoid using bool as bitfield.  Prefer bool bitfields as unsigned int or u<8|16|32>\n" . $herecurr);
-               }
-
-# check for bool use in .h files
-               if ($realfile =~ /\.h$/ &&
-                   $sline =~ /^.\s+bool\s*$Ident\s*(?::\s*d+\s*)?;/) {
-                       CHK("BOOL_MEMBER",
-                           "Avoid using bool structure members because of possible alignment issues - see: https://lkml.org/lkml/2017/11/21/384\n" . $herecurr);
-               }
-
 # check for semaphores initialized locked
                if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) {
                        WARN("CONSIDER_COMPLETION",
@@ -6385,9 +7255,24 @@ sub process {
                             "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr);
                }
 
+# check for spin_is_locked(), suggest lockdep instead
+               if ($line =~ /\bspin_is_locked\(/) {
+                       WARN("USE_LOCKDEP",
+                            "Where possible, use lockdep_assert_held instead of assertions based on spin_is_locked\n" . $herecurr);
+               }
+
+# check for deprecated apis
+               if ($line =~ /\b($deprecated_apis_search)\b\s*\(/) {
+                       my $deprecated_api = $1;
+                       my $new_api = $deprecated_apis{$deprecated_api};
+                       WARN("DEPRECATED_API",
+                            "Deprecated use of '$deprecated_api', prefer '$new_api' instead\n" . $herecurr);
+               }
+
 # check for various structs that are normally const (ops, kgdb, device_tree)
 # and avoid what seem like struct definitions 'struct foo {'
-               if ($line !~ /\bconst\b/ &&
+               if (defined($const_structs) &&
+                   $line !~ /\bconst\b/ &&
                    $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) {
                        WARN("CONST_STRUCT",
                             "struct $1 should normally be const\n" . $herecurr);
@@ -6395,12 +7280,14 @@ sub process {
 
 # use of NR_CPUS is usually wrong
 # ignore definitions of NR_CPUS and usage to define arrays as likely right
+# ignore designated initializers using NR_CPUS
                if ($line =~ /\bNR_CPUS\b/ &&
                    $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ &&
                    $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ &&
                    $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ &&
                    $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ &&
-                   $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/)
+                   $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/ &&
+                   $line !~ /^.\s*\.\w+\s*=\s*.*\bNR_CPUS\b/)
                {
                        WARN("NR_CPUS",
                             "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr);
@@ -6419,6 +7306,23 @@ sub process {
                             "Using $1 should generally have parentheses around the comparison\n" . $herecurr);
                }
 
+# return sysfs_emit(foo, fmt, ...) fmt without newline
+               if ($line =~ /\breturn\s+sysfs_emit\s*\(\s*$FuncArg\s*,\s*($String)/ &&
+                   substr($rawline, $-[6], $+[6] - $-[6]) !~ /\\n"$/) {
+                       my $offset = $+[6] - 1;
+                       if (WARN("SYSFS_EMIT",
+                                "return sysfs_emit(...) formats should include a terminating newline\n" . $herecurr) &&
+                           $fix) {
+                               substr($fixed[$fixlinenr], $offset, 0) = '\\n';
+                       }
+               }
+
+# nested likely/unlikely calls
+               if ($line =~ /\b(?:(?:un)?likely)\s*\(\s*!?\s*(IS_ERR(?:_OR_NULL|_VALUE)?|WARN)/) {
+                       WARN("LIKELY_MISUSE",
+                            "nested (un)?likely() calls, $1 already uses unlikely() internally\n" . $herecurr);
+               }
+
 # whine mightly about in_atomic
                if ($line =~ /\bin_atomic\s*\(/) {
                        if ($realfile =~ m@^drivers/@) {
@@ -6430,12 +7334,6 @@ sub process {
                        }
                }
 
-# check for mutex_trylock_recursive usage
-               if ($line =~ /mutex_trylock_recursive/) {
-                       ERROR("LOCKING",
-                             "recursive locking is bad, do not use this ever.\n" . $herecurr);
-               }
-
 # check for lockdep_set_novalidate_class
                if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ ||
                    $line =~ /__lockdep_no_validate__\s*\)/ ) {
@@ -6578,6 +7476,12 @@ sub process {
                                     "unknown module license " . $extracted_string . "\n" . $herecurr);
                        }
                }
+
+# check for sysctl duplicate constants
+               if ($line =~ /\.extra[12]\s*=\s*&(zero|one|int_max)\b/) {
+                       WARN("DUPLICATED_SYSCTL_CONST",
+                               "duplicated sysctl range checking value '$1', consider using the shared one in include/linux/sysctl.h\n" . $herecurr);
+               }
        }
 
        # If we have no input at all, then there is nothing to report on
@@ -6592,7 +7496,7 @@ sub process {
                exit(0);
        }
 
-       # This is not a patch, and we are are in 'no-patch' mode so
+       # This is not a patch, and we are in 'no-patch' mode so
        # just keep quiet.
        if (!$chk_patch && !$is_patch) {
                exit(0);
@@ -6606,9 +7510,33 @@ sub process {
                if ($signoff == 0) {
                        ERROR("MISSING_SIGN_OFF",
                              "Missing Signed-off-by: line(s)\n");
-               } elsif (!$authorsignoff) {
-                       WARN("NO_AUTHOR_SIGN_OFF",
-                            "Missing Signed-off-by: line by nominal patch author '$author'\n");
+               } elsif ($authorsignoff != 1) {
+                       # authorsignoff values:
+                       # 0 -> missing sign off
+                       # 1 -> sign off identical
+                       # 2 -> names and addresses match, comments mismatch
+                       # 3 -> addresses match, names different
+                       # 4 -> names match, addresses different
+                       # 5 -> names match, addresses excluding subaddress details (refer RFC 5233) match
+
+                       my $sob_msg = "'From: $author' != 'Signed-off-by: $author_sob'";
+
+                       if ($authorsignoff == 0) {
+                               ERROR("NO_AUTHOR_SIGN_OFF",
+                                     "Missing Signed-off-by: line by nominal patch author '$author'\n");
+                       } elsif ($authorsignoff == 2) {
+                               CHK("FROM_SIGN_OFF_MISMATCH",
+                                   "From:/Signed-off-by: email comments mismatch: $sob_msg\n");
+                       } elsif ($authorsignoff == 3) {
+                               WARN("FROM_SIGN_OFF_MISMATCH",
+                                    "From:/Signed-off-by: email name mismatch: $sob_msg\n");
+                       } elsif ($authorsignoff == 4) {
+                               WARN("FROM_SIGN_OFF_MISMATCH",
+                                    "From:/Signed-off-by: email address mismatch: $sob_msg\n");
+                       } elsif ($authorsignoff == 5) {
+                               WARN("FROM_SIGN_OFF_MISMATCH",
+                                    "From:/Signed-off-by: email subaddress mismatch: $sob_msg\n");
+                       }
                }
        }