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 calls to RTE_LOG in libraries 57 awk -v FOLDERS="lib" \ 58 -v EXPRESSIONS="RTE_LOG\\\(" \ 59 -v RET_ON_FAIL=1 \ 60 -v MESSAGE='Prefer RTE_LOG_LINE' \ 61 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 62 "$1" || res=1 63 64 # refrain from new calls to RTE_LOG in drivers (but leave some leeway for base drivers) 65 awk -v FOLDERS="drivers" \ 66 -v SKIP_FILES='osdep.h$' \ 67 -v EXPRESSIONS="RTE_LOG\\\( RTE_LOG_DP\\\( rte_log\\\(" \ 68 -v RET_ON_FAIL=1 \ 69 -v MESSAGE='Prefer RTE_LOG_LINE/RTE_LOG_DP_LINE' \ 70 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 71 "$1" || res=1 72 73 # no output on stdout or stderr 74 awk -v FOLDERS="lib drivers" \ 75 -v EXPRESSIONS="\\\<printf\\\> \\\<fprintf\\\(stdout, \\\<fprintf\\\(stderr," \ 76 -v RET_ON_FAIL=1 \ 77 -v MESSAGE='Writing to stdout or stderr' \ 78 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 79 "$1" || res=1 80 81 # refrain from new additions of rte_panic() and rte_exit() 82 # multiple folders and expressions are separated by spaces 83 awk -v FOLDERS="lib drivers" \ 84 -v EXPRESSIONS="rte_panic\\\( rte_exit\\\(" \ 85 -v RET_ON_FAIL=1 \ 86 -v MESSAGE='Using rte_panic/rte_exit' \ 87 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 88 "$1" || res=1 89 90 # refrain from using compiler attribute without defining a common macro 91 awk -v FOLDERS="lib drivers app examples" \ 92 -v SKIP_FILES='lib/eal/include/rte_common.h' \ 93 -v EXPRESSIONS="__attribute__" \ 94 -v RET_ON_FAIL=1 \ 95 -v MESSAGE='Using compiler attribute directly' \ 96 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 97 "$1" || res=1 98 99 # check %l or %ll format specifier 100 awk -v FOLDERS='lib drivers app examples' \ 101 -v EXPRESSIONS='%ll*[xud]' \ 102 -v RET_ON_FAIL=1 \ 103 -v MESSAGE='Using %l format, prefer %PRI*64 if type is [u]int64_t' \ 104 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 105 "$1" || res=1 106 107 # refrain from new additions of 16/32/64 bits rte_atomicNN_xxx() 108 awk -v FOLDERS="lib drivers app examples" \ 109 -v EXPRESSIONS="rte_atomic[0-9][0-9]_.*\\\(" \ 110 -v RET_ON_FAIL=1 \ 111 -v MESSAGE='Using rte_atomicNN_xxx' \ 112 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 113 "$1" || res=1 114 115 # refrain from new additions of rte_smp_[r/w]mb() 116 awk -v FOLDERS="lib drivers app examples" \ 117 -v EXPRESSIONS="rte_smp_(r|w)?mb\\\(" \ 118 -v RET_ON_FAIL=1 \ 119 -v MESSAGE='Using rte_smp_[r/w]mb' \ 120 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 121 "$1" || res=1 122 123 # refrain from using compiler __sync_xxx builtins 124 awk -v FOLDERS="lib drivers app examples" \ 125 -v EXPRESSIONS="__sync_.*\\\(" \ 126 -v RET_ON_FAIL=1 \ 127 -v MESSAGE='Using __sync_xxx builtins' \ 128 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 129 "$1" || res=1 130 131 # refrain from using compiler __rte_atomic_thread_fence() 132 # It should be avoided on x86 for SMP case. 133 awk -v FOLDERS="lib drivers app examples" \ 134 -v EXPRESSIONS="__rte_atomic_thread_fence\\\(" \ 135 -v RET_ON_FAIL=1 \ 136 -v MESSAGE='Using __rte_atomic_thread_fence, prefer rte_atomic_thread_fence' \ 137 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 138 "$1" || res=1 139 140 # refrain from using compiler __atomic_xxx builtins 141 awk -v FOLDERS="lib drivers app examples" \ 142 -v EXPRESSIONS="__atomic_.*\\\( __ATOMIC_(RELAXED|CONSUME|ACQUIRE|RELEASE|ACQ_REL|SEQ_CST)" \ 143 -v RET_ON_FAIL=1 \ 144 -v MESSAGE='Using __atomic_xxx/__ATOMIC_XXX built-ins, prefer rte_atomic_xxx/rte_memory_order_xxx' \ 145 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 146 "$1" || res=1 147 148 # refrain from using some pthread functions 149 awk -v FOLDERS="lib drivers app examples" \ 150 -v EXPRESSIONS="pthread_(create|join|detach|set(_?name_np|affinity_np)|attr_set(inheritsched|schedpolicy))\\\(" \ 151 -v RET_ON_FAIL=1 \ 152 -v MESSAGE='Using pthread functions, prefer rte_thread' \ 153 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 154 "$1" || res=1 155 156 # forbid use of __reserved which is a reserved keyword in Windows system headers 157 awk -v FOLDERS="lib drivers app examples" \ 158 -v EXPRESSIONS='\\<__reserved\\>' \ 159 -v RET_ON_FAIL=1 \ 160 -v MESSAGE='Using __reserved' \ 161 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 162 "$1" || res=1 163 164 # forbid use of __alignof__ 165 awk -v FOLDERS="lib drivers app examples" \ 166 -v EXPRESSIONS='\\<__alignof__\\>' \ 167 -v RET_ON_FAIL=1 \ 168 -v MESSAGE='Using __alignof__, prefer C11 alignof' \ 169 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 170 "$1" || res=1 171 172 # forbid use of __typeof__ 173 awk -v FOLDERS="lib drivers app examples" \ 174 -v EXPRESSIONS='\\<__typeof__\\>' \ 175 -v RET_ON_FAIL=1 \ 176 -v MESSAGE='Using __typeof__, prefer typeof' \ 177 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 178 "$1" || res=1 179 180 # forbid use of compiler __builtin_* 181 awk -v FOLDERS="lib drivers app examples" \ 182 -v SKIP_FILES='lib/eal/ drivers/.*/base/ drivers/.*osdep.h$' \ 183 -v EXPRESSIONS='\\<__builtin_' \ 184 -v RET_ON_FAIL=1 \ 185 -v MESSAGE='Using __builtin helpers, prefer EAL macros' \ 186 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 187 "$1" || res=1 188 189 # forbid inclusion of Linux header for PCI constants 190 awk -v FOLDERS="lib drivers app examples" \ 191 -v EXPRESSIONS='include.*linux/pci_regs\\.h' \ 192 -v RET_ON_FAIL=1 \ 193 -v MESSAGE='Using linux/pci_regs.h, prefer rte_pci.h' \ 194 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 195 "$1" || res=1 196 197 # forbid use of variadic argument pack extension in macros 198 awk -v FOLDERS="lib drivers app examples" \ 199 -v EXPRESSIONS='#[[:space:]]*define.*[^(,[:space:]]\\.\\.\\.[[:space:]]*)' \ 200 -v RET_ON_FAIL=1 \ 201 -v MESSAGE='Do not use variadic argument pack in macros' \ 202 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 203 "$1" || res=1 204 205 # forbid use of experimental build flag except in examples 206 awk -v FOLDERS='lib drivers app' \ 207 -v EXPRESSIONS='-DALLOW_EXPERIMENTAL_API allow_experimental_apis' \ 208 -v RET_ON_FAIL=1 \ 209 -v MESSAGE='Using experimental build flag for in-tree compilation' \ 210 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 211 "$1" || res=1 212 213 # refrain from using RTE_LOG_REGISTER for drivers and libs 214 awk -v FOLDERS='lib drivers' \ 215 -v EXPRESSIONS='\\<RTE_LOG_REGISTER\\>' \ 216 -v RET_ON_FAIL=1 \ 217 -v MESSAGE='Using RTE_LOG_REGISTER, prefer RTE_LOG_REGISTER_(DEFAULT|SUFFIX)' \ 218 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 219 "$1" || res=1 220 221 # forbid non-internal thread in drivers and libs 222 awk -v FOLDERS='lib drivers' \ 223 -v EXPRESSIONS="rte_thread_(set_name|create_control)\\\(" \ 224 -v RET_ON_FAIL=1 \ 225 -v MESSAGE='Prefer rte_thread_(set_prefixed_name|create_internal_control)' \ 226 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 227 "$1" || res=1 228 229 # forbid rte_ symbols in cnxk base driver 230 awk -v FOLDERS='drivers/common/cnxk/roc_*' \ 231 -v SKIP_FILES='roc_platform*' \ 232 -v EXPRESSIONS="rte_ RTE_" \ 233 -v RET_ON_FAIL=1 \ 234 -v MESSAGE='Use plt_ symbols instead of rte_ API in cnxk base driver' \ 235 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 236 "$1" || res=1 237 238 # forbid inclusion of driver specific headers in apps and examples 239 awk -v FOLDERS='app examples' \ 240 -v EXPRESSIONS='include.*_driver\\.h include.*_pmd\\.h' \ 241 -v RET_ON_FAIL=1 \ 242 -v MESSAGE='Using driver specific headers in applications' \ 243 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 244 "$1" || res=1 245 246 # prevent addition of tests not in one of our test suites 247 awk -v FOLDERS='app/test' \ 248 -v EXPRESSIONS='REGISTER_TEST_COMMAND' \ 249 -v RET_ON_FAIL=1 \ 250 -v MESSAGE='Using REGISTER_TEST_COMMAND instead of REGISTER_<suite_name>_TEST' \ 251 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 252 "$1" || res=1 253 254 # SVG must be included with wildcard extension to allow conversion 255 awk -v FOLDERS='doc' \ 256 -v EXPRESSIONS='::[[:space:]]*[^[:space:]]*\\.svg' \ 257 -v RET_ON_FAIL=1 \ 258 -v MESSAGE='Using explicit .svg extension instead of .*' \ 259 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 260 "$1" || res=1 261 262 # links must prefer https over http 263 awk -v FOLDERS='doc' \ 264 -v EXPRESSIONS='http://.*dpdk.org' \ 265 -v RET_ON_FAIL=1 \ 266 -v MESSAGE='Using non https link to dpdk.org' \ 267 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 268 "$1" || res=1 269 270 # prefer Sphinx references for internal documentation 271 awk -v FOLDERS='doc' \ 272 -v EXPRESSIONS='//doc.dpdk.org/guides/' \ 273 -v RET_ON_FAIL=1 \ 274 -v MESSAGE='Using explicit URL to doc.dpdk.org, prefer :ref: or :doc:' \ 275 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 276 "$1" || res=1 277 278 # '// XXX is not set' must be preferred over '#undef XXX' 279 awk -v FOLDERS='config/rte_config.h' \ 280 -v EXPRESSIONS='#undef' \ 281 -v RET_ON_FAIL=1 \ 282 -v MESSAGE='Using "#undef XXX", prefer "// XXX is not set"' \ 283 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 284 "$1" || res=1 285 286 return $res 287} 288 289check_experimental_tags() { # <patch> 290 res=0 291 292 cat "$1" |awk ' 293 BEGIN { 294 current_file = ""; 295 ret = 0; 296 } 297 /^+++ b\// { 298 current_file = $2; 299 } 300 /^+.*__rte_experimental/ { 301 if (current_file ~ ".c$" ) { 302 print "Please only put __rte_experimental tags in " \ 303 "headers ("current_file")"; 304 ret = 1; 305 } 306 if ($1 != "+__rte_experimental" || $2 != "") { 307 print "__rte_experimental must appear alone on the line" \ 308 " immediately preceding the return type of a function." 309 ret = 1; 310 } 311 } 312 END { 313 exit ret; 314 }' || res=1 315 316 return $res 317} 318 319check_internal_tags() { # <patch> 320 res=0 321 322 cat "$1" |awk ' 323 BEGIN { 324 current_file = ""; 325 ret = 0; 326 } 327 /^+++ b\// { 328 current_file = $2; 329 } 330 /^+.*__rte_internal/ { 331 if (current_file ~ ".c$" ) { 332 print "Please only put __rte_internal tags in " \ 333 "headers ("current_file")"; 334 ret = 1; 335 } 336 if ($1 != "+__rte_internal" || $2 != "") { 337 print "__rte_internal must appear alone on the line" \ 338 " immediately preceding the return type of" \ 339 " a function." 340 ret = 1; 341 } 342 } 343 END { 344 exit ret; 345 }' || res=1 346 347 return $res 348} 349 350check_aligned_attributes() { # <patch> 351 res=0 352 353 for token in __rte_aligned __rte_cache_aligned __rte_cache_min_aligned; do 354 if [ $(grep -E '^\+.*\<'$token'\>' "$1" | \ 355 grep -vE '\<(struct|union)[[:space:]]*'$token'\>' | \ 356 wc -l) != 0 ]; then 357 echo "Please use $token only for struct or union types alignment." 358 res=1 359 fi 360 done 361 362 return $res 363} 364 365check_release_notes() { # <patch> 366 rel_notes_prefix=doc/guides/rel_notes/release_ 367 IFS=. read year month release < VERSION 368 current_rel_notes=${rel_notes_prefix}${year}_${month}.rst 369 370 ! grep -e '^--- a/'$rel_notes_prefix -e '^+++ b/'$rel_notes_prefix "$1" | 371 grep -v $current_rel_notes 372} 373 374number=0 375range='origin/main..' 376quiet=false 377verbose=false 378while getopts hn:qr:v ARG ; do 379 case $ARG in 380 n ) number=$OPTARG ;; 381 q ) quiet=true ;; 382 r ) range=$OPTARG ;; 383 v ) verbose=true ;; 384 h ) print_usage ; exit 0 ;; 385 ? ) print_usage ; exit 1 ;; 386 esac 387done 388shift $(($OPTIND - 1)) 389 390if [ ! -f "$DPDK_CHECKPATCH_PATH" ] || [ ! -x "$DPDK_CHECKPATCH_PATH" ] ; then 391 default_path="/lib/modules/$(uname -r)/source/scripts/checkpatch.pl" 392 if [ -f "$default_path" ] && [ -x "$default_path" ] ; then 393 DPDK_CHECKPATCH_PATH="$default_path" 394 else 395 print_usage >&2 396 echo 397 echo 'Cannot execute DPDK_CHECKPATCH_PATH' >&2 398 exit 1 399 fi 400fi 401 402print_headline() { # <title> 403 printf '\n### %s\n\n' "$1" 404 headline_printed=true 405} 406 407total=0 408status=0 409 410check () { # <patch-file> <commit> 411 local ret=0 412 local subject='' 413 headline_printed=false 414 415 total=$(($total + 1)) 416 if [ -n "$1" ] ; then 417 tmpinput=$1 418 else 419 tmpinput=$(mktemp -t dpdk.checkpatches.XXXXXX) 420 trap "rm -f '$tmpinput'" INT 421 422 if [ -n "$2" ] ; then 423 git format-patch --find-renames \ 424 --no-stat --stdout -1 $commit > "$tmpinput" 425 else 426 cat > "$tmpinput" 427 fi 428 fi 429 430 # Subject can be on 2 lines 431 subject=$(sed '/^Subject: */!d;s///;N;s,\n[[:space:]]\+, ,;s,\n.*,,;q' "$tmpinput") 432 ! $verbose || print_headline "$subject" 433 434 ! $verbose || printf 'Running checkpatch.pl:\n' 435 report=$($DPDK_CHECKPATCH_PATH $options "$tmpinput" 2>/dev/null) 436 if [ $? -ne 0 ] ; then 437 $headline_printed || print_headline "$subject" 438 printf '%s\n' "$report" | sed -n '1,/^total:.*lines checked$/p' 439 ret=1 440 fi 441 442 ! $verbose || printf '\nChecking API additions/removals:\n' 443 report=$($VALIDATE_NEW_API "$tmpinput") 444 if [ $? -ne 0 ] ; then 445 $headline_printed || print_headline "$subject" 446 printf '%s\n' "$report" 447 ret=1 448 fi 449 450 ! $verbose || printf '\nChecking forbidden tokens additions:\n' 451 report=$(check_forbidden_additions "$tmpinput") 452 if [ $? -ne 0 ] ; then 453 $headline_printed || print_headline "$subject" 454 printf '%s\n' "$report" 455 ret=1 456 fi 457 458 ! $verbose || printf '\nChecking __rte_experimental tags:\n' 459 report=$(check_experimental_tags "$tmpinput") 460 if [ $? -ne 0 ] ; then 461 $headline_printed || print_headline "$subject" 462 printf '%s\n' "$report" 463 ret=1 464 fi 465 466 ! $verbose || printf '\nChecking __rte_internal tags:\n' 467 report=$(check_internal_tags "$tmpinput") 468 if [ $? -ne 0 ] ; then 469 $headline_printed || print_headline "$subject" 470 printf '%s\n' "$report" 471 ret=1 472 fi 473 474 ! $verbose || printf '\nChecking alignment attributes:\n' 475 report=$(check_aligned_attributes "$tmpinput") 476 if [ $? -ne 0 ] ; then 477 $headline_printed || print_headline "$subject" 478 printf '%s\n' "$report" 479 ret=1 480 fi 481 482 ! $verbose || printf '\nChecking release notes updates:\n' 483 report=$(check_release_notes "$tmpinput") 484 if [ $? -ne 0 ] ; then 485 $headline_printed || print_headline "$subject" 486 printf '%s\n' "$report" 487 ret=1 488 fi 489 490 if [ "$tmpinput" != "$1" ]; then 491 rm -f "$tmpinput" 492 trap - INT 493 fi 494 [ $ret -eq 0 ] && return 0 495 496 status=$(($status + 1)) 497} 498 499if [ -n "$1" ] ; then 500 for patch in "$@" ; do 501 check "$patch" '' 502 done 503elif [ ! -t 0 ] ; then # stdin 504 check '' '' 505else 506 if [ $number -eq 0 ] ; then 507 commits=$(git rev-list --reverse $range) 508 else 509 commits=$(git rev-list --reverse --max-count=$number HEAD) 510 fi 511 for commit in $commits ; do 512 check '' $commit 513 done 514fi 515pass=$(($total - $status)) 516$quiet || printf '\n%d/%d valid patch' $pass $total 517$quiet || [ $pass -le 1 ] || printf 'es' 518$quiet || printf '\n' 519exit $status 520