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 non abstracted bit count operations 181 awk -v FOLDERS="lib drivers app examples" \ 182 -v EXPRESSIONS='\\<__builtin_(clz|clzll|ctz|ctzll|popcount|popcountll)\\>' \ 183 -v RET_ON_FAIL=1 \ 184 -v MESSAGE='Using __builtin helpers for bit count operations' \ 185 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 186 "$1" || res=1 187 188 # forbid inclusion of Linux header for PCI constants 189 awk -v FOLDERS="lib drivers app examples" \ 190 -v EXPRESSIONS='include.*linux/pci_regs\\.h' \ 191 -v RET_ON_FAIL=1 \ 192 -v MESSAGE='Using linux/pci_regs.h, prefer rte_pci.h' \ 193 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 194 "$1" || res=1 195 196 # forbid use of variadic argument pack extension in macros 197 awk -v FOLDERS="lib drivers app examples" \ 198 -v EXPRESSIONS='#[[:space:]]*define.*[^(,[:space:]]\\.\\.\\.[[:space:]]*)' \ 199 -v RET_ON_FAIL=1 \ 200 -v MESSAGE='Do not use variadic argument pack in macros' \ 201 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 202 "$1" || res=1 203 204 # forbid use of experimental build flag except in examples 205 awk -v FOLDERS='lib drivers app' \ 206 -v EXPRESSIONS='-DALLOW_EXPERIMENTAL_API allow_experimental_apis' \ 207 -v RET_ON_FAIL=1 \ 208 -v MESSAGE='Using experimental build flag for in-tree compilation' \ 209 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 210 "$1" || res=1 211 212 # refrain from using RTE_LOG_REGISTER for drivers and libs 213 awk -v FOLDERS='lib drivers' \ 214 -v EXPRESSIONS='\\<RTE_LOG_REGISTER\\>' \ 215 -v RET_ON_FAIL=1 \ 216 -v MESSAGE='Using RTE_LOG_REGISTER, prefer RTE_LOG_REGISTER_(DEFAULT|SUFFIX)' \ 217 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 218 "$1" || res=1 219 220 # forbid non-internal thread in drivers and libs 221 awk -v FOLDERS='lib drivers' \ 222 -v EXPRESSIONS="rte_thread_(set_name|create_control)\\\(" \ 223 -v RET_ON_FAIL=1 \ 224 -v MESSAGE='Prefer rte_thread_(set_prefixed_name|create_internal_control)' \ 225 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 226 "$1" || res=1 227 228 # forbid rte_ symbols in cnxk base driver 229 awk -v FOLDERS='drivers/common/cnxk/roc_*' \ 230 -v SKIP_FILES='roc_platform*' \ 231 -v EXPRESSIONS="rte_ RTE_" \ 232 -v RET_ON_FAIL=1 \ 233 -v MESSAGE='Use plt_ symbols instead of rte_ API in cnxk base driver' \ 234 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 235 "$1" || res=1 236 237 # forbid inclusion of driver specific headers in apps and examples 238 awk -v FOLDERS='app examples' \ 239 -v EXPRESSIONS='include.*_driver\\.h include.*_pmd\\.h' \ 240 -v RET_ON_FAIL=1 \ 241 -v MESSAGE='Using driver specific headers in applications' \ 242 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 243 "$1" || res=1 244 245 # prevent addition of tests not in one of our test suites 246 awk -v FOLDERS='app/test' \ 247 -v EXPRESSIONS='REGISTER_TEST_COMMAND' \ 248 -v RET_ON_FAIL=1 \ 249 -v MESSAGE='Using REGISTER_TEST_COMMAND instead of REGISTER_<suite_name>_TEST' \ 250 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 251 "$1" || res=1 252 253 # SVG must be included with wildcard extension to allow conversion 254 awk -v FOLDERS='doc' \ 255 -v EXPRESSIONS='::[[:space:]]*[^[:space:]]*\\.svg' \ 256 -v RET_ON_FAIL=1 \ 257 -v MESSAGE='Using explicit .svg extension instead of .*' \ 258 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 259 "$1" || res=1 260 261 # links must prefer https over http 262 awk -v FOLDERS='doc' \ 263 -v EXPRESSIONS='http://.*dpdk.org' \ 264 -v RET_ON_FAIL=1 \ 265 -v MESSAGE='Using non https link to dpdk.org' \ 266 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 267 "$1" || res=1 268 269 # prefer Sphinx references for internal documentation 270 awk -v FOLDERS='doc' \ 271 -v EXPRESSIONS='//doc.dpdk.org/guides/' \ 272 -v RET_ON_FAIL=1 \ 273 -v MESSAGE='Using explicit URL to doc.dpdk.org, prefer :ref: or :doc:' \ 274 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 275 "$1" || res=1 276 277 # '// XXX is not set' must be preferred over '#undef XXX' 278 awk -v FOLDERS='config/rte_config.h' \ 279 -v EXPRESSIONS='#undef' \ 280 -v RET_ON_FAIL=1 \ 281 -v MESSAGE='Using "#undef XXX", prefer "// XXX is not set"' \ 282 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 283 "$1" || res=1 284 285 return $res 286} 287 288check_experimental_tags() { # <patch> 289 res=0 290 291 cat "$1" |awk ' 292 BEGIN { 293 current_file = ""; 294 ret = 0; 295 } 296 /^+++ b\// { 297 current_file = $2; 298 } 299 /^+.*__rte_experimental/ { 300 if (current_file ~ ".c$" ) { 301 print "Please only put __rte_experimental tags in " \ 302 "headers ("current_file")"; 303 ret = 1; 304 } 305 if ($1 != "+__rte_experimental" || $2 != "") { 306 print "__rte_experimental must appear alone on the line" \ 307 " immediately preceding the return type of a function." 308 ret = 1; 309 } 310 } 311 END { 312 exit ret; 313 }' || res=1 314 315 return $res 316} 317 318check_internal_tags() { # <patch> 319 res=0 320 321 cat "$1" |awk ' 322 BEGIN { 323 current_file = ""; 324 ret = 0; 325 } 326 /^+++ b\// { 327 current_file = $2; 328 } 329 /^+.*__rte_internal/ { 330 if (current_file ~ ".c$" ) { 331 print "Please only put __rte_internal tags in " \ 332 "headers ("current_file")"; 333 ret = 1; 334 } 335 if ($1 != "+__rte_internal" || $2 != "") { 336 print "__rte_internal must appear alone on the line" \ 337 " immediately preceding the return type of" \ 338 " a function." 339 ret = 1; 340 } 341 } 342 END { 343 exit ret; 344 }' || res=1 345 346 return $res 347} 348 349check_aligned_attributes() { # <patch> 350 res=0 351 352 for token in __rte_aligned __rte_cache_aligned __rte_cache_min_aligned; do 353 if [ $(grep -E '^\+.*\<'$token'\>' "$1" | \ 354 grep -vE '\<(struct|union)[[:space:]]*'$token'\>' | \ 355 wc -l) != 0 ]; then 356 echo "Please use $token only for struct or union types alignment." 357 res=1 358 fi 359 done 360 361 return $res 362} 363 364check_release_notes() { # <patch> 365 rel_notes_prefix=doc/guides/rel_notes/release_ 366 IFS=. read year month release < VERSION 367 current_rel_notes=${rel_notes_prefix}${year}_${month}.rst 368 369 ! grep -e '^--- a/'$rel_notes_prefix -e '^+++ b/'$rel_notes_prefix "$1" | 370 grep -v $current_rel_notes 371} 372 373number=0 374range='origin/main..' 375quiet=false 376verbose=false 377while getopts hn:qr:v ARG ; do 378 case $ARG in 379 n ) number=$OPTARG ;; 380 q ) quiet=true ;; 381 r ) range=$OPTARG ;; 382 v ) verbose=true ;; 383 h ) print_usage ; exit 0 ;; 384 ? ) print_usage ; exit 1 ;; 385 esac 386done 387shift $(($OPTIND - 1)) 388 389if [ ! -f "$DPDK_CHECKPATCH_PATH" ] || [ ! -x "$DPDK_CHECKPATCH_PATH" ] ; then 390 default_path="/lib/modules/$(uname -r)/source/scripts/checkpatch.pl" 391 if [ -f "$default_path" ] && [ -x "$default_path" ] ; then 392 DPDK_CHECKPATCH_PATH="$default_path" 393 else 394 print_usage >&2 395 echo 396 echo 'Cannot execute DPDK_CHECKPATCH_PATH' >&2 397 exit 1 398 fi 399fi 400 401print_headline() { # <title> 402 printf '\n### %s\n\n' "$1" 403 headline_printed=true 404} 405 406total=0 407status=0 408 409check () { # <patch-file> <commit> 410 local ret=0 411 local subject='' 412 headline_printed=false 413 414 total=$(($total + 1)) 415 if [ -n "$1" ] ; then 416 tmpinput=$1 417 else 418 tmpinput=$(mktemp -t dpdk.checkpatches.XXXXXX) 419 trap "rm -f '$tmpinput'" INT 420 421 if [ -n "$2" ] ; then 422 git format-patch --find-renames \ 423 --no-stat --stdout -1 $commit > "$tmpinput" 424 else 425 cat > "$tmpinput" 426 fi 427 fi 428 429 # Subject can be on 2 lines 430 subject=$(sed '/^Subject: */!d;s///;N;s,\n[[:space:]]\+, ,;s,\n.*,,;q' "$tmpinput") 431 ! $verbose || print_headline "$subject" 432 433 ! $verbose || printf 'Running checkpatch.pl:\n' 434 report=$($DPDK_CHECKPATCH_PATH $options "$tmpinput" 2>/dev/null) 435 if [ $? -ne 0 ] ; then 436 $headline_printed || print_headline "$subject" 437 printf '%s\n' "$report" | sed -n '1,/^total:.*lines checked$/p' 438 ret=1 439 fi 440 441 ! $verbose || printf '\nChecking API additions/removals:\n' 442 report=$($VALIDATE_NEW_API "$tmpinput") 443 if [ $? -ne 0 ] ; then 444 $headline_printed || print_headline "$subject" 445 printf '%s\n' "$report" 446 ret=1 447 fi 448 449 ! $verbose || printf '\nChecking forbidden tokens additions:\n' 450 report=$(check_forbidden_additions "$tmpinput") 451 if [ $? -ne 0 ] ; then 452 $headline_printed || print_headline "$subject" 453 printf '%s\n' "$report" 454 ret=1 455 fi 456 457 ! $verbose || printf '\nChecking __rte_experimental tags:\n' 458 report=$(check_experimental_tags "$tmpinput") 459 if [ $? -ne 0 ] ; then 460 $headline_printed || print_headline "$subject" 461 printf '%s\n' "$report" 462 ret=1 463 fi 464 465 ! $verbose || printf '\nChecking __rte_internal tags:\n' 466 report=$(check_internal_tags "$tmpinput") 467 if [ $? -ne 0 ] ; then 468 $headline_printed || print_headline "$subject" 469 printf '%s\n' "$report" 470 ret=1 471 fi 472 473 ! $verbose || printf '\nChecking alignment attributes:\n' 474 report=$(check_aligned_attributes "$tmpinput") 475 if [ $? -ne 0 ] ; then 476 $headline_printed || print_headline "$subject" 477 printf '%s\n' "$report" 478 ret=1 479 fi 480 481 ! $verbose || printf '\nChecking release notes updates:\n' 482 report=$(check_release_notes "$tmpinput") 483 if [ $? -ne 0 ] ; then 484 $headline_printed || print_headline "$subject" 485 printf '%s\n' "$report" 486 ret=1 487 fi 488 489 if [ "$tmpinput" != "$1" ]; then 490 rm -f "$tmpinput" 491 trap - INT 492 fi 493 [ $ret -eq 0 ] && return 0 494 495 status=$(($status + 1)) 496} 497 498if [ -n "$1" ] ; then 499 for patch in "$@" ; do 500 check "$patch" '' 501 done 502elif [ ! -t 0 ] ; then # stdin 503 check '' '' 504else 505 if [ $number -eq 0 ] ; then 506 commits=$(git rev-list --reverse $range) 507 else 508 commits=$(git rev-list --reverse --max-count=$number HEAD) 509 fi 510 for commit in $commits ; do 511 check '' $commit 512 done 513fi 514pass=$(($total - $status)) 515$quiet || printf '\n%d/%d valid patch' $pass $total 516$quiet || [ $pass -le 1 ] || printf 'es' 517$quiet || printf '\n' 518exit $status 519