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