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