Merge tag 'nfs-for-5.13-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
[platform/kernel/linux-starfive.git] / scripts / dev-needs.sh
1 #! /bin/sh
2 # SPDX-License-Identifier: GPL-2.0
3 # Copyright (c) 2020, Google LLC. All rights reserved.
4 # Author: Saravana Kannan <saravanak@google.com>
5
6 function help() {
7         cat << EOF
8 Usage: $(basename $0) [-c|-d|-m|-f] [filter options] <list of devices>
9
10 This script needs to be run on the target device once it has booted to a
11 shell.
12
13 The script takes as input a list of one or more device directories under
14 /sys/devices and then lists the probe dependency chain (suppliers and
15 parents) of these devices. It does a breadth first search of the dependency
16 chain, so the last entry in the output is close to the root of the
17 dependency chain.
18
19 By default it lists the full path to the devices under /sys/devices.
20
21 It also takes an optional modifier flag as the first parameter to change
22 what information is listed in the output. If the requested information is
23 not available, the device name is printed.
24
25   -c    lists the compatible string of the dependencies
26   -d    lists the driver name of the dependencies that have probed
27   -m    lists the module name of the dependencies that have a module
28   -f    list the firmware node path of the dependencies
29   -g    list the dependencies as edges and nodes for graphviz
30   -t    list the dependencies as edges for tsort
31
32 The filter options provide a way to filter out some dependencies:
33   --allow-no-driver     By default dependencies that don't have a driver
34                         attached are ignored. This is to avoid following
35                         device links to "class" devices that are created
36                         when the consumer probes (as in, not a probe
37                         dependency). If you want to follow these links
38                         anyway, use this flag.
39
40   --exclude-devlinks    Don't follow device links when tracking probe
41                         dependencies.
42
43   --exclude-parents     Don't follow parent devices when tracking probe
44                         dependencies.
45
46 EOF
47 }
48
49 function dev_to_detail() {
50         local i=0
51         while [ $i -lt ${#OUT_LIST[@]} ]
52         do
53                 local C=${OUT_LIST[i]}
54                 local S=${OUT_LIST[i+1]}
55                 local D="'$(detail_chosen $C $S)'"
56                 if [ ! -z "$D" ]
57                 then
58                         # This weirdness is needed to work with toybox when
59                         # using the -t option.
60                         printf '%05u\t%s\n' ${i} "$D" | tr -d \'
61                 fi
62                 i=$((i+2))
63         done
64 }
65
66 function already_seen() {
67         local i=0
68         while [ $i -lt ${#OUT_LIST[@]} ]
69         do
70                 if [ "$1" = "${OUT_LIST[$i]}" ]
71                 then
72                         # if-statement treats 0 (no-error) as true
73                         return 0
74                 fi
75                 i=$(($i+2))
76         done
77
78         # if-statement treats 1 (error) as false
79         return 1
80 }
81
82 # Return 0 (no-error/true) if parent was added
83 function add_parent() {
84
85         if [ ${ALLOW_PARENTS} -eq 0 ]
86         then
87                 return 1
88         fi
89
90         local CON=$1
91         # $CON could be a symlink path. So, we need to find the real path and
92         # then go up one level to find the real parent.
93         local PARENT=$(realpath $CON/..)
94
95         while [ ! -e ${PARENT}/driver ]
96         do
97                 if [ "$PARENT" = "/sys/devices" ]
98                 then
99                         return 1
100                 fi
101                 PARENT=$(realpath $PARENT/..)
102         done
103
104         CONSUMERS+=($PARENT)
105         OUT_LIST+=(${CON} ${PARENT})
106         return 0
107 }
108
109 # Return 0 (no-error/true) if one or more suppliers were added
110 function add_suppliers() {
111         local CON=$1
112         local RET=1
113
114         if [ ${ALLOW_DEVLINKS} -eq 0 ]
115         then
116                 return 1
117         fi
118
119         SUPPLIER_LINKS=$(ls -1d $CON/supplier:* 2>/dev/null)
120         for SL in $SUPPLIER_LINKS;
121         do
122                 SYNC_STATE=$(cat $SL/sync_state_only)
123
124                 # sync_state_only links are proxy dependencies.
125                 # They can also have cycles. So, don't follow them.
126                 if [ "$SYNC_STATE" != '0' ]
127                 then
128                         continue
129                 fi
130
131                 SUPPLIER=$(realpath $SL/supplier)
132
133                 if [ ! -e $SUPPLIER/driver -a ${ALLOW_NO_DRIVER} -eq 0 ]
134                 then
135                         continue
136                 fi
137
138                 CONSUMERS+=($SUPPLIER)
139                 OUT_LIST+=(${CON} ${SUPPLIER})
140                 RET=0
141         done
142
143         return $RET
144 }
145
146 function detail_compat() {
147         f=$1/of_node/compatible
148         if [ -e $f ]
149         then
150                 echo -n $(cat $f)
151         else
152                 echo -n $1
153         fi
154 }
155
156 function detail_module() {
157         f=$1/driver/module
158         if [ -e $f ]
159         then
160                 echo -n $(basename $(realpath $f))
161         else
162                 echo -n $1
163         fi
164 }
165
166 function detail_driver() {
167         f=$1/driver
168         if [ -e $f ]
169         then
170                 echo -n $(basename $(realpath $f))
171         else
172                 echo -n $1
173         fi
174 }
175
176 function detail_fwnode() {
177         f=$1/firmware_node
178         if [ ! -e $f ]
179         then
180                 f=$1/of_node
181         fi
182
183         if [ -e $f ]
184         then
185                 echo -n $(realpath $f)
186         else
187                 echo -n $1
188         fi
189 }
190
191 function detail_graphviz() {
192         if [ "$2" != "ROOT" ]
193         then
194                 echo -n "\"$(basename $2)\"->\"$(basename $1)\""
195         else
196                 echo -n "\"$(basename $1)\""
197         fi
198 }
199
200 function detail_tsort() {
201         echo -n "\"$2\" \"$1\""
202 }
203
204 function detail_device() { echo -n $1; }
205
206 alias detail=detail_device
207 ALLOW_NO_DRIVER=0
208 ALLOW_DEVLINKS=1
209 ALLOW_PARENTS=1
210
211 while [ $# -gt 0 ]
212 do
213         ARG=$1
214         case $ARG in
215                 --help)
216                         help
217                         exit 0
218                         ;;
219                 -c)
220                         alias detail=detail_compat
221                         ;;
222                 -m)
223                         alias detail=detail_module
224                         ;;
225                 -d)
226                         alias detail=detail_driver
227                         ;;
228                 -f)
229                         alias detail=detail_fwnode
230                         ;;
231                 -g)
232                         alias detail=detail_graphviz
233                         ;;
234                 -t)
235                         alias detail=detail_tsort
236                         ;;
237                 --allow-no-driver)
238                         ALLOW_NO_DRIVER=1
239                         ;;
240                 --exclude-devlinks)
241                         ALLOW_DEVLINKS=0
242                         ;;
243                 --exclude-parents)
244                         ALLOW_PARENTS=0
245                         ;;
246                 *)
247                         # Stop at the first argument that's not an option.
248                         break
249                         ;;
250         esac
251         shift
252 done
253
254 function detail_chosen() {
255         detail $1 $2
256 }
257
258 if [ $# -eq 0 ]
259 then
260         help
261         exit 1
262 fi
263
264 CONSUMERS=($@)
265 OUT_LIST=()
266
267 # Do a breadth first, non-recursive tracking of suppliers. The parent is also
268 # considered a "supplier" as a device can't probe without its parent.
269 i=0
270 while [ $i -lt ${#CONSUMERS[@]} ]
271 do
272         CONSUMER=$(realpath ${CONSUMERS[$i]})
273         i=$(($i+1))
274
275         if already_seen ${CONSUMER}
276         then
277                 continue
278         fi
279
280         # If this is not a device with a driver, we don't care about its
281         # suppliers.
282         if [ ! -e ${CONSUMER}/driver -a ${ALLOW_NO_DRIVER} -eq 0 ]
283         then
284                 continue
285         fi
286
287         ROOT=1
288
289         # Add suppliers to CONSUMERS list and output the consumer details.
290         #
291         # We don't need to worry about a cycle in the dependency chain causing
292         # infinite loops. That's because the kernel doesn't allow cycles in
293         # device links unless it's a sync_state_only device link. And we ignore
294         # sync_state_only device links inside add_suppliers.
295         if add_suppliers ${CONSUMER}
296         then
297                 ROOT=0
298         fi
299
300         if add_parent ${CONSUMER}
301         then
302                 ROOT=0
303         fi
304
305         if [ $ROOT -eq 1 ]
306         then
307                 OUT_LIST+=(${CONSUMER} "ROOT")
308         fi
309 done
310
311 # Can NOT combine sort and uniq using sort -suk2 because stable sort in toybox
312 # isn't really stable.
313 dev_to_detail | sort -k2 -k1 | uniq -f 1 | sort | cut -f2-
314
315 exit 0