diff options
| -rw-r--r-- | drivers/firewire/core-device.c | 3 | ||||
| -rw-r--r-- | drivers/firewire/core-transaction.c | 33 | ||||
| -rw-r--r-- | drivers/firewire/ohci.c | 28 | ||||
| -rw-r--r-- | include/linux/firewire.h | 12 |
4 files changed, 59 insertions, 17 deletions
diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 7a05fd24d68b..3873d535b28d 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c | |||
| @@ -32,6 +32,7 @@ | |||
| 32 | #include <linux/mod_devicetable.h> | 32 | #include <linux/mod_devicetable.h> |
| 33 | #include <linux/module.h> | 33 | #include <linux/module.h> |
| 34 | #include <linux/mutex.h> | 34 | #include <linux/mutex.h> |
| 35 | #include <linux/random.h> | ||
| 35 | #include <linux/rwsem.h> | 36 | #include <linux/rwsem.h> |
| 36 | #include <linux/slab.h> | 37 | #include <linux/slab.h> |
| 37 | #include <linux/spinlock.h> | 38 | #include <linux/spinlock.h> |
| @@ -1066,6 +1067,8 @@ static void fw_device_init(struct work_struct *work) | |||
| 1066 | device->config_rom_retries = 0; | 1067 | device->config_rom_retries = 0; |
| 1067 | 1068 | ||
| 1068 | set_broadcast_channel(device, device->generation); | 1069 | set_broadcast_channel(device, device->generation); |
| 1070 | |||
| 1071 | add_device_randomness(&device->config_rom[3], 8); | ||
| 1069 | } | 1072 | } |
| 1070 | 1073 | ||
| 1071 | /* | 1074 | /* |
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 87d6f2d2f02d..28a94c7ec6e5 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #include <linux/kernel.h> | 31 | #include <linux/kernel.h> |
| 32 | #include <linux/list.h> | 32 | #include <linux/list.h> |
| 33 | #include <linux/module.h> | 33 | #include <linux/module.h> |
| 34 | #include <linux/rculist.h> | ||
| 34 | #include <linux/slab.h> | 35 | #include <linux/slab.h> |
| 35 | #include <linux/spinlock.h> | 36 | #include <linux/spinlock.h> |
| 36 | #include <linux/string.h> | 37 | #include <linux/string.h> |
| @@ -489,7 +490,7 @@ static struct fw_address_handler *lookup_overlapping_address_handler( | |||
| 489 | { | 490 | { |
| 490 | struct fw_address_handler *handler; | 491 | struct fw_address_handler *handler; |
| 491 | 492 | ||
| 492 | list_for_each_entry(handler, list, link) { | 493 | list_for_each_entry_rcu(handler, list, link) { |
| 493 | if (handler->offset < offset + length && | 494 | if (handler->offset < offset + length && |
| 494 | offset < handler->offset + handler->length) | 495 | offset < handler->offset + handler->length) |
| 495 | return handler; | 496 | return handler; |
| @@ -510,7 +511,7 @@ static struct fw_address_handler *lookup_enclosing_address_handler( | |||
| 510 | { | 511 | { |
| 511 | struct fw_address_handler *handler; | 512 | struct fw_address_handler *handler; |
| 512 | 513 | ||
| 513 | list_for_each_entry(handler, list, link) { | 514 | list_for_each_entry_rcu(handler, list, link) { |
| 514 | if (is_enclosing_handler(handler, offset, length)) | 515 | if (is_enclosing_handler(handler, offset, length)) |
| 515 | return handler; | 516 | return handler; |
| 516 | } | 517 | } |
| @@ -518,7 +519,7 @@ static struct fw_address_handler *lookup_enclosing_address_handler( | |||
| 518 | return NULL; | 519 | return NULL; |
| 519 | } | 520 | } |
| 520 | 521 | ||
| 521 | static DEFINE_SPINLOCK(address_handler_lock); | 522 | static DEFINE_SPINLOCK(address_handler_list_lock); |
| 522 | static LIST_HEAD(address_handler_list); | 523 | static LIST_HEAD(address_handler_list); |
| 523 | 524 | ||
| 524 | const struct fw_address_region fw_high_memory_region = | 525 | const struct fw_address_region fw_high_memory_region = |
| @@ -555,6 +556,7 @@ static bool is_in_fcp_region(u64 offset, size_t length) | |||
| 555 | * the specified callback is invoked. The parameters passed to the callback | 556 | * the specified callback is invoked. The parameters passed to the callback |
| 556 | * give the details of the particular request. | 557 | * give the details of the particular request. |
| 557 | * | 558 | * |
| 559 | * To be called in process context. | ||
| 558 | * Return value: 0 on success, non-zero otherwise. | 560 | * Return value: 0 on success, non-zero otherwise. |
| 559 | * | 561 | * |
| 560 | * The start offset of the handler's address region is determined by | 562 | * The start offset of the handler's address region is determined by |
| @@ -575,7 +577,7 @@ int fw_core_add_address_handler(struct fw_address_handler *handler, | |||
| 575 | handler->length == 0) | 577 | handler->length == 0) |
| 576 | return -EINVAL; | 578 | return -EINVAL; |
| 577 | 579 | ||
| 578 | spin_lock_bh(&address_handler_lock); | 580 | spin_lock(&address_handler_list_lock); |
| 579 | 581 | ||
| 580 | handler->offset = region->start; | 582 | handler->offset = region->start; |
| 581 | while (handler->offset + handler->length <= region->end) { | 583 | while (handler->offset + handler->length <= region->end) { |
| @@ -588,13 +590,13 @@ int fw_core_add_address_handler(struct fw_address_handler *handler, | |||
| 588 | if (other != NULL) { | 590 | if (other != NULL) { |
| 589 | handler->offset += other->length; | 591 | handler->offset += other->length; |
| 590 | } else { | 592 | } else { |
| 591 | list_add_tail(&handler->link, &address_handler_list); | 593 | list_add_tail_rcu(&handler->link, &address_handler_list); |
| 592 | ret = 0; | 594 | ret = 0; |
| 593 | break; | 595 | break; |
| 594 | } | 596 | } |
| 595 | } | 597 | } |
| 596 | 598 | ||
| 597 | spin_unlock_bh(&address_handler_lock); | 599 | spin_unlock(&address_handler_list_lock); |
| 598 | 600 | ||
| 599 | return ret; | 601 | return ret; |
| 600 | } | 602 | } |
| @@ -603,14 +605,17 @@ EXPORT_SYMBOL(fw_core_add_address_handler); | |||
| 603 | /** | 605 | /** |
| 604 | * fw_core_remove_address_handler() - unregister an address handler | 606 | * fw_core_remove_address_handler() - unregister an address handler |
| 605 | * | 607 | * |
| 608 | * To be called in process context. | ||
| 609 | * | ||
| 606 | * When fw_core_remove_address_handler() returns, @handler->callback() is | 610 | * When fw_core_remove_address_handler() returns, @handler->callback() is |
| 607 | * guaranteed to not run on any CPU anymore. | 611 | * guaranteed to not run on any CPU anymore. |
| 608 | */ | 612 | */ |
| 609 | void fw_core_remove_address_handler(struct fw_address_handler *handler) | 613 | void fw_core_remove_address_handler(struct fw_address_handler *handler) |
| 610 | { | 614 | { |
| 611 | spin_lock_bh(&address_handler_lock); | 615 | spin_lock(&address_handler_list_lock); |
| 612 | list_del(&handler->link); | 616 | list_del_rcu(&handler->link); |
| 613 | spin_unlock_bh(&address_handler_lock); | 617 | spin_unlock(&address_handler_list_lock); |
| 618 | synchronize_rcu(); | ||
| 614 | } | 619 | } |
| 615 | EXPORT_SYMBOL(fw_core_remove_address_handler); | 620 | EXPORT_SYMBOL(fw_core_remove_address_handler); |
| 616 | 621 | ||
| @@ -844,7 +849,7 @@ static void handle_exclusive_region_request(struct fw_card *card, | |||
| 844 | if (tcode == TCODE_LOCK_REQUEST) | 849 | if (tcode == TCODE_LOCK_REQUEST) |
| 845 | tcode = 0x10 + HEADER_GET_EXTENDED_TCODE(p->header[3]); | 850 | tcode = 0x10 + HEADER_GET_EXTENDED_TCODE(p->header[3]); |
| 846 | 851 | ||
| 847 | spin_lock_bh(&address_handler_lock); | 852 | rcu_read_lock(); |
| 848 | handler = lookup_enclosing_address_handler(&address_handler_list, | 853 | handler = lookup_enclosing_address_handler(&address_handler_list, |
| 849 | offset, request->length); | 854 | offset, request->length); |
| 850 | if (handler) | 855 | if (handler) |
| @@ -853,7 +858,7 @@ static void handle_exclusive_region_request(struct fw_card *card, | |||
| 853 | p->generation, offset, | 858 | p->generation, offset, |
| 854 | request->data, request->length, | 859 | request->data, request->length, |
| 855 | handler->callback_data); | 860 | handler->callback_data); |
| 856 | spin_unlock_bh(&address_handler_lock); | 861 | rcu_read_unlock(); |
| 857 | 862 | ||
| 858 | if (!handler) | 863 | if (!handler) |
| 859 | fw_send_response(card, request, RCODE_ADDRESS_ERROR); | 864 | fw_send_response(card, request, RCODE_ADDRESS_ERROR); |
| @@ -886,8 +891,8 @@ static void handle_fcp_region_request(struct fw_card *card, | |||
| 886 | return; | 891 | return; |
| 887 | } | 892 | } |
| 888 | 893 | ||
| 889 | spin_lock_bh(&address_handler_lock); | 894 | rcu_read_lock(); |
| 890 | list_for_each_entry(handler, &address_handler_list, link) { | 895 | list_for_each_entry_rcu(handler, &address_handler_list, link) { |
| 891 | if (is_enclosing_handler(handler, offset, request->length)) | 896 | if (is_enclosing_handler(handler, offset, request->length)) |
| 892 | handler->address_callback(card, NULL, tcode, | 897 | handler->address_callback(card, NULL, tcode, |
| 893 | destination, source, | 898 | destination, source, |
| @@ -896,7 +901,7 @@ static void handle_fcp_region_request(struct fw_card *card, | |||
| 896 | request->length, | 901 | request->length, |
| 897 | handler->callback_data); | 902 | handler->callback_data); |
| 898 | } | 903 | } |
| 899 | spin_unlock_bh(&address_handler_lock); | 904 | rcu_read_unlock(); |
| 900 | 905 | ||
| 901 | fw_send_response(card, request, RCODE_COMPLETE); | 906 | fw_send_response(card, request, RCODE_COMPLETE); |
| 902 | } | 907 | } |
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index c788dbdaf3bc..834e71d2324d 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c | |||
| @@ -1777,11 +1777,35 @@ static int get_self_id_pos(struct fw_ohci *ohci, u32 self_id, | |||
| 1777 | return i; | 1777 | return i; |
| 1778 | } | 1778 | } |
| 1779 | 1779 | ||
| 1780 | static int initiated_reset(struct fw_ohci *ohci) | ||
| 1781 | { | ||
| 1782 | int reg; | ||
| 1783 | int ret = 0; | ||
| 1784 | |||
| 1785 | mutex_lock(&ohci->phy_reg_mutex); | ||
| 1786 | reg = write_phy_reg(ohci, 7, 0xe0); /* Select page 7 */ | ||
| 1787 | if (reg >= 0) { | ||
| 1788 | reg = read_phy_reg(ohci, 8); | ||
| 1789 | reg |= 0x40; | ||
| 1790 | reg = write_phy_reg(ohci, 8, reg); /* set PMODE bit */ | ||
| 1791 | if (reg >= 0) { | ||
| 1792 | reg = read_phy_reg(ohci, 12); /* read register 12 */ | ||
| 1793 | if (reg >= 0) { | ||
| 1794 | if ((reg & 0x08) == 0x08) { | ||
| 1795 | /* bit 3 indicates "initiated reset" */ | ||
| 1796 | ret = 0x2; | ||
| 1797 | } | ||
| 1798 | } | ||
| 1799 | } | ||
| 1800 | } | ||
| 1801 | mutex_unlock(&ohci->phy_reg_mutex); | ||
| 1802 | return ret; | ||
| 1803 | } | ||
| 1804 | |||
| 1780 | /* | 1805 | /* |
| 1781 | * TI TSB82AA2B and TSB12LV26 do not receive the selfID of a locally | 1806 | * TI TSB82AA2B and TSB12LV26 do not receive the selfID of a locally |
| 1782 | * attached TSB41BA3D phy; see http://www.ti.com/litv/pdf/sllz059. | 1807 | * attached TSB41BA3D phy; see http://www.ti.com/litv/pdf/sllz059. |
| 1783 | * Construct the selfID from phy register contents. | 1808 | * Construct the selfID from phy register contents. |
| 1784 | * FIXME: How to determine the selfID.i flag? | ||
| 1785 | */ | 1809 | */ |
| 1786 | static int find_and_insert_self_id(struct fw_ohci *ohci, int self_id_count) | 1810 | static int find_and_insert_self_id(struct fw_ohci *ohci, int self_id_count) |
| 1787 | { | 1811 | { |
| @@ -1814,6 +1838,8 @@ static int find_and_insert_self_id(struct fw_ohci *ohci, int self_id_count) | |||
| 1814 | self_id |= ((status & 0x3) << (6 - (i * 2))); | 1838 | self_id |= ((status & 0x3) << (6 - (i * 2))); |
| 1815 | } | 1839 | } |
| 1816 | 1840 | ||
| 1841 | self_id |= initiated_reset(ohci); | ||
| 1842 | |||
| 1817 | pos = get_self_id_pos(ohci, self_id, self_id_count); | 1843 | pos = get_self_id_pos(ohci, self_id, self_id_count); |
| 1818 | if (pos >= 0) { | 1844 | if (pos >= 0) { |
| 1819 | memmove(&(ohci->self_id_buffer[pos+1]), | 1845 | memmove(&(ohci->self_id_buffer[pos+1]), |
diff --git a/include/linux/firewire.h b/include/linux/firewire.h index db04ec5121cb..191501afd7fb 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h | |||
| @@ -265,8 +265,16 @@ typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode, | |||
| 265 | void *data, size_t length, | 265 | void *data, size_t length, |
| 266 | void *callback_data); | 266 | void *callback_data); |
| 267 | /* | 267 | /* |
| 268 | * Important note: Except for the FCP registers, the callback must guarantee | 268 | * This callback handles an inbound request subaction. It is called in |
| 269 | * that either fw_send_response() or kfree() is called on the @request. | 269 | * RCU read-side context, therefore must not sleep. |
| 270 | * | ||
| 271 | * The callback should not initiate outbound request subactions directly. | ||
| 272 | * Otherwise there is a danger of recursion of inbound and outbound | ||
| 273 | * transactions from and to the local node. | ||
| 274 | * | ||
| 275 | * The callback is responsible that either fw_send_response() or kfree() | ||
| 276 | * is called on the @request, except for FCP registers for which the core | ||
| 277 | * takes care of that. | ||
| 270 | */ | 278 | */ |
| 271 | typedef void (*fw_address_callback_t)(struct fw_card *card, | 279 | typedef void (*fw_address_callback_t)(struct fw_card *card, |
| 272 | struct fw_request *request, | 280 | struct fw_request *request, |
