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_release_notes() { # <patch> 340 rel_notes_prefix=doc/guides/rel_notes/release_ 341 IFS=. read year month release < VERSION 342 current_rel_notes=${rel_notes_prefix}${year}_${month}.rst 343 344 ! grep -e '^--- a/'$rel_notes_prefix -e '^+++ b/'$rel_notes_prefix "$1" | 345 grep -v $current_rel_notes 346} 347 348number=0 349range='origin/main..' 350quiet=false 351verbose=false 352while getopts hn:qr:v ARG ; do 353 case $ARG in 354 n ) number=$OPTARG ;; 355 q ) quiet=true ;; 356 r ) range=$OPTARG ;; 357 v ) verbose=true ;; 358 h ) print_usage ; exit 0 ;; 359 ? ) print_usage ; exit 1 ;; 360 esac 361done 362shift $(($OPTIND - 1)) 363 364if [ ! -f "$DPDK_CHECKPATCH_PATH" ] || [ ! -x "$DPDK_CHECKPATCH_PATH" ] ; then 365 default_path="/lib/modules/$(uname -r)/source/scripts/checkpatch.pl" 366 if [ -f "$default_path" ] && [ -x "$default_path" ] ; then 367 DPDK_CHECKPATCH_PATH="$default_path" 368 else 369 print_usage >&2 370 echo 371 echo 'Cannot execute DPDK_CHECKPATCH_PATH' >&2 372 exit 1 373 fi 374fi 375 376print_headline() { # <title> 377 printf '\n### %s\n\n' "$1" 378 headline_printed=true 379} 380 381total=0 382status=0 383 384check () { # <patch-file> <commit> 385 local ret=0 386 local subject='' 387 headline_printed=false 388 389 total=$(($total + 1)) 390 if [ -n "$1" ] ; then 391 tmpinput=$1 392 else 393 tmpinput=$(mktemp -t dpdk.checkpatches.XXXXXX) 394 trap "rm -f '$tmpinput'" INT 395 396 if [ -n "$2" ] ; then 397 git format-patch --find-renames \ 398 --no-stat --stdout -1 $commit > "$tmpinput" 399 else 400 cat > "$tmpinput" 401 fi 402 fi 403 404 # Subject can be on 2 lines 405 subject=$(sed '/^Subject: */!d;s///;N;s,\n[[:space:]]\+, ,;s,\n.*,,;q' "$tmpinput") 406 ! $verbose || print_headline "$subject" 407 408 ! $verbose || printf 'Running checkpatch.pl:\n' 409 report=$($DPDK_CHECKPATCH_PATH $options "$tmpinput" 2>/dev/null) 410 if [ $? -ne 0 ] ; then 411 $headline_printed || print_headline "$subject" 412 printf '%s\n' "$report" | sed -n '1,/^total:.*lines checked$/p' 413 ret=1 414 fi 415 416 ! $verbose || printf '\nChecking API additions/removals:\n' 417 report=$($VALIDATE_NEW_API "$tmpinput") 418 if [ $? -ne 0 ] ; then 419 $headline_printed || print_headline "$subject" 420 printf '%s\n' "$report" 421 ret=1 422 fi 423 424 ! $verbose || printf '\nChecking forbidden tokens additions:\n' 425 report=$(check_forbidden_additions "$tmpinput") 426 if [ $? -ne 0 ] ; then 427 $headline_printed || print_headline "$subject" 428 printf '%s\n' "$report" 429 ret=1 430 fi 431 432 ! $verbose || printf '\nChecking __rte_experimental tags:\n' 433 report=$(check_experimental_tags "$tmpinput") 434 if [ $? -ne 0 ] ; then 435 $headline_printed || print_headline "$subject" 436 printf '%s\n' "$report" 437 ret=1 438 fi 439 440 ! $verbose || printf '\nChecking __rte_internal tags:\n' 441 report=$(check_internal_tags "$tmpinput") 442 if [ $? -ne 0 ] ; then 443 $headline_printed || print_headline "$subject" 444 printf '%s\n' "$report" 445 ret=1 446 fi 447 448 ! $verbose || printf '\nChecking release notes updates:\n' 449 report=$(check_release_notes "$tmpinput") 450 if [ $? -ne 0 ] ; then 451 $headline_printed || print_headline "$subject" 452 printf '%s\n' "$report" 453 ret=1 454 fi 455 456 if [ "$tmpinput" != "$1" ]; then 457 rm -f "$tmpinput" 458 trap - INT 459 fi 460 [ $ret -eq 0 ] && return 0 461 462 status=$(($status + 1)) 463} 464 465if [ -n "$1" ] ; then 466 for patch in "$@" ; do 467 check "$patch" '' 468 done 469elif [ ! -t 0 ] ; then # stdin 470 check '' '' 471else 472 if [ $number -eq 0 ] ; then 473 commits=$(git rev-list --reverse $range) 474 else 475 commits=$(git rev-list --reverse --max-count=$number HEAD) 476 fi 477 for commit in $commits ; do 478 check '' $commit 479 done 480fi 481pass=$(($total - $status)) 482$quiet || printf '\n%d/%d valid patch' $pass $total 483$quiet || [ $pass -le 1 ] || printf 'es' 484$quiet || printf '\n' 485exit $status 486