diff options
Diffstat (limited to 'drivers/firmware/iscsi_ibft.c')
-rw-r--r-- | drivers/firmware/iscsi_ibft.c | 698 |
1 files changed, 247 insertions, 451 deletions
diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c index b3ab24f9d78f..4f04ec0410a0 100644 --- a/drivers/firmware/iscsi_ibft.c +++ b/drivers/firmware/iscsi_ibft.c | |||
@@ -82,6 +82,7 @@ | |||
82 | #include <linux/string.h> | 82 | #include <linux/string.h> |
83 | #include <linux/types.h> | 83 | #include <linux/types.h> |
84 | #include <linux/acpi.h> | 84 | #include <linux/acpi.h> |
85 | #include <linux/iscsi_boot_sysfs.h> | ||
85 | 86 | ||
86 | #define IBFT_ISCSI_VERSION "0.5.0" | 87 | #define IBFT_ISCSI_VERSION "0.5.0" |
87 | #define IBFT_ISCSI_DATE "2010-Feb-25" | 88 | #define IBFT_ISCSI_DATE "2010-Feb-25" |
@@ -170,74 +171,6 @@ enum ibft_id { | |||
170 | }; | 171 | }; |
171 | 172 | ||
172 | /* | 173 | /* |
173 | * We do not support the other types, hence the usage of NULL. | ||
174 | * This maps to the enum ibft_id. | ||
175 | */ | ||
176 | static const char *ibft_id_names[] = | ||
177 | {NULL, NULL, "initiator", "ethernet%d", "target%d", NULL, NULL}; | ||
178 | |||
179 | /* | ||
180 | * The text attributes names for each of the kobjects. | ||
181 | */ | ||
182 | enum ibft_eth_properties_enum { | ||
183 | ibft_eth_index, | ||
184 | ibft_eth_flags, | ||
185 | ibft_eth_ip_addr, | ||
186 | ibft_eth_subnet_mask, | ||
187 | ibft_eth_origin, | ||
188 | ibft_eth_gateway, | ||
189 | ibft_eth_primary_dns, | ||
190 | ibft_eth_secondary_dns, | ||
191 | ibft_eth_dhcp, | ||
192 | ibft_eth_vlan, | ||
193 | ibft_eth_mac, | ||
194 | /* ibft_eth_pci_bdf - this is replaced by link to the device itself. */ | ||
195 | ibft_eth_hostname, | ||
196 | ibft_eth_end_marker, | ||
197 | }; | ||
198 | |||
199 | static const char *ibft_eth_properties[] = | ||
200 | {"index", "flags", "ip-addr", "subnet-mask", "origin", "gateway", | ||
201 | "primary-dns", "secondary-dns", "dhcp", "vlan", "mac", "hostname", | ||
202 | NULL}; | ||
203 | |||
204 | enum ibft_tgt_properties_enum { | ||
205 | ibft_tgt_index, | ||
206 | ibft_tgt_flags, | ||
207 | ibft_tgt_ip_addr, | ||
208 | ibft_tgt_port, | ||
209 | ibft_tgt_lun, | ||
210 | ibft_tgt_chap_type, | ||
211 | ibft_tgt_nic_assoc, | ||
212 | ibft_tgt_name, | ||
213 | ibft_tgt_chap_name, | ||
214 | ibft_tgt_chap_secret, | ||
215 | ibft_tgt_rev_chap_name, | ||
216 | ibft_tgt_rev_chap_secret, | ||
217 | ibft_tgt_end_marker, | ||
218 | }; | ||
219 | |||
220 | static const char *ibft_tgt_properties[] = | ||
221 | {"index", "flags", "ip-addr", "port", "lun", "chap-type", "nic-assoc", | ||
222 | "target-name", "chap-name", "chap-secret", "rev-chap-name", | ||
223 | "rev-chap-name-secret", NULL}; | ||
224 | |||
225 | enum ibft_initiator_properties_enum { | ||
226 | ibft_init_index, | ||
227 | ibft_init_flags, | ||
228 | ibft_init_isns_server, | ||
229 | ibft_init_slp_server, | ||
230 | ibft_init_pri_radius_server, | ||
231 | ibft_init_sec_radius_server, | ||
232 | ibft_init_initiator_name, | ||
233 | ibft_init_end_marker, | ||
234 | }; | ||
235 | |||
236 | static const char *ibft_initiator_properties[] = | ||
237 | {"index", "flags", "isns-server", "slp-server", "pri-radius-server", | ||
238 | "sec-radius-server", "initiator-name", NULL}; | ||
239 | |||
240 | /* | ||
241 | * The kobject and attribute structures. | 174 | * The kobject and attribute structures. |
242 | */ | 175 | */ |
243 | 176 | ||
@@ -249,29 +182,9 @@ struct ibft_kobject { | |||
249 | struct ibft_tgt *tgt; | 182 | struct ibft_tgt *tgt; |
250 | struct ibft_hdr *hdr; | 183 | struct ibft_hdr *hdr; |
251 | }; | 184 | }; |
252 | struct kobject kobj; | ||
253 | struct list_head node; | ||
254 | }; | 185 | }; |
255 | 186 | ||
256 | struct ibft_attribute { | 187 | static struct iscsi_boot_kset *boot_kset; |
257 | struct attribute attr; | ||
258 | ssize_t (*show) (struct ibft_kobject *entry, | ||
259 | struct ibft_attribute *attr, char *buf); | ||
260 | union { | ||
261 | struct ibft_initiator *initiator; | ||
262 | struct ibft_nic *nic; | ||
263 | struct ibft_tgt *tgt; | ||
264 | struct ibft_hdr *hdr; | ||
265 | }; | ||
266 | struct kobject *kobj; | ||
267 | int type; /* The enum of the type. This can be any value of: | ||
268 | ibft_eth_properties_enum, ibft_tgt_properties_enum, | ||
269 | or ibft_initiator_properties_enum. */ | ||
270 | struct list_head node; | ||
271 | }; | ||
272 | |||
273 | static LIST_HEAD(ibft_attr_list); | ||
274 | static LIST_HEAD(ibft_kobject_list); | ||
275 | 188 | ||
276 | static const char nulls[16]; | 189 | static const char nulls[16]; |
277 | 190 | ||
@@ -310,35 +223,27 @@ static ssize_t sprintf_string(char *str, int len, char *buf) | |||
310 | static int ibft_verify_hdr(char *t, struct ibft_hdr *hdr, int id, int length) | 223 | static int ibft_verify_hdr(char *t, struct ibft_hdr *hdr, int id, int length) |
311 | { | 224 | { |
312 | if (hdr->id != id) { | 225 | if (hdr->id != id) { |
313 | printk(KERN_ERR "iBFT error: We expected the " \ | 226 | printk(KERN_ERR "iBFT error: We expected the %s " \ |
314 | "field header.id to have %d but " \ | 227 | "field header.id to have %d but " \ |
315 | "found %d instead!\n", id, hdr->id); | 228 | "found %d instead!\n", t, id, hdr->id); |
316 | return -ENODEV; | 229 | return -ENODEV; |
317 | } | 230 | } |
318 | if (hdr->length != length) { | 231 | if (hdr->length != length) { |
319 | printk(KERN_ERR "iBFT error: We expected the " \ | 232 | printk(KERN_ERR "iBFT error: We expected the %s " \ |
320 | "field header.length to have %d but " \ | 233 | "field header.length to have %d but " \ |
321 | "found %d instead!\n", length, hdr->length); | 234 | "found %d instead!\n", t, length, hdr->length); |
322 | return -ENODEV; | 235 | return -ENODEV; |
323 | } | 236 | } |
324 | 237 | ||
325 | return 0; | 238 | return 0; |
326 | } | 239 | } |
327 | 240 | ||
328 | static void ibft_release(struct kobject *kobj) | ||
329 | { | ||
330 | struct ibft_kobject *ibft = | ||
331 | container_of(kobj, struct ibft_kobject, kobj); | ||
332 | kfree(ibft); | ||
333 | } | ||
334 | |||
335 | /* | 241 | /* |
336 | * Routines for parsing the iBFT data to be human readable. | 242 | * Routines for parsing the iBFT data to be human readable. |
337 | */ | 243 | */ |
338 | static ssize_t ibft_attr_show_initiator(struct ibft_kobject *entry, | 244 | static ssize_t ibft_attr_show_initiator(void *data, int type, char *buf) |
339 | struct ibft_attribute *attr, | ||
340 | char *buf) | ||
341 | { | 245 | { |
246 | struct ibft_kobject *entry = data; | ||
342 | struct ibft_initiator *initiator = entry->initiator; | 247 | struct ibft_initiator *initiator = entry->initiator; |
343 | void *ibft_loc = entry->header; | 248 | void *ibft_loc = entry->header; |
344 | char *str = buf; | 249 | char *str = buf; |
@@ -346,26 +251,26 @@ static ssize_t ibft_attr_show_initiator(struct ibft_kobject *entry, | |||
346 | if (!initiator) | 251 | if (!initiator) |
347 | return 0; | 252 | return 0; |
348 | 253 | ||
349 | switch (attr->type) { | 254 | switch (type) { |
350 | case ibft_init_index: | 255 | case ISCSI_BOOT_INI_INDEX: |
351 | str += sprintf(str, "%d\n", initiator->hdr.index); | 256 | str += sprintf(str, "%d\n", initiator->hdr.index); |
352 | break; | 257 | break; |
353 | case ibft_init_flags: | 258 | case ISCSI_BOOT_INI_FLAGS: |
354 | str += sprintf(str, "%d\n", initiator->hdr.flags); | 259 | str += sprintf(str, "%d\n", initiator->hdr.flags); |
355 | break; | 260 | break; |
356 | case ibft_init_isns_server: | 261 | case ISCSI_BOOT_INI_ISNS_SERVER: |
357 | str += sprintf_ipaddr(str, initiator->isns_server); | 262 | str += sprintf_ipaddr(str, initiator->isns_server); |
358 | break; | 263 | break; |
359 | case ibft_init_slp_server: | 264 | case ISCSI_BOOT_INI_SLP_SERVER: |
360 | str += sprintf_ipaddr(str, initiator->slp_server); | 265 | str += sprintf_ipaddr(str, initiator->slp_server); |
361 | break; | 266 | break; |
362 | case ibft_init_pri_radius_server: | 267 | case ISCSI_BOOT_INI_PRI_RADIUS_SERVER: |
363 | str += sprintf_ipaddr(str, initiator->pri_radius_server); | 268 | str += sprintf_ipaddr(str, initiator->pri_radius_server); |
364 | break; | 269 | break; |
365 | case ibft_init_sec_radius_server: | 270 | case ISCSI_BOOT_INI_SEC_RADIUS_SERVER: |
366 | str += sprintf_ipaddr(str, initiator->sec_radius_server); | 271 | str += sprintf_ipaddr(str, initiator->sec_radius_server); |
367 | break; | 272 | break; |
368 | case ibft_init_initiator_name: | 273 | case ISCSI_BOOT_INI_INITIATOR_NAME: |
369 | str += sprintf_string(str, initiator->initiator_name_len, | 274 | str += sprintf_string(str, initiator->initiator_name_len, |
370 | (char *)ibft_loc + | 275 | (char *)ibft_loc + |
371 | initiator->initiator_name_off); | 276 | initiator->initiator_name_off); |
@@ -377,10 +282,9 @@ static ssize_t ibft_attr_show_initiator(struct ibft_kobject *entry, | |||
377 | return str - buf; | 282 | return str - buf; |
378 | } | 283 | } |
379 | 284 | ||
380 | static ssize_t ibft_attr_show_nic(struct ibft_kobject *entry, | 285 | static ssize_t ibft_attr_show_nic(void *data, int type, char *buf) |
381 | struct ibft_attribute *attr, | ||
382 | char *buf) | ||
383 | { | 286 | { |
287 | struct ibft_kobject *entry = data; | ||
384 | struct ibft_nic *nic = entry->nic; | 288 | struct ibft_nic *nic = entry->nic; |
385 | void *ibft_loc = entry->header; | 289 | void *ibft_loc = entry->header; |
386 | char *str = buf; | 290 | char *str = buf; |
@@ -389,42 +293,42 @@ static ssize_t ibft_attr_show_nic(struct ibft_kobject *entry, | |||
389 | if (!nic) | 293 | if (!nic) |
390 | return 0; | 294 | return 0; |
391 | 295 | ||
392 | switch (attr->type) { | 296 | switch (type) { |
393 | case ibft_eth_index: | 297 | case ISCSI_BOOT_ETH_INDEX: |
394 | str += sprintf(str, "%d\n", nic->hdr.index); | 298 | str += sprintf(str, "%d\n", nic->hdr.index); |
395 | break; | 299 | break; |
396 | case ibft_eth_flags: | 300 | case ISCSI_BOOT_ETH_FLAGS: |
397 | str += sprintf(str, "%d\n", nic->hdr.flags); | 301 | str += sprintf(str, "%d\n", nic->hdr.flags); |
398 | break; | 302 | break; |
399 | case ibft_eth_ip_addr: | 303 | case ISCSI_BOOT_ETH_IP_ADDR: |
400 | str += sprintf_ipaddr(str, nic->ip_addr); | 304 | str += sprintf_ipaddr(str, nic->ip_addr); |
401 | break; | 305 | break; |
402 | case ibft_eth_subnet_mask: | 306 | case ISCSI_BOOT_ETH_SUBNET_MASK: |
403 | val = cpu_to_be32(~((1 << (32-nic->subnet_mask_prefix))-1)); | 307 | val = cpu_to_be32(~((1 << (32-nic->subnet_mask_prefix))-1)); |
404 | str += sprintf(str, "%pI4", &val); | 308 | str += sprintf(str, "%pI4", &val); |
405 | break; | 309 | break; |
406 | case ibft_eth_origin: | 310 | case ISCSI_BOOT_ETH_ORIGIN: |
407 | str += sprintf(str, "%d\n", nic->origin); | 311 | str += sprintf(str, "%d\n", nic->origin); |
408 | break; | 312 | break; |
409 | case ibft_eth_gateway: | 313 | case ISCSI_BOOT_ETH_GATEWAY: |
410 | str += sprintf_ipaddr(str, nic->gateway); | 314 | str += sprintf_ipaddr(str, nic->gateway); |
411 | break; | 315 | break; |
412 | case ibft_eth_primary_dns: | 316 | case ISCSI_BOOT_ETH_PRIMARY_DNS: |
413 | str += sprintf_ipaddr(str, nic->primary_dns); | 317 | str += sprintf_ipaddr(str, nic->primary_dns); |
414 | break; | 318 | break; |
415 | case ibft_eth_secondary_dns: | 319 | case ISCSI_BOOT_ETH_SECONDARY_DNS: |
416 | str += sprintf_ipaddr(str, nic->secondary_dns); | 320 | str += sprintf_ipaddr(str, nic->secondary_dns); |
417 | break; | 321 | break; |
418 | case ibft_eth_dhcp: | 322 | case ISCSI_BOOT_ETH_DHCP: |
419 | str += sprintf_ipaddr(str, nic->dhcp); | 323 | str += sprintf_ipaddr(str, nic->dhcp); |
420 | break; | 324 | break; |
421 | case ibft_eth_vlan: | 325 | case ISCSI_BOOT_ETH_VLAN: |
422 | str += sprintf(str, "%d\n", nic->vlan); | 326 | str += sprintf(str, "%d\n", nic->vlan); |
423 | break; | 327 | break; |
424 | case ibft_eth_mac: | 328 | case ISCSI_BOOT_ETH_MAC: |
425 | str += sprintf(str, "%pM\n", nic->mac); | 329 | str += sprintf(str, "%pM\n", nic->mac); |
426 | break; | 330 | break; |
427 | case ibft_eth_hostname: | 331 | case ISCSI_BOOT_ETH_HOSTNAME: |
428 | str += sprintf_string(str, nic->hostname_len, | 332 | str += sprintf_string(str, nic->hostname_len, |
429 | (char *)ibft_loc + nic->hostname_off); | 333 | (char *)ibft_loc + nic->hostname_off); |
430 | break; | 334 | break; |
@@ -435,10 +339,9 @@ static ssize_t ibft_attr_show_nic(struct ibft_kobject *entry, | |||
435 | return str - buf; | 339 | return str - buf; |
436 | }; | 340 | }; |
437 | 341 | ||
438 | static ssize_t ibft_attr_show_target(struct ibft_kobject *entry, | 342 | static ssize_t ibft_attr_show_target(void *data, int type, char *buf) |
439 | struct ibft_attribute *attr, | ||
440 | char *buf) | ||
441 | { | 343 | { |
344 | struct ibft_kobject *entry = data; | ||
442 | struct ibft_tgt *tgt = entry->tgt; | 345 | struct ibft_tgt *tgt = entry->tgt; |
443 | void *ibft_loc = entry->header; | 346 | void *ibft_loc = entry->header; |
444 | char *str = buf; | 347 | char *str = buf; |
@@ -447,48 +350,48 @@ static ssize_t ibft_attr_show_target(struct ibft_kobject *entry, | |||
447 | if (!tgt) | 350 | if (!tgt) |
448 | return 0; | 351 | return 0; |
449 | 352 | ||
450 | switch (attr->type) { | 353 | switch (type) { |
451 | case ibft_tgt_index: | 354 | case ISCSI_BOOT_TGT_INDEX: |
452 | str += sprintf(str, "%d\n", tgt->hdr.index); | 355 | str += sprintf(str, "%d\n", tgt->hdr.index); |
453 | break; | 356 | break; |
454 | case ibft_tgt_flags: | 357 | case ISCSI_BOOT_TGT_FLAGS: |
455 | str += sprintf(str, "%d\n", tgt->hdr.flags); | 358 | str += sprintf(str, "%d\n", tgt->hdr.flags); |
456 | break; | 359 | break; |
457 | case ibft_tgt_ip_addr: | 360 | case ISCSI_BOOT_TGT_IP_ADDR: |
458 | str += sprintf_ipaddr(str, tgt->ip_addr); | 361 | str += sprintf_ipaddr(str, tgt->ip_addr); |
459 | break; | 362 | break; |
460 | case ibft_tgt_port: | 363 | case ISCSI_BOOT_TGT_PORT: |
461 | str += sprintf(str, "%d\n", tgt->port); | 364 | str += sprintf(str, "%d\n", tgt->port); |
462 | break; | 365 | break; |
463 | case ibft_tgt_lun: | 366 | case ISCSI_BOOT_TGT_LUN: |
464 | for (i = 0; i < 8; i++) | 367 | for (i = 0; i < 8; i++) |
465 | str += sprintf(str, "%x", (u8)tgt->lun[i]); | 368 | str += sprintf(str, "%x", (u8)tgt->lun[i]); |
466 | str += sprintf(str, "\n"); | 369 | str += sprintf(str, "\n"); |
467 | break; | 370 | break; |
468 | case ibft_tgt_nic_assoc: | 371 | case ISCSI_BOOT_TGT_NIC_ASSOC: |
469 | str += sprintf(str, "%d\n", tgt->nic_assoc); | 372 | str += sprintf(str, "%d\n", tgt->nic_assoc); |
470 | break; | 373 | break; |
471 | case ibft_tgt_chap_type: | 374 | case ISCSI_BOOT_TGT_CHAP_TYPE: |
472 | str += sprintf(str, "%d\n", tgt->chap_type); | 375 | str += sprintf(str, "%d\n", tgt->chap_type); |
473 | break; | 376 | break; |
474 | case ibft_tgt_name: | 377 | case ISCSI_BOOT_TGT_NAME: |
475 | str += sprintf_string(str, tgt->tgt_name_len, | 378 | str += sprintf_string(str, tgt->tgt_name_len, |
476 | (char *)ibft_loc + tgt->tgt_name_off); | 379 | (char *)ibft_loc + tgt->tgt_name_off); |
477 | break; | 380 | break; |
478 | case ibft_tgt_chap_name: | 381 | case ISCSI_BOOT_TGT_CHAP_NAME: |
479 | str += sprintf_string(str, tgt->chap_name_len, | 382 | str += sprintf_string(str, tgt->chap_name_len, |
480 | (char *)ibft_loc + tgt->chap_name_off); | 383 | (char *)ibft_loc + tgt->chap_name_off); |
481 | break; | 384 | break; |
482 | case ibft_tgt_chap_secret: | 385 | case ISCSI_BOOT_TGT_CHAP_SECRET: |
483 | str += sprintf_string(str, tgt->chap_secret_len, | 386 | str += sprintf_string(str, tgt->chap_secret_len, |
484 | (char *)ibft_loc + tgt->chap_secret_off); | 387 | (char *)ibft_loc + tgt->chap_secret_off); |
485 | break; | 388 | break; |
486 | case ibft_tgt_rev_chap_name: | 389 | case ISCSI_BOOT_TGT_REV_CHAP_NAME: |
487 | str += sprintf_string(str, tgt->rev_chap_name_len, | 390 | str += sprintf_string(str, tgt->rev_chap_name_len, |
488 | (char *)ibft_loc + | 391 | (char *)ibft_loc + |
489 | tgt->rev_chap_name_off); | 392 | tgt->rev_chap_name_off); |
490 | break; | 393 | break; |
491 | case ibft_tgt_rev_chap_secret: | 394 | case ISCSI_BOOT_TGT_REV_CHAP_SECRET: |
492 | str += sprintf_string(str, tgt->rev_chap_secret_len, | 395 | str += sprintf_string(str, tgt->rev_chap_secret_len, |
493 | (char *)ibft_loc + | 396 | (char *)ibft_loc + |
494 | tgt->rev_chap_secret_off); | 397 | tgt->rev_chap_secret_off); |
@@ -500,40 +403,6 @@ static ssize_t ibft_attr_show_target(struct ibft_kobject *entry, | |||
500 | return str - buf; | 403 | return str - buf; |
501 | } | 404 | } |
502 | 405 | ||
503 | /* | ||
504 | * The routine called for all sysfs attributes. | ||
505 | */ | ||
506 | static ssize_t ibft_show_attribute(struct kobject *kobj, | ||
507 | struct attribute *attr, | ||
508 | char *buf) | ||
509 | { | ||
510 | struct ibft_kobject *dev = | ||
511 | container_of(kobj, struct ibft_kobject, kobj); | ||
512 | struct ibft_attribute *ibft_attr = | ||
513 | container_of(attr, struct ibft_attribute, attr); | ||
514 | ssize_t ret = -EIO; | ||
515 | char *str = buf; | ||
516 | |||
517 | if (!capable(CAP_SYS_ADMIN)) | ||
518 | return -EACCES; | ||
519 | |||
520 | if (ibft_attr->show) | ||
521 | ret = ibft_attr->show(dev, ibft_attr, str); | ||
522 | |||
523 | return ret; | ||
524 | } | ||
525 | |||
526 | static const struct sysfs_ops ibft_attr_ops = { | ||
527 | .show = ibft_show_attribute, | ||
528 | }; | ||
529 | |||
530 | static struct kobj_type ibft_ktype = { | ||
531 | .release = ibft_release, | ||
532 | .sysfs_ops = &ibft_attr_ops, | ||
533 | }; | ||
534 | |||
535 | static struct kset *ibft_kset; | ||
536 | |||
537 | static int __init ibft_check_device(void) | 406 | static int __init ibft_check_device(void) |
538 | { | 407 | { |
539 | int len; | 408 | int len; |
@@ -561,12 +430,149 @@ static int __init ibft_check_device(void) | |||
561 | } | 430 | } |
562 | 431 | ||
563 | /* | 432 | /* |
433 | * Helper routiners to check to determine if the entry is valid | ||
434 | * in the proper iBFT structure. | ||
435 | */ | ||
436 | static mode_t ibft_check_nic_for(void *data, int type) | ||
437 | { | ||
438 | struct ibft_kobject *entry = data; | ||
439 | struct ibft_nic *nic = entry->nic; | ||
440 | mode_t rc = 0; | ||
441 | |||
442 | switch (type) { | ||
443 | case ISCSI_BOOT_ETH_INDEX: | ||
444 | case ISCSI_BOOT_ETH_FLAGS: | ||
445 | rc = S_IRUGO; | ||
446 | break; | ||
447 | case ISCSI_BOOT_ETH_IP_ADDR: | ||
448 | if (memcmp(nic->ip_addr, nulls, sizeof(nic->ip_addr))) | ||
449 | rc = S_IRUGO; | ||
450 | break; | ||
451 | case ISCSI_BOOT_ETH_SUBNET_MASK: | ||
452 | if (nic->subnet_mask_prefix) | ||
453 | rc = S_IRUGO; | ||
454 | break; | ||
455 | case ISCSI_BOOT_ETH_ORIGIN: | ||
456 | rc = S_IRUGO; | ||
457 | break; | ||
458 | case ISCSI_BOOT_ETH_GATEWAY: | ||
459 | if (memcmp(nic->gateway, nulls, sizeof(nic->gateway))) | ||
460 | rc = S_IRUGO; | ||
461 | break; | ||
462 | case ISCSI_BOOT_ETH_PRIMARY_DNS: | ||
463 | if (memcmp(nic->primary_dns, nulls, | ||
464 | sizeof(nic->primary_dns))) | ||
465 | rc = S_IRUGO; | ||
466 | break; | ||
467 | case ISCSI_BOOT_ETH_SECONDARY_DNS: | ||
468 | if (memcmp(nic->secondary_dns, nulls, | ||
469 | sizeof(nic->secondary_dns))) | ||
470 | rc = S_IRUGO; | ||
471 | break; | ||
472 | case ISCSI_BOOT_ETH_DHCP: | ||
473 | if (memcmp(nic->dhcp, nulls, sizeof(nic->dhcp))) | ||
474 | rc = S_IRUGO; | ||
475 | break; | ||
476 | case ISCSI_BOOT_ETH_VLAN: | ||
477 | case ISCSI_BOOT_ETH_MAC: | ||
478 | rc = S_IRUGO; | ||
479 | break; | ||
480 | case ISCSI_BOOT_ETH_HOSTNAME: | ||
481 | if (nic->hostname_off) | ||
482 | rc = S_IRUGO; | ||
483 | break; | ||
484 | default: | ||
485 | break; | ||
486 | } | ||
487 | |||
488 | return rc; | ||
489 | } | ||
490 | |||
491 | static mode_t __init ibft_check_tgt_for(void *data, int type) | ||
492 | { | ||
493 | struct ibft_kobject *entry = data; | ||
494 | struct ibft_tgt *tgt = entry->tgt; | ||
495 | mode_t rc = 0; | ||
496 | |||
497 | switch (type) { | ||
498 | case ISCSI_BOOT_TGT_INDEX: | ||
499 | case ISCSI_BOOT_TGT_FLAGS: | ||
500 | case ISCSI_BOOT_TGT_IP_ADDR: | ||
501 | case ISCSI_BOOT_TGT_PORT: | ||
502 | case ISCSI_BOOT_TGT_LUN: | ||
503 | case ISCSI_BOOT_TGT_NIC_ASSOC: | ||
504 | case ISCSI_BOOT_TGT_CHAP_TYPE: | ||
505 | rc = S_IRUGO; | ||
506 | case ISCSI_BOOT_TGT_NAME: | ||
507 | if (tgt->tgt_name_len) | ||
508 | rc = S_IRUGO; | ||
509 | break; | ||
510 | case ISCSI_BOOT_TGT_CHAP_NAME: | ||
511 | case ISCSI_BOOT_TGT_CHAP_SECRET: | ||
512 | if (tgt->chap_name_len) | ||
513 | rc = S_IRUGO; | ||
514 | break; | ||
515 | case ISCSI_BOOT_TGT_REV_CHAP_NAME: | ||
516 | case ISCSI_BOOT_TGT_REV_CHAP_SECRET: | ||
517 | if (tgt->rev_chap_name_len) | ||
518 | rc = S_IRUGO; | ||
519 | break; | ||
520 | default: | ||
521 | break; | ||
522 | } | ||
523 | |||
524 | return rc; | ||
525 | } | ||
526 | |||
527 | static mode_t __init ibft_check_initiator_for(void *data, int type) | ||
528 | { | ||
529 | struct ibft_kobject *entry = data; | ||
530 | struct ibft_initiator *init = entry->initiator; | ||
531 | mode_t rc = 0; | ||
532 | |||
533 | switch (type) { | ||
534 | case ISCSI_BOOT_INI_INDEX: | ||
535 | case ISCSI_BOOT_INI_FLAGS: | ||
536 | rc = S_IRUGO; | ||
537 | break; | ||
538 | case ISCSI_BOOT_INI_ISNS_SERVER: | ||
539 | if (memcmp(init->isns_server, nulls, | ||
540 | sizeof(init->isns_server))) | ||
541 | rc = S_IRUGO; | ||
542 | break; | ||
543 | case ISCSI_BOOT_INI_SLP_SERVER: | ||
544 | if (memcmp(init->slp_server, nulls, | ||
545 | sizeof(init->slp_server))) | ||
546 | rc = S_IRUGO; | ||
547 | break; | ||
548 | case ISCSI_BOOT_INI_PRI_RADIUS_SERVER: | ||
549 | if (memcmp(init->pri_radius_server, nulls, | ||
550 | sizeof(init->pri_radius_server))) | ||
551 | rc = S_IRUGO; | ||
552 | break; | ||
553 | case ISCSI_BOOT_INI_SEC_RADIUS_SERVER: | ||
554 | if (memcmp(init->sec_radius_server, nulls, | ||
555 | sizeof(init->sec_radius_server))) | ||
556 | rc = S_IRUGO; | ||
557 | break; | ||
558 | case ISCSI_BOOT_INI_INITIATOR_NAME: | ||
559 | if (init->initiator_name_len) | ||
560 | rc = S_IRUGO; | ||
561 | break; | ||
562 | default: | ||
563 | break; | ||
564 | } | ||
565 | |||
566 | return rc; | ||
567 | } | ||
568 | |||
569 | /* | ||
564 | * Helper function for ibft_register_kobjects. | 570 | * Helper function for ibft_register_kobjects. |
565 | */ | 571 | */ |
566 | static int __init ibft_create_kobject(struct acpi_table_ibft *header, | 572 | static int __init ibft_create_kobject(struct acpi_table_ibft *header, |
567 | struct ibft_hdr *hdr, | 573 | struct ibft_hdr *hdr) |
568 | struct list_head *list) | ||
569 | { | 574 | { |
575 | struct iscsi_boot_kobj *boot_kobj = NULL; | ||
570 | struct ibft_kobject *ibft_kobj = NULL; | 576 | struct ibft_kobject *ibft_kobj = NULL; |
571 | struct ibft_nic *nic = (struct ibft_nic *)hdr; | 577 | struct ibft_nic *nic = (struct ibft_nic *)hdr; |
572 | struct pci_dev *pci_dev; | 578 | struct pci_dev *pci_dev; |
@@ -583,14 +589,47 @@ static int __init ibft_create_kobject(struct acpi_table_ibft *header, | |||
583 | case id_initiator: | 589 | case id_initiator: |
584 | rc = ibft_verify_hdr("initiator", hdr, id_initiator, | 590 | rc = ibft_verify_hdr("initiator", hdr, id_initiator, |
585 | sizeof(*ibft_kobj->initiator)); | 591 | sizeof(*ibft_kobj->initiator)); |
592 | if (rc) | ||
593 | break; | ||
594 | |||
595 | boot_kobj = iscsi_boot_create_initiator(boot_kset, hdr->index, | ||
596 | ibft_kobj, | ||
597 | ibft_attr_show_initiator, | ||
598 | ibft_check_initiator_for); | ||
599 | if (!boot_kobj) { | ||
600 | rc = -ENOMEM; | ||
601 | goto free_ibft_obj; | ||
602 | } | ||
586 | break; | 603 | break; |
587 | case id_nic: | 604 | case id_nic: |
588 | rc = ibft_verify_hdr("ethernet", hdr, id_nic, | 605 | rc = ibft_verify_hdr("ethernet", hdr, id_nic, |
589 | sizeof(*ibft_kobj->nic)); | 606 | sizeof(*ibft_kobj->nic)); |
607 | if (rc) | ||
608 | break; | ||
609 | |||
610 | boot_kobj = iscsi_boot_create_ethernet(boot_kset, hdr->index, | ||
611 | ibft_kobj, | ||
612 | ibft_attr_show_nic, | ||
613 | ibft_check_nic_for); | ||
614 | if (!boot_kobj) { | ||
615 | rc = -ENOMEM; | ||
616 | goto free_ibft_obj; | ||
617 | } | ||
590 | break; | 618 | break; |
591 | case id_target: | 619 | case id_target: |
592 | rc = ibft_verify_hdr("target", hdr, id_target, | 620 | rc = ibft_verify_hdr("target", hdr, id_target, |
593 | sizeof(*ibft_kobj->tgt)); | 621 | sizeof(*ibft_kobj->tgt)); |
622 | if (rc) | ||
623 | break; | ||
624 | |||
625 | boot_kobj = iscsi_boot_create_target(boot_kset, hdr->index, | ||
626 | ibft_kobj, | ||
627 | ibft_attr_show_target, | ||
628 | ibft_check_tgt_for); | ||
629 | if (!boot_kobj) { | ||
630 | rc = -ENOMEM; | ||
631 | goto free_ibft_obj; | ||
632 | } | ||
594 | break; | 633 | break; |
595 | case id_reserved: | 634 | case id_reserved: |
596 | case id_control: | 635 | case id_control: |
@@ -608,22 +647,10 @@ static int __init ibft_create_kobject(struct acpi_table_ibft *header, | |||
608 | 647 | ||
609 | if (rc) { | 648 | if (rc) { |
610 | /* Skip adding this kobject, but exit with non-fatal error. */ | 649 | /* Skip adding this kobject, but exit with non-fatal error. */ |
611 | kfree(ibft_kobj); | 650 | rc = 0; |
612 | goto out_invalid_struct; | 651 | goto free_ibft_obj; |
613 | } | ||
614 | |||
615 | ibft_kobj->kobj.kset = ibft_kset; | ||
616 | |||
617 | rc = kobject_init_and_add(&ibft_kobj->kobj, &ibft_ktype, | ||
618 | NULL, ibft_id_names[hdr->id], hdr->index); | ||
619 | |||
620 | if (rc) { | ||
621 | kfree(ibft_kobj); | ||
622 | goto out; | ||
623 | } | 652 | } |
624 | 653 | ||
625 | kobject_uevent(&ibft_kobj->kobj, KOBJ_ADD); | ||
626 | |||
627 | if (hdr->id == id_nic) { | 654 | if (hdr->id == id_nic) { |
628 | /* | 655 | /* |
629 | * We don't search for the device in other domains than | 656 | * We don't search for the device in other domains than |
@@ -634,19 +661,16 @@ static int __init ibft_create_kobject(struct acpi_table_ibft *header, | |||
634 | pci_dev = pci_get_bus_and_slot((nic->pci_bdf & 0xff00) >> 8, | 661 | pci_dev = pci_get_bus_and_slot((nic->pci_bdf & 0xff00) >> 8, |
635 | (nic->pci_bdf & 0xff)); | 662 | (nic->pci_bdf & 0xff)); |
636 | if (pci_dev) { | 663 | if (pci_dev) { |
637 | rc = sysfs_create_link(&ibft_kobj->kobj, | 664 | rc = sysfs_create_link(&boot_kobj->kobj, |
638 | &pci_dev->dev.kobj, "device"); | 665 | &pci_dev->dev.kobj, "device"); |
639 | pci_dev_put(pci_dev); | 666 | pci_dev_put(pci_dev); |
640 | } | 667 | } |
641 | } | 668 | } |
669 | return 0; | ||
642 | 670 | ||
643 | /* Nothing broke so lets add it to the list. */ | 671 | free_ibft_obj: |
644 | list_add_tail(&ibft_kobj->node, list); | 672 | kfree(ibft_kobj); |
645 | out: | ||
646 | return rc; | 673 | return rc; |
647 | out_invalid_struct: | ||
648 | /* Unsupported structs are skipped. */ | ||
649 | return 0; | ||
650 | } | 674 | } |
651 | 675 | ||
652 | /* | 676 | /* |
@@ -654,8 +678,7 @@ out_invalid_struct: | |||
654 | * found add them on the passed-in list. We do not support the other | 678 | * found add them on the passed-in list. We do not support the other |
655 | * fields at this point, so they are skipped. | 679 | * fields at this point, so they are skipped. |
656 | */ | 680 | */ |
657 | static int __init ibft_register_kobjects(struct acpi_table_ibft *header, | 681 | static int __init ibft_register_kobjects(struct acpi_table_ibft *header) |
658 | struct list_head *list) | ||
659 | { | 682 | { |
660 | struct ibft_control *control = NULL; | 683 | struct ibft_control *control = NULL; |
661 | void *ptr, *end; | 684 | void *ptr, *end; |
@@ -680,8 +703,7 @@ static int __init ibft_register_kobjects(struct acpi_table_ibft *header, | |||
680 | if (offset && offset < header->header.length && | 703 | if (offset && offset < header->header.length && |
681 | offset < eot_offset) { | 704 | offset < eot_offset) { |
682 | rc = ibft_create_kobject(header, | 705 | rc = ibft_create_kobject(header, |
683 | (void *)header + offset, | 706 | (void *)header + offset); |
684 | list); | ||
685 | if (rc) | 707 | if (rc) |
686 | break; | 708 | break; |
687 | } | 709 | } |
@@ -690,240 +712,28 @@ static int __init ibft_register_kobjects(struct acpi_table_ibft *header, | |||
690 | return rc; | 712 | return rc; |
691 | } | 713 | } |
692 | 714 | ||
693 | static void ibft_unregister(struct list_head *attr_list, | 715 | static void ibft_unregister(void) |
694 | struct list_head *kobj_list) | ||
695 | { | 716 | { |
696 | struct ibft_kobject *data = NULL, *n; | 717 | struct iscsi_boot_kobj *boot_kobj, *tmp_kobj; |
697 | struct ibft_attribute *attr = NULL, *m; | 718 | struct ibft_kobject *ibft_kobj; |
698 | 719 | ||
699 | list_for_each_entry_safe(attr, m, attr_list, node) { | 720 | list_for_each_entry_safe(boot_kobj, tmp_kobj, |
700 | sysfs_remove_file(attr->kobj, &attr->attr); | 721 | &boot_kset->kobj_list, list) { |
701 | list_del(&attr->node); | 722 | ibft_kobj = boot_kobj->data; |
702 | kfree(attr); | 723 | if (ibft_kobj->hdr->id == id_nic) |
724 | sysfs_remove_link(&boot_kobj->kobj, "device"); | ||
703 | }; | 725 | }; |
704 | list_del_init(attr_list); | ||
705 | |||
706 | list_for_each_entry_safe(data, n, kobj_list, node) { | ||
707 | list_del(&data->node); | ||
708 | if (data->hdr->id == id_nic) | ||
709 | sysfs_remove_link(&data->kobj, "device"); | ||
710 | kobject_put(&data->kobj); | ||
711 | }; | ||
712 | list_del_init(kobj_list); | ||
713 | } | 726 | } |
714 | 727 | ||
715 | static int __init ibft_create_attribute(struct ibft_kobject *kobj_data, | 728 | static void ibft_cleanup(void) |
716 | int type, | ||
717 | const char *name, | ||
718 | ssize_t (*show)(struct ibft_kobject *, | ||
719 | struct ibft_attribute*, | ||
720 | char *buf), | ||
721 | struct list_head *list) | ||
722 | { | 729 | { |
723 | struct ibft_attribute *attr = NULL; | 730 | ibft_unregister(); |
724 | struct ibft_hdr *hdr = kobj_data->hdr; | 731 | iscsi_boot_destroy_kset(boot_kset); |
725 | |||
726 | attr = kmalloc(sizeof(*attr), GFP_KERNEL); | ||
727 | if (!attr) | ||
728 | return -ENOMEM; | ||
729 | |||
730 | attr->attr.name = name; | ||
731 | attr->attr.mode = S_IRUSR; | ||
732 | |||
733 | attr->hdr = hdr; | ||
734 | attr->show = show; | ||
735 | attr->kobj = &kobj_data->kobj; | ||
736 | attr->type = type; | ||
737 | |||
738 | list_add_tail(&attr->node, list); | ||
739 | |||
740 | return 0; | ||
741 | } | ||
742 | |||
743 | /* | ||
744 | * Helper routiners to check to determine if the entry is valid | ||
745 | * in the proper iBFT structure. | ||
746 | */ | ||
747 | static int __init ibft_check_nic_for(struct ibft_nic *nic, int entry) | ||
748 | { | ||
749 | int rc = 0; | ||
750 | |||
751 | switch (entry) { | ||
752 | case ibft_eth_index: | ||
753 | case ibft_eth_flags: | ||
754 | rc = 1; | ||
755 | break; | ||
756 | case ibft_eth_ip_addr: | ||
757 | if (memcmp(nic->ip_addr, nulls, sizeof(nic->ip_addr))) | ||
758 | rc = 1; | ||
759 | break; | ||
760 | case ibft_eth_subnet_mask: | ||
761 | if (nic->subnet_mask_prefix) | ||
762 | rc = 1; | ||
763 | break; | ||
764 | case ibft_eth_origin: | ||
765 | rc = 1; | ||
766 | break; | ||
767 | case ibft_eth_gateway: | ||
768 | if (memcmp(nic->gateway, nulls, sizeof(nic->gateway))) | ||
769 | rc = 1; | ||
770 | break; | ||
771 | case ibft_eth_primary_dns: | ||
772 | if (memcmp(nic->primary_dns, nulls, | ||
773 | sizeof(nic->primary_dns))) | ||
774 | rc = 1; | ||
775 | break; | ||
776 | case ibft_eth_secondary_dns: | ||
777 | if (memcmp(nic->secondary_dns, nulls, | ||
778 | sizeof(nic->secondary_dns))) | ||
779 | rc = 1; | ||
780 | break; | ||
781 | case ibft_eth_dhcp: | ||
782 | if (memcmp(nic->dhcp, nulls, sizeof(nic->dhcp))) | ||
783 | rc = 1; | ||
784 | break; | ||
785 | case ibft_eth_vlan: | ||
786 | case ibft_eth_mac: | ||
787 | rc = 1; | ||
788 | break; | ||
789 | case ibft_eth_hostname: | ||
790 | if (nic->hostname_off) | ||
791 | rc = 1; | ||
792 | break; | ||
793 | default: | ||
794 | break; | ||
795 | } | ||
796 | |||
797 | return rc; | ||
798 | } | 732 | } |
799 | 733 | ||
800 | static int __init ibft_check_tgt_for(struct ibft_tgt *tgt, int entry) | 734 | static void __exit ibft_exit(void) |
801 | { | ||
802 | int rc = 0; | ||
803 | |||
804 | switch (entry) { | ||
805 | case ibft_tgt_index: | ||
806 | case ibft_tgt_flags: | ||
807 | case ibft_tgt_ip_addr: | ||
808 | case ibft_tgt_port: | ||
809 | case ibft_tgt_lun: | ||
810 | case ibft_tgt_nic_assoc: | ||
811 | case ibft_tgt_chap_type: | ||
812 | rc = 1; | ||
813 | case ibft_tgt_name: | ||
814 | if (tgt->tgt_name_len) | ||
815 | rc = 1; | ||
816 | break; | ||
817 | case ibft_tgt_chap_name: | ||
818 | case ibft_tgt_chap_secret: | ||
819 | if (tgt->chap_name_len) | ||
820 | rc = 1; | ||
821 | break; | ||
822 | case ibft_tgt_rev_chap_name: | ||
823 | case ibft_tgt_rev_chap_secret: | ||
824 | if (tgt->rev_chap_name_len) | ||
825 | rc = 1; | ||
826 | break; | ||
827 | default: | ||
828 | break; | ||
829 | } | ||
830 | |||
831 | return rc; | ||
832 | } | ||
833 | |||
834 | static int __init ibft_check_initiator_for(struct ibft_initiator *init, | ||
835 | int entry) | ||
836 | { | ||
837 | int rc = 0; | ||
838 | |||
839 | switch (entry) { | ||
840 | case ibft_init_index: | ||
841 | case ibft_init_flags: | ||
842 | rc = 1; | ||
843 | break; | ||
844 | case ibft_init_isns_server: | ||
845 | if (memcmp(init->isns_server, nulls, | ||
846 | sizeof(init->isns_server))) | ||
847 | rc = 1; | ||
848 | break; | ||
849 | case ibft_init_slp_server: | ||
850 | if (memcmp(init->slp_server, nulls, | ||
851 | sizeof(init->slp_server))) | ||
852 | rc = 1; | ||
853 | break; | ||
854 | case ibft_init_pri_radius_server: | ||
855 | if (memcmp(init->pri_radius_server, nulls, | ||
856 | sizeof(init->pri_radius_server))) | ||
857 | rc = 1; | ||
858 | break; | ||
859 | case ibft_init_sec_radius_server: | ||
860 | if (memcmp(init->sec_radius_server, nulls, | ||
861 | sizeof(init->sec_radius_server))) | ||
862 | rc = 1; | ||
863 | break; | ||
864 | case ibft_init_initiator_name: | ||
865 | if (init->initiator_name_len) | ||
866 | rc = 1; | ||
867 | break; | ||
868 | default: | ||
869 | break; | ||
870 | } | ||
871 | |||
872 | return rc; | ||
873 | } | ||
874 | |||
875 | /* | ||
876 | * Register the attributes for all of the kobjects. | ||
877 | */ | ||
878 | static int __init ibft_register_attributes(struct list_head *kobject_list, | ||
879 | struct list_head *attr_list) | ||
880 | { | 735 | { |
881 | int rc = 0, i = 0; | 736 | ibft_cleanup(); |
882 | struct ibft_kobject *data = NULL; | ||
883 | struct ibft_attribute *attr = NULL, *m; | ||
884 | |||
885 | list_for_each_entry(data, kobject_list, node) { | ||
886 | switch (data->hdr->id) { | ||
887 | case id_nic: | ||
888 | for (i = 0; i < ibft_eth_end_marker && !rc; i++) | ||
889 | if (ibft_check_nic_for(data->nic, i)) | ||
890 | rc = ibft_create_attribute(data, i, | ||
891 | ibft_eth_properties[i], | ||
892 | ibft_attr_show_nic, attr_list); | ||
893 | break; | ||
894 | case id_target: | ||
895 | for (i = 0; i < ibft_tgt_end_marker && !rc; i++) | ||
896 | if (ibft_check_tgt_for(data->tgt, i)) | ||
897 | rc = ibft_create_attribute(data, i, | ||
898 | ibft_tgt_properties[i], | ||
899 | ibft_attr_show_target, | ||
900 | attr_list); | ||
901 | break; | ||
902 | case id_initiator: | ||
903 | for (i = 0; i < ibft_init_end_marker && !rc; i++) | ||
904 | if (ibft_check_initiator_for( | ||
905 | data->initiator, i)) | ||
906 | rc = ibft_create_attribute(data, i, | ||
907 | ibft_initiator_properties[i], | ||
908 | ibft_attr_show_initiator, | ||
909 | attr_list); | ||
910 | break; | ||
911 | default: | ||
912 | break; | ||
913 | } | ||
914 | if (rc) | ||
915 | break; | ||
916 | } | ||
917 | list_for_each_entry_safe(attr, m, attr_list, node) { | ||
918 | rc = sysfs_create_file(attr->kobj, &attr->attr); | ||
919 | if (rc) { | ||
920 | list_del(&attr->node); | ||
921 | kfree(attr); | ||
922 | break; | ||
923 | } | ||
924 | } | ||
925 | |||
926 | return rc; | ||
927 | } | 737 | } |
928 | 738 | ||
929 | /* | 739 | /* |
@@ -933,26 +743,20 @@ static int __init ibft_init(void) | |||
933 | { | 743 | { |
934 | int rc = 0; | 744 | int rc = 0; |
935 | 745 | ||
936 | ibft_kset = kset_create_and_add("ibft", NULL, firmware_kobj); | ||
937 | if (!ibft_kset) | ||
938 | return -ENOMEM; | ||
939 | |||
940 | if (ibft_addr) { | 746 | if (ibft_addr) { |
941 | printk(KERN_INFO "iBFT detected at 0x%llx.\n", | 747 | printk(KERN_INFO "iBFT detected at 0x%llx.\n", |
942 | (u64)isa_virt_to_bus(ibft_addr)); | 748 | (u64)isa_virt_to_bus(ibft_addr)); |
943 | 749 | ||
944 | rc = ibft_check_device(); | 750 | rc = ibft_check_device(); |
945 | if (rc) | 751 | if (rc) |
946 | goto out_firmware_unregister; | 752 | return rc; |
947 | 753 | ||
948 | /* Scan the IBFT for data and register the kobjects. */ | 754 | boot_kset = iscsi_boot_create_kset("ibft"); |
949 | rc = ibft_register_kobjects(ibft_addr, &ibft_kobject_list); | 755 | if (!boot_kset) |
950 | if (rc) | 756 | return -ENOMEM; |
951 | goto out_free; | ||
952 | 757 | ||
953 | /* Register the attributes */ | 758 | /* Scan the IBFT for data and register the kobjects. */ |
954 | rc = ibft_register_attributes(&ibft_kobject_list, | 759 | rc = ibft_register_kobjects(ibft_addr); |
955 | &ibft_attr_list); | ||
956 | if (rc) | 760 | if (rc) |
957 | goto out_free; | 761 | goto out_free; |
958 | } else | 762 | } else |
@@ -961,17 +765,9 @@ static int __init ibft_init(void) | |||
961 | return 0; | 765 | return 0; |
962 | 766 | ||
963 | out_free: | 767 | out_free: |
964 | ibft_unregister(&ibft_attr_list, &ibft_kobject_list); | 768 | ibft_cleanup(); |
965 | out_firmware_unregister: | ||
966 | kset_unregister(ibft_kset); | ||
967 | return rc; | 769 | return rc; |
968 | } | 770 | } |
969 | 771 | ||
970 | static void __exit ibft_exit(void) | ||
971 | { | ||
972 | ibft_unregister(&ibft_attr_list, &ibft_kobject_list); | ||
973 | kset_unregister(ibft_kset); | ||
974 | } | ||
975 | |||
976 | module_init(ibft_init); | 772 | module_init(ibft_init); |
977 | module_exit(ibft_exit); | 773 | module_exit(ibft_exit); |