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