DMI: Introduce dmi_first_match to make the interface more flexible

Some notebooks from HP have the problem that their BIOSes attempt to
spin down hard drives before entering ACPI system states S4 and S5.
This leads to a yo-yo effect during system power-off shutdown and the
last phase of hibernation when the disk is first spun down by the
kernel and then almost immediately turned on and off by the BIOS.
This, in turn, may result in shortening the disk's life times.

To prevent this from happening we can blacklist the affected systems
using DMI information. However, only the on-board controlles should
be blacklisted and their PCI slot numbers can be used for this
purpose. Unfortunately the existing interface for checking DMI
information of the system is not very convenient for this purpose,
because to use it, we would have to define special callback functions
or create a separate struct dmi_system_id table for each blacklisted
system.

To overcome this difficulty introduce a new function
dmi_first_match() returning a pointer to the first entry in an array
of struct dmi_system_id elements that matches the system DMI
information. Then, we can use this pointer to access the entry's
.driver_data field containing the additional information, such as
the PCI slot number, allowing us to do the desired blacklisting.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>

authored by Rafael J. Wysocki and committed by Jeff Garzik d7b1956f abfe2d7b

+55 -18
+54 -18
drivers/firmware/dmi_scan.c
··· 415 415 } 416 416 417 417 /** 418 + * dmi_matches - check if dmi_system_id structure matches system DMI data 419 + * @dmi: pointer to the dmi_system_id structure to check 420 + */ 421 + static bool dmi_matches(const struct dmi_system_id *dmi) 422 + { 423 + int i; 424 + 425 + WARN(!dmi_initialized, KERN_ERR "dmi check: not initialized yet.\n"); 426 + 427 + for (i = 0; i < ARRAY_SIZE(dmi->matches); i++) { 428 + int s = dmi->matches[i].slot; 429 + if (s == DMI_NONE) 430 + continue; 431 + if (dmi_ident[s] 432 + && strstr(dmi_ident[s], dmi->matches[i].substr)) 433 + continue; 434 + /* No match */ 435 + return false; 436 + } 437 + return true; 438 + } 439 + 440 + /** 418 441 * dmi_check_system - check system DMI data 419 442 * @list: array of dmi_system_id structures to match against 420 443 * All non-null elements of the list must match ··· 452 429 */ 453 430 int dmi_check_system(const struct dmi_system_id *list) 454 431 { 455 - int i, count = 0; 456 - const struct dmi_system_id *d = list; 432 + int count = 0; 433 + const struct dmi_system_id *d; 457 434 458 - WARN(!dmi_initialized, KERN_ERR "dmi check: not initialized yet.\n"); 459 - 460 - while (d->ident) { 461 - for (i = 0; i < ARRAY_SIZE(d->matches); i++) { 462 - int s = d->matches[i].slot; 463 - if (s == DMI_NONE) 464 - continue; 465 - if (dmi_ident[s] && strstr(dmi_ident[s], d->matches[i].substr)) 466 - continue; 467 - /* No match */ 468 - goto fail; 435 + for (d = list; d->ident; d++) 436 + if (dmi_matches(d)) { 437 + count++; 438 + if (d->callback && d->callback(d)) 439 + break; 469 440 } 470 - count++; 471 - if (d->callback && d->callback(d)) 472 - break; 473 - fail: d++; 474 - } 475 441 476 442 return count; 477 443 } 478 444 EXPORT_SYMBOL(dmi_check_system); 445 + 446 + /** 447 + * dmi_first_match - find dmi_system_id structure matching system DMI data 448 + * @list: array of dmi_system_id structures to match against 449 + * All non-null elements of the list must match 450 + * their slot's (field index's) data (i.e., each 451 + * list string must be a substring of the specified 452 + * DMI slot's string data) to be considered a 453 + * successful match. 454 + * 455 + * Walk the blacklist table until the first match is found. Return the 456 + * pointer to the matching entry or NULL if there's no match. 457 + */ 458 + const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list) 459 + { 460 + const struct dmi_system_id *d; 461 + 462 + for (d = list; d->ident; d++) 463 + if (dmi_matches(d)) 464 + return d; 465 + 466 + return NULL; 467 + } 468 + EXPORT_SYMBOL(dmi_first_match); 479 469 480 470 /** 481 471 * dmi_get_system_info - return DMI data value
+1
include/linux/dmi.h
··· 38 38 #ifdef CONFIG_DMI 39 39 40 40 extern int dmi_check_system(const struct dmi_system_id *list); 41 + const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list); 41 42 extern const char * dmi_get_system_info(int field); 42 43 extern const struct dmi_device * dmi_find_device(int type, const char *name, 43 44 const struct dmi_device *from);