1#! /bin/sh 2# SPDX-License-Identifier: BSD-3-Clause 3# Copyright 2015 6WIND S.A. 4 5# Load config options: 6# - DPDK_CHECKPATCH_PATH 7# - DPDK_CHECKPATCH_CODESPELL 8# - DPDK_CHECKPATCH_LINE_LENGTH 9# - DPDK_CHECKPATCH_OPTIONS 10. $(dirname $(readlink -f $0))/load-devel-config 11 12VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.sh 13 14# Enable codespell by default. This can be overwritten from a config file. 15# Codespell can also be enabled by setting DPDK_CHECKPATCH_CODESPELL to a valid path 16# to a dictionary.txt file if dictionary.txt is not in the default location. 17codespell=${DPDK_CHECKPATCH_CODESPELL:-enable} 18length=${DPDK_CHECKPATCH_LINE_LENGTH:-100} 19 20# override default Linux options 21options="--no-tree" 22if [ "$codespell" = "enable" ] ; then 23 options="$options --codespell" 24elif [ -f "$codespell" ] ; then 25 options="$options --codespell" 26 options="$options --codespellfile $codespell" 27fi 28options="$options --max-line-length=$length" 29options="$options --show-types" 30options="$options --ignore=LINUX_VERSION_CODE,ENOSYS,\ 31FILE_PATH_CHANGES,MAINTAINERS_STYLE,SPDX_LICENSE_TAG,\ 32VOLATILE,PREFER_PACKED,PREFER_ALIGNED,PREFER_PRINTF,STRLCPY,\ 33PREFER_KERNEL_TYPES,PREFER_FALLTHROUGH,BIT_MACRO,CONST_STRUCT,\ 34SPLIT_STRING,LONG_LINE_STRING,C99_COMMENT_TOLERANCE,\ 35LINE_SPACING,PARENTHESIS_ALIGNMENT,NETWORKING_BLOCK_COMMENT_STYLE,\ 36NEW_TYPEDEFS,COMPARISON_TO_NULL,AVOID_BUG" 37options="$options $DPDK_CHECKPATCH_OPTIONS" 38 39print_usage () { 40 cat <<- END_OF_HELP 41 usage: $(basename $0) [-h] [-q] [-v] [-nX|-r range|patch1 [patch2] ...] 42 43 Run Linux kernel checkpatch.pl with DPDK options. 44 The environment variable DPDK_CHECKPATCH_PATH can be set, if not we will 45 try to find the script in the sources of the currently running kernel. 46 47 The patches to check can be from stdin, files specified on the command line, 48 latest git commits limited with -n option, or commits in the git range 49 specified with -r option (default: "origin/main.."). 50 END_OF_HELP 51} 52 53check_forbidden_additions() { # <patch> 54 res=0 55 56 # refrain from new additions of rte_panic() and rte_exit() 57 # multiple folders and expressions are separated by spaces 58 awk -v FOLDERS="lib drivers" \ 59 -v EXPRESSIONS="rte_panic\\\( rte_exit\\\(" \ 60 -v RET_ON_FAIL=1 \ 61 -v MESSAGE='Using rte_panic/rte_exit' \ 62 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 63 "$1" || res=1 64 65 # refrain from using compiler attribute without defining a common macro 66 awk -v FOLDERS="lib drivers app examples" \ 67 -v EXPRESSIONS="__attribute__" \ 68 -v RET_ON_FAIL=1 \ 69 -v MESSAGE='Using compiler attribute directly' \ 70 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 71 "$1" || res=1 72 73 # check %l or %ll format specifier 74 awk -v FOLDERS='lib drivers app examples' \ 75 -v EXPRESSIONS='%ll*[xud]' \ 76 -v RET_ON_FAIL=1 \ 77 -v MESSAGE='Using %l format, prefer %PRI*64 if type is [u]int64_t' \ 78 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 79 "$1" || res=1 80 81 # refrain from new additions of 16/32/64 bits rte_atomicNN_xxx() 82 awk -v FOLDERS="lib drivers app examples" \ 83 -v EXPRESSIONS="rte_atomic[0-9][0-9]_.*\\\(" \ 84 -v RET_ON_FAIL=1 \ 85 -v MESSAGE='Using rte_atomicNN_xxx' \ 86 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 87 "$1" || res=1 88 89 # refrain from new additions of rte_smp_[r/w]mb() 90 awk -v FOLDERS="lib drivers app examples" \ 91 -v EXPRESSIONS="rte_smp_(r|w)?mb\\\(" \ 92 -v RET_ON_FAIL=1 \ 93 -v MESSAGE='Using rte_smp_[r/w]mb' \ 94 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 95 "$1" || res=1 96 97 # refrain from using compiler __sync_xxx builtins 98 awk -v FOLDERS="lib drivers app examples" \ 99 -v EXPRESSIONS="__sync_.*\\\(" \ 100 -v RET_ON_FAIL=1 \ 101 -v MESSAGE='Using __sync_xxx builtins' \ 102 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 103 "$1" || res=1 104 105 # refrain from using compiler __atomic_thread_fence() 106 # It should be avoided on x86 for SMP case. 107 awk -v FOLDERS="lib drivers app examples" \ 108 -v EXPRESSIONS="__atomic_thread_fence\\\(" \ 109 -v RET_ON_FAIL=1 \ 110 -v MESSAGE='Using __atomic_thread_fence' \ 111 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 112 "$1" || res=1 113 114 # refrain from using compiler __atomic_{add,and,nand,or,sub,xor}_fetch() 115 awk -v FOLDERS="lib drivers app examples" \ 116 -v EXPRESSIONS="__atomic_(add|and|nand|or|sub|xor)_fetch\\\(" \ 117 -v RET_ON_FAIL=1 \ 118 -v MESSAGE='Using __atomic_op_fetch, prefer __atomic_fetch_op' \ 119 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 120 "$1" || res=1 121 122 # refrain from using some pthread functions 123 awk -v FOLDERS="lib drivers app examples" \ 124 -v EXPRESSIONS="pthread_(create|join|detach|set(_?name_np|affinity_np)|attr_set(inheritsched|schedpolicy))\\\(" \ 125 -v RET_ON_FAIL=1 \ 126 -v MESSAGE='Using pthread functions, prefer rte_thread' \ 127 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 128 "$1" || res=1 129 130 # forbid use of __reserved which is a reserved keyword in Windows system headers 131 awk -v FOLDERS="lib drivers app examples" \ 132 -v EXPRESSIONS='\\<__reserved\\>' \ 133 -v RET_ON_FAIL=1 \ 134 -v MESSAGE='Using __reserved' \ 135 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 136 "$1" || res=1 137 138 # forbid use of non abstracted bit count operations 139 awk -v FOLDERS="lib drivers app examples" \ 140 -v EXPRESSIONS='\\<__builtin_(clz|clzll|ctz|ctzll|popcount|popcountll)\\>' \ 141 -v RET_ON_FAIL=1 \ 142 -v MESSAGE='Using __builtin helpers for bit count operations' \ 143 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 144 "$1" || res=1 145 146 # forbid inclusion of Linux header for PCI constants 147 awk -v FOLDERS="lib drivers app examples" \ 148 -v EXPRESSIONS='include.*linux/pci_regs\\.h' \ 149 -v RET_ON_FAIL=1 \ 150 -v MESSAGE='Using linux/pci_regs.h, prefer rte_pci.h' \ 151 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 152 "$1" || res=1 153 154 # forbid use of experimental build flag except in examples 155 awk -v FOLDERS='lib drivers app' \ 156 -v EXPRESSIONS='-DALLOW_EXPERIMENTAL_API allow_experimental_apis' \ 157 -v RET_ON_FAIL=1 \ 158 -v MESSAGE='Using experimental build flag for in-tree compilation' \ 159 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 160 "$1" || res=1 161 162 # refrain from using RTE_LOG_REGISTER for drivers and libs 163 awk -v FOLDERS='lib drivers' \ 164 -v EXPRESSIONS='\\<RTE_LOG_REGISTER\\>' \ 165 -v RET_ON_FAIL=1 \ 166 -v MESSAGE='Using RTE_LOG_REGISTER, prefer RTE_LOG_REGISTER_(DEFAULT|SUFFIX)' \ 167 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 168 "$1" || res=1 169 170 # forbid non-internal thread in drivers and libs 171 awk -v FOLDERS='lib drivers' \ 172 -v EXPRESSIONS="rte_thread_(set_name|create_control)\\\(" \ 173 -v RET_ON_FAIL=1 \ 174 -v MESSAGE='Prefer rte_thread_(set_prefixed_name|create_internal_control)' \ 175 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 176 "$1" || res=1 177 178 # forbid inclusion of driver specific headers in apps and examples 179 awk -v FOLDERS='app examples' \ 180 -v EXPRESSIONS='include.*_driver\\.h include.*_pmd\\.h' \ 181 -v RET_ON_FAIL=1 \ 182 -v MESSAGE='Using driver specific headers in applications' \ 183 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 184 "$1" || res=1 185 186 # SVG must be included with wildcard extension to allow conversion 187 awk -v FOLDERS='doc' \ 188 -v EXPRESSIONS='::[[:space:]]*[^[:space:]]*\\.svg' \ 189 -v RET_ON_FAIL=1 \ 190 -v MESSAGE='Using explicit .svg extension instead of .*' \ 191 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 192 "$1" || res=1 193 194 # links must prefer https over http 195 awk -v FOLDERS='doc' \ 196 -v EXPRESSIONS='http://.*dpdk.org' \ 197 -v RET_ON_FAIL=1 \ 198 -v MESSAGE='Using non https link to dpdk.org' \ 199 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 200 "$1" || res=1 201 202 # prefer Sphinx references for internal documentation 203 awk -v FOLDERS='doc' \ 204 -v EXPRESSIONS='//doc.dpdk.org/guides/' \ 205 -v RET_ON_FAIL=1 \ 206 -v MESSAGE='Using explicit URL to doc.dpdk.org, prefer :ref: or :doc:' \ 207 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 208 "$1" || res=1 209 210 # '// XXX is not set' must be preferred over '#undef XXX' 211 awk -v FOLDERS='config/rte_config.h' \ 212 -v EXPRESSIONS='#undef' \ 213 -v RET_ON_FAIL=1 \ 214 -v MESSAGE='Using "#undef XXX", prefer "// XXX is not set"' \ 215 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 216 "$1" || res=1 217 218 return $res 219} 220 221check_experimental_tags() { # <patch> 222 res=0 223 224 cat "$1" |awk ' 225 BEGIN { 226 current_file = ""; 227 ret = 0; 228 } 229 /^+++ b\// { 230 current_file = $2; 231 } 232 /^+.*__rte_experimental/ { 233 if (current_file ~ ".c$" ) { 234 print "Please only put __rte_experimental tags in " \ 235 "headers ("current_file")"; 236 ret = 1; 237 } 238 if ($1 != "+__rte_experimental" || $2 != "") { 239 print "__rte_experimental must appear alone on the line" \ 240 " immediately preceding the return type of a function." 241 ret = 1; 242 } 243 } 244 END { 245 exit ret; 246 }' || res=1 247 248 return $res 249} 250 251check_internal_tags() { # <patch> 252 res=0 253 254 cat "$1" |awk ' 255 BEGIN { 256 current_file = ""; 257 ret = 0; 258 } 259 /^+++ b\// { 260 current_file = $2; 261 } 262 /^+.*__rte_internal/ { 263 if (current_file ~ ".c$" ) { 264 print "Please only put __rte_internal tags in " \ 265 "headers ("current_file")"; 266 ret = 1; 267 } 268 if ($1 != "+__rte_internal" || $2 != "") { 269 print "__rte_internal must appear alone on the line" \ 270 " immediately preceding the return type of" \ 271 " a function." 272 ret = 1; 273 } 274 } 275 END { 276 exit ret; 277 }' || res=1 278 279 return $res 280} 281 282check_release_notes() { # <patch> 283 rel_notes_prefix=doc/guides/rel_notes/release_ 284 IFS=. read year month release < VERSION 285 current_rel_notes=${rel_notes_prefix}${year}_${month}.rst 286 287 ! grep -e '^--- a/'$rel_notes_prefix -e '^+++ b/'$rel_notes_prefix "$1" | 288 grep -v $current_rel_notes 289} 290 291number=0 292range='origin/main..' 293quiet=false 294verbose=false 295while getopts hn:qr:v ARG ; do 296 case $ARG in 297 n ) number=$OPTARG ;; 298 q ) quiet=true ;; 299 r ) range=$OPTARG ;; 300 v ) verbose=true ;; 301 h ) print_usage ; exit 0 ;; 302 ? ) print_usage ; exit 1 ;; 303 esac 304done 305shift $(($OPTIND - 1)) 306 307if [ ! -f "$DPDK_CHECKPATCH_PATH" ] || [ ! -x "$DPDK_CHECKPATCH_PATH" ] ; then 308 default_path="/lib/modules/$(uname -r)/source/scripts/checkpatch.pl" 309 if [ -f "$default_path" ] && [ -x "$default_path" ] ; then 310 DPDK_CHECKPATCH_PATH="$default_path" 311 else 312 print_usage >&2 313 echo 314 echo 'Cannot execute DPDK_CHECKPATCH_PATH' >&2 315 exit 1 316 fi 317fi 318 319print_headline() { # <title> 320 printf '\n### %s\n\n' "$1" 321 headline_printed=true 322} 323 324total=0 325status=0 326 327check () { # <patch-file> <commit> 328 local ret=0 329 local subject='' 330 headline_printed=false 331 332 total=$(($total + 1)) 333 if [ -n "$1" ] ; then 334 tmpinput=$1 335 else 336 tmpinput=$(mktemp -t dpdk.checkpatches.XXXXXX) 337 trap "rm -f '$tmpinput'" INT 338 339 if [ -n "$2" ] ; then 340 git format-patch --find-renames \ 341 --no-stat --stdout -1 $commit > "$tmpinput" 342 else 343 cat > "$tmpinput" 344 fi 345 fi 346 347 # Subject can be on 2 lines 348 subject=$(sed '/^Subject: */!d;s///;N;s,\n[[:space:]]\+, ,;s,\n.*,,;q' "$tmpinput") 349 ! $verbose || print_headline "$subject" 350 351 ! $verbose || printf 'Running checkpatch.pl:\n' 352 report=$($DPDK_CHECKPATCH_PATH $options "$tmpinput" 2>/dev/null) 353 if [ $? -ne 0 ] ; then 354 $headline_printed || print_headline "$subject" 355 printf '%s\n' "$report" | sed -n '1,/^total:.*lines checked$/p' 356 ret=1 357 fi 358 359 ! $verbose || printf '\nChecking API additions/removals:\n' 360 report=$($VALIDATE_NEW_API "$tmpinput") 361 if [ $? -ne 0 ] ; then 362 $headline_printed || print_headline "$subject" 363 printf '%s\n' "$report" 364 ret=1 365 fi 366 367 ! $verbose || printf '\nChecking forbidden tokens additions:\n' 368 report=$(check_forbidden_additions "$tmpinput") 369 if [ $? -ne 0 ] ; then 370 $headline_printed || print_headline "$subject" 371 printf '%s\n' "$report" 372 ret=1 373 fi 374 375 ! $verbose || printf '\nChecking __rte_experimental tags:\n' 376 report=$(check_experimental_tags "$tmpinput") 377 if [ $? -ne 0 ] ; then 378 $headline_printed || print_headline "$subject" 379 printf '%s\n' "$report" 380 ret=1 381 fi 382 383 ! $verbose || printf '\nChecking __rte_internal tags:\n' 384 report=$(check_internal_tags "$tmpinput") 385 if [ $? -ne 0 ] ; then 386 $headline_printed || print_headline "$subject" 387 printf '%s\n' "$report" 388 ret=1 389 fi 390 391 ! $verbose || printf '\nChecking release notes updates:\n' 392 report=$(check_release_notes "$tmpinput") 393 if [ $? -ne 0 ] ; then 394 $headline_printed || print_headline "$subject" 395 printf '%s\n' "$report" 396 ret=1 397 fi 398 399 if [ "$tmpinput" != "$1" ]; then 400 rm -f "$tmpinput" 401 trap - INT 402 fi 403 [ $ret -eq 0 ] && return 0 404 405 status=$(($status + 1)) 406} 407 408if [ -n "$1" ] ; then 409 for patch in "$@" ; do 410 check "$patch" '' 411 done 412elif [ ! -t 0 ] ; then # stdin 413 check '' '' 414else 415 if [ $number -eq 0 ] ; then 416 commits=$(git rev-list --reverse $range) 417 else 418 commits=$(git rev-list --reverse --max-count=$number HEAD) 419 fi 420 for commit in $commits ; do 421 check '' $commit 422 done 423fi 424pass=$(($total - $status)) 425$quiet || printf '\n%d/%d valid patch' $pass $total 426$quiet || [ $pass -le 1 ] || printf 'es' 427$quiet || printf '\n' 428exit $status 429