| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: if_media.c,v 1.49 2020/01/20 19:35:39 thorpej Exp $ */ | | 1 | /* $NetBSD: if_media.c,v 1.50 2020/01/31 00:49:18 thorpej Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 1998 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 1998 The NetBSD Foundation, Inc. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation | | 7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, | | 8 | * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, |
9 | * NASA Ames Research Center. | | 9 | * NASA Ames Research Center. |
10 | * | | 10 | * |
11 | * Redistribution and use in source and binary forms, with or without | | 11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions | | 12 | * modification, are permitted provided that the following conditions |
13 | * are met: | | 13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright | | 14 | * 1. Redistributions of source code must retain the above copyright |
| @@ -66,58 +66,56 @@ | | | @@ -66,58 +66,56 @@ |
66 | | | 66 | |
67 | /* | | 67 | /* |
68 | * BSD/OS-compatible network interface media selection. | | 68 | * BSD/OS-compatible network interface media selection. |
69 | * | | 69 | * |
70 | * Where it is safe to do so, this code strays slightly from the BSD/OS | | 70 | * Where it is safe to do so, this code strays slightly from the BSD/OS |
71 | * design. Software which uses the API (device drivers, basically) | | 71 | * design. Software which uses the API (device drivers, basically) |
72 | * shouldn't notice any difference. | | 72 | * shouldn't notice any difference. |
73 | * | | 73 | * |
74 | * Many thanks to Matt Thomas for providing the information necessary | | 74 | * Many thanks to Matt Thomas for providing the information necessary |
75 | * to implement this interface. | | 75 | * to implement this interface. |
76 | */ | | 76 | */ |
77 | | | 77 | |
78 | #include <sys/cdefs.h> | | 78 | #include <sys/cdefs.h> |
79 | __KERNEL_RCSID(0, "$NetBSD: if_media.c,v 1.49 2020/01/20 19:35:39 thorpej Exp $"); | | 79 | __KERNEL_RCSID(0, "$NetBSD: if_media.c,v 1.50 2020/01/31 00:49:18 thorpej Exp $"); |
80 | | | 80 | |
81 | #include <sys/param.h> | | 81 | #include <sys/param.h> |
82 | #include <sys/systm.h> | | 82 | #include <sys/systm.h> |
83 | #include <sys/errno.h> | | 83 | #include <sys/errno.h> |
84 | #include <sys/ioctl.h> | | 84 | #include <sys/ioctl.h> |
85 | #include <sys/socket.h> | | 85 | #include <sys/socket.h> |
86 | #include <sys/malloc.h> | | 86 | #include <sys/kmem.h> |
87 | | | 87 | |
88 | #include <net/if.h> | | 88 | #include <net/if.h> |
89 | #include <net/if_media.h> | | 89 | #include <net/if_media.h> |
90 | #include <net/netisr.h> | | 90 | #include <net/netisr.h> |
91 | | | 91 | |
92 | static void ifmedia_status(struct ifmedia *, struct ifnet *, | | 92 | static void ifmedia_status(struct ifmedia *, struct ifnet *, |
93 | struct ifmediareq *); | | 93 | struct ifmediareq *); |
94 | static int ifmedia_ioctl_locked(struct ifnet *, struct ifreq *, | | 94 | static int ifmedia_ioctl_locked(struct ifnet *, struct ifreq *, |
95 | struct ifmedia *, u_long); | | 95 | struct ifmedia *, u_long); |
96 | | | 96 | |
97 | /* | | 97 | /* |
98 | * Compile-time options: | | 98 | * Compile-time options: |
99 | * IFMEDIA_DEBUG: | | 99 | * IFMEDIA_DEBUG: |
100 | * Turn on implementation-level debug printfs. | | 100 | * Turn on implementation-level debug printfs. |
101 | * Useful for debugging newly-ported drivers. | | 101 | * Useful for debugging newly-ported drivers. |
102 | */ | | 102 | */ |
103 | | | 103 | |
104 | #ifdef IFMEDIA_DEBUG | | 104 | #ifdef IFMEDIA_DEBUG |
105 | int ifmedia_debug = 0; | | 105 | int ifmedia_debug = 0; |
106 | static void ifmedia_printword(int); | | 106 | static void ifmedia_printword(int); |
107 | #endif | | 107 | #endif |
108 | | | 108 | |
109 | MALLOC_DEFINE(M_IFMEDIA, "ifmedia", "interface media state"); | | | |
110 | | | | |
111 | /* | | 109 | /* |
112 | * Initialize if_media struct for a specific interface instance. | | 110 | * Initialize if_media struct for a specific interface instance. |
113 | */ | | 111 | */ |
114 | void | | 112 | void |
115 | ifmedia_init(struct ifmedia *ifm, int dontcare_mask, | | 113 | ifmedia_init(struct ifmedia *ifm, int dontcare_mask, |
116 | ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback) | | 114 | ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback) |
117 | { | | 115 | { |
118 | | | 116 | |
119 | TAILQ_INIT(&ifm->ifm_list); | | 117 | TAILQ_INIT(&ifm->ifm_list); |
120 | ifm->ifm_cur = NULL; | | 118 | ifm->ifm_cur = NULL; |
121 | ifm->ifm_media = IFM_NONE; | | 119 | ifm->ifm_media = IFM_NONE; |
122 | ifm->ifm_mask = dontcare_mask; /* IF don't-care bits */ | | 120 | ifm->ifm_mask = dontcare_mask; /* IF don't-care bits */ |
123 | ifm->ifm_change = change_callback; | | 121 | ifm->ifm_change = change_callback; |
| @@ -152,27 +150,27 @@ ifmedia_add(struct ifmedia *ifm, int mwo | | | @@ -152,27 +150,27 @@ ifmedia_add(struct ifmedia *ifm, int mwo |
152 | struct ifmedia_entry *entry; | | 150 | struct ifmedia_entry *entry; |
153 | | | 151 | |
154 | #ifdef IFMEDIA_DEBUG | | 152 | #ifdef IFMEDIA_DEBUG |
155 | if (ifmedia_debug) { | | 153 | if (ifmedia_debug) { |
156 | if (ifm == NULL) { | | 154 | if (ifm == NULL) { |
157 | printf("ifmedia_add: null ifm\n"); | | 155 | printf("ifmedia_add: null ifm\n"); |
158 | return; | | 156 | return; |
159 | } | | 157 | } |
160 | printf("Adding entry for "); | | 158 | printf("Adding entry for "); |
161 | ifmedia_printword(mword); | | 159 | ifmedia_printword(mword); |
162 | } | | 160 | } |
163 | #endif | | 161 | #endif |
164 | | | 162 | |
165 | entry = malloc(sizeof(*entry), M_IFMEDIA, M_WAITOK); | | 163 | entry = kmem_zalloc(sizeof(*entry), KM_SLEEP); |
166 | entry->ifm_media = mword; | | 164 | entry->ifm_media = mword; |
167 | entry->ifm_data = data; | | 165 | entry->ifm_data = data; |
168 | entry->ifm_aux = aux; | | 166 | entry->ifm_aux = aux; |
169 | TAILQ_INSERT_TAIL(&ifm->ifm_list, entry, ifm_list); | | 167 | TAILQ_INSERT_TAIL(&ifm->ifm_list, entry, ifm_list); |
170 | } | | 168 | } |
171 | | | 169 | |
172 | /* | | 170 | /* |
173 | * Add an array of media configurations to the list of | | 171 | * Add an array of media configurations to the list of |
174 | * supported media for a specific interface instance. | | 172 | * supported media for a specific interface instance. |
175 | */ | | 173 | */ |
176 | void | | 174 | void |
177 | ifmedia_list_add(struct ifmedia *ifm, struct ifmedia_entry *lp, int count) | | 175 | ifmedia_list_add(struct ifmedia *ifm, struct ifmedia_entry *lp, int count) |
178 | { | | 176 | { |
| @@ -225,26 +223,42 @@ ifmedia_set(struct ifmedia *ifm, int tar | | | @@ -225,26 +223,42 @@ ifmedia_set(struct ifmedia *ifm, int tar |
225 | } | | 223 | } |
226 | ifm->ifm_cur = match; | | 224 | ifm->ifm_cur = match; |
227 | | | 225 | |
228 | #ifdef IFMEDIA_DEBUG | | 226 | #ifdef IFMEDIA_DEBUG |
229 | if (ifmedia_debug) { | | 227 | if (ifmedia_debug) { |
230 | printf("ifmedia_set: target "); | | 228 | printf("ifmedia_set: target "); |
231 | ifmedia_printword(target); | | 229 | ifmedia_printword(target); |
232 | printf("ifmedia_set: setting to "); | | 230 | printf("ifmedia_set: setting to "); |
233 | ifmedia_printword(ifm->ifm_cur->ifm_media); | | 231 | ifmedia_printword(ifm->ifm_cur->ifm_media); |
234 | } | | 232 | } |
235 | #endif | | 233 | #endif |
236 | } | | 234 | } |
237 | | | 235 | |
| | | 236 | static int |
| | | 237 | ifmedia_getwords(struct ifmedia * const ifm, int *words, int maxwords) |
| | | 238 | { |
| | | 239 | struct ifmedia_entry *ep; |
| | | 240 | int nwords = 0; |
| | | 241 | |
| | | 242 | TAILQ_FOREACH(ep, &ifm->ifm_list, ifm_list) { |
| | | 243 | if (words != NULL && nwords < maxwords) { |
| | | 244 | words[nwords] = ep->ifm_media; |
| | | 245 | } |
| | | 246 | nwords++; |
| | | 247 | } |
| | | 248 | |
| | | 249 | return nwords; |
| | | 250 | } |
| | | 251 | |
238 | /* | | 252 | /* |
239 | * Device-independent media ioctl support function. | | 253 | * Device-independent media ioctl support function. |
240 | */ | | 254 | */ |
241 | static int | | 255 | static int |
242 | ifmedia_ioctl_locked(struct ifnet *ifp, struct ifreq *ifr, struct ifmedia *ifm, | | 256 | ifmedia_ioctl_locked(struct ifnet *ifp, struct ifreq *ifr, struct ifmedia *ifm, |
243 | u_long cmd) | | 257 | u_long cmd) |
244 | { | | 258 | { |
245 | struct ifmedia_entry *match; | | 259 | struct ifmedia_entry *match; |
246 | struct ifmediareq *ifmr = (struct ifmediareq *)ifr; | | 260 | struct ifmediareq *ifmr = (struct ifmediareq *)ifr; |
247 | int error = 0; | | 261 | int error = 0; |
248 | | | 262 | |
249 | if (ifp == NULL || ifr == NULL || ifm == NULL) | | 263 | if (ifp == NULL || ifr == NULL || ifm == NULL) |
250 | return EINVAL; | | 264 | return EINVAL; |
| @@ -294,67 +308,57 @@ ifmedia_ioctl_locked(struct ifnet *ifp, | | | @@ -294,67 +308,57 @@ ifmedia_ioctl_locked(struct ifnet *ifp, |
294 | ifm->ifm_cur = match; | | 308 | ifm->ifm_cur = match; |
295 | ifm->ifm_media = newmedia; | | 309 | ifm->ifm_media = newmedia; |
296 | error = ifmedia_change(ifm, ifp); | | 310 | error = ifmedia_change(ifm, ifp); |
297 | if (error) { | | 311 | if (error) { |
298 | ifm->ifm_cur = oldentry; | | 312 | ifm->ifm_cur = oldentry; |
299 | ifm->ifm_media = oldmedia; | | 313 | ifm->ifm_media = oldmedia; |
300 | } | | 314 | } |
301 | break; | | 315 | break; |
302 | } | | 316 | } |
303 | | | 317 | |
304 | /* Get list of available media and current media on interface. */ | | 318 | /* Get list of available media and current media on interface. */ |
305 | case SIOCGIFMEDIA: | | 319 | case SIOCGIFMEDIA: |
306 | { | | 320 | { |
307 | struct ifmedia_entry *ep; | | 321 | int nwords1, nwords2; |
308 | size_t nwords; | | | |
309 | | | 322 | |
310 | if (ifmr->ifm_count < 0) | | 323 | if (ifmr->ifm_count < 0) |
311 | return EINVAL; | | 324 | return EINVAL; |
312 | | | 325 | |
313 | ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ? | | 326 | ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ? |
314 | ifm->ifm_cur->ifm_media : IFM_NONE; | | 327 | ifm->ifm_cur->ifm_media : IFM_NONE; |
315 | ifmr->ifm_mask = ifm->ifm_mask; | | 328 | ifmr->ifm_mask = ifm->ifm_mask; |
316 | ifmr->ifm_status = 0; | | 329 | ifmr->ifm_status = 0; |
317 | ifmedia_status(ifm, ifp, ifmr); | | 330 | ifmedia_status(ifm, ifp, ifmr); |
318 | | | 331 | |
319 | /* | | 332 | /* |
320 | * Count them so we know a-priori how much is the max we'll | | 333 | * Count them so we know a-priori how much is the max we'll |
321 | * need. | | 334 | * need. |
322 | */ | | 335 | */ |
323 | ep = TAILQ_FIRST(&ifm->ifm_list); | | 336 | nwords1 = nwords2 = ifmedia_getwords(ifm, NULL, 0); |
324 | for (nwords = 0; ep != NULL; ep = TAILQ_NEXT(ep, ifm_list)) | | | |
325 | nwords++; | | | |
326 | | | 337 | |
327 | if (ifmr->ifm_count != 0) { | | 338 | if (ifmr->ifm_count != 0) { |
328 | size_t count; | | 339 | int maxwords = MIN(nwords1, ifmr->ifm_count); |
329 | size_t minwords = nwords > (size_t)ifmr->ifm_count | | 340 | int *kptr = kmem_zalloc(maxwords * sizeof(int), |
330 | ? (size_t)ifmr->ifm_count : nwords; | | 341 | KM_SLEEP); |
331 | int *kptr = malloc(minwords * sizeof(int), M_TEMP, | | | |
332 | M_WAITOK); | | | |
333 | | | | |
334 | /* Get the media words from the interface's list. */ | | | |
335 | ep = TAILQ_FIRST(&ifm->ifm_list); | | | |
336 | for (count = 0; ep != NULL && count < minwords; | | | |
337 | ep = TAILQ_NEXT(ep, ifm_list), count++) | | | |
338 | kptr[count] = ep->ifm_media; | | | |
339 | | | 342 | |
| | | 343 | nwords2 = ifmedia_getwords(ifm, kptr, maxwords); |
340 | error = copyout(kptr, ifmr->ifm_ulist, | | 344 | error = copyout(kptr, ifmr->ifm_ulist, |
341 | minwords * sizeof(int)); | | 345 | maxwords * sizeof(int)); |
342 | if (error == 0 && ep != NULL) | | 346 | if (error == 0 && nwords2 > nwords1) |
343 | error = E2BIG; /* oops! */ | | 347 | error = E2BIG; /* oops! */ |
344 | free(kptr, M_TEMP); | | 348 | kmem_free(kptr, maxwords * sizeof(int)); |
345 | } | | 349 | } |
346 | /* Update with the real number */ | | 350 | /* Update with the real number */ |
347 | ifmr->ifm_count = nwords; | | 351 | ifmr->ifm_count = nwords2; |
348 | break; | | 352 | break; |
349 | } | | 353 | } |
350 | | | 354 | |
351 | default: | | 355 | default: |
352 | return EINVAL; | | 356 | return EINVAL; |
353 | } | | 357 | } |
354 | | | 358 | |
355 | return error; | | 359 | return error; |
356 | } | | 360 | } |
357 | | | 361 | |
358 | int | | 362 | int |
359 | ifmedia_ioctl(struct ifnet *ifp, struct ifreq *ifr, struct ifmedia *ifm, | | 363 | ifmedia_ioctl(struct ifnet *ifp, struct ifreq *ifr, struct ifmedia *ifm, |
360 | u_long cmd) | | 364 | u_long cmd) |
| @@ -410,27 +414,27 @@ ifmedia_match(struct ifmedia *ifm, u_int | | | @@ -410,27 +414,27 @@ ifmedia_match(struct ifmedia *ifm, u_int |
410 | | | 414 | |
411 | /* | | 415 | /* |
412 | * Delete all media for a given instance. | | 416 | * Delete all media for a given instance. |
413 | */ | | 417 | */ |
414 | void | | 418 | void |
415 | ifmedia_delete_instance(struct ifmedia *ifm, u_int inst) | | 419 | ifmedia_delete_instance(struct ifmedia *ifm, u_int inst) |
416 | { | | 420 | { |
417 | struct ifmedia_entry *ife, *nife; | | 421 | struct ifmedia_entry *ife, *nife; |
418 | | | 422 | |
419 | TAILQ_FOREACH_SAFE(ife, &ifm->ifm_list, ifm_list, nife) { | | 423 | TAILQ_FOREACH_SAFE(ife, &ifm->ifm_list, ifm_list, nife) { |
420 | if (inst == IFM_INST_ANY || | | 424 | if (inst == IFM_INST_ANY || |
421 | inst == IFM_INST(ife->ifm_media)) { | | 425 | inst == IFM_INST(ife->ifm_media)) { |
422 | TAILQ_REMOVE(&ifm->ifm_list, ife, ifm_list); | | 426 | TAILQ_REMOVE(&ifm->ifm_list, ife, ifm_list); |
423 | free(ife, M_IFMEDIA); | | 427 | kmem_free(ife, sizeof(*ife)); |
424 | } | | 428 | } |
425 | } | | 429 | } |
426 | if (inst == IFM_INST_ANY) { | | 430 | if (inst == IFM_INST_ANY) { |
427 | ifm->ifm_cur = NULL; | | 431 | ifm->ifm_cur = NULL; |
428 | ifm->ifm_media = IFM_NONE; | | 432 | ifm->ifm_media = IFM_NONE; |
429 | } | | 433 | } |
430 | } | | 434 | } |
431 | | | 435 | |
432 | void | | 436 | void |
433 | ifmedia_removeall(struct ifmedia *ifm) | | 437 | ifmedia_removeall(struct ifmedia *ifm) |
434 | { | | 438 | { |
435 | | | 439 | |
436 | ifmedia_delete_instance(ifm, IFM_INST_ANY); | | 440 | ifmedia_delete_instance(ifm, IFM_INST_ANY); |