From 568a8cbb3d4da609a799920c761b2cdb7e583839 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 29 Jul 2024 22:46:29 +0900 Subject: [PATCH 01/55] firewire: ohci: use TCODE_LINK_INTERNAL consistently In IEEE 1394 specification, 0x0e in tcode field is reserved for internal purpose depending on link layer. In 1394 OHCI specification, it is used to express phy packet in AT/AR contexts. Current implementation of 1394 OHCI driver has several macros for the code. They can be simply replaced with a macro in core code. This commit obsoletes the macros. Link: https://lore.kernel.org/r/20240729134631.127189-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 9 ++++----- drivers/firewire/ohci.h | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 314a29c0fd3e..c3fff94b13e5 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -264,7 +264,6 @@ static inline struct fw_ohci *fw_ohci(struct fw_card *card) #define OHCI1394_REGISTER_SIZE 0x800 #define OHCI1394_PCI_HCI_Control 0x40 #define SELF_ID_BUF_SIZE 0x800 -#define OHCI_TCODE_PHY_PACKET 0x0e #define OHCI_VERSION_1_1 0x010010 static char ohci_driver_name[] = KBUILD_MODNAME; @@ -586,7 +585,7 @@ static void log_ar_at_event(struct fw_ohci *ohci, ohci_notice(ohci, "A%c %s, %s\n", dir, evts[evt], tcodes[tcode]); break; - case 0xe: + case TCODE_LINK_INTERNAL: ohci_notice(ohci, "A%c %s, PHY %08x %08x\n", dir, evts[evt], header[1], header[2]); break; @@ -939,7 +938,7 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer) case TCODE_WRITE_RESPONSE: case TCODE_READ_QUADLET_REQUEST: - case OHCI_TCODE_PHY_PACKET: + case TCODE_LINK_INTERNAL: p.header_length = 12; p.payload_length = 0; break; @@ -967,7 +966,7 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer) * Several controllers, notably from NEC and VIA, forget to * write ack_complete status at PHY packet reception. */ - if (evt == OHCI1394_evt_no_status && tcode == OHCI1394_phy_tcode) + if (evt == OHCI1394_evt_no_status && tcode == TCODE_LINK_INTERNAL) p.ack = ACK_COMPLETE; /* @@ -1435,7 +1434,7 @@ static int at_context_queue_packet(struct context *ctx, break; case TCODE_LINK_INTERNAL: - header[0] = cpu_to_le32((OHCI1394_phy_tcode << 4) | + header[0] = cpu_to_le32((TCODE_LINK_INTERNAL << 4) | (packet->speed << 16)); header[1] = cpu_to_le32(packet->header[1]); header[2] = cpu_to_le32(packet->header[2]); diff --git a/drivers/firewire/ohci.h b/drivers/firewire/ohci.h index 71c2ed84cafb..9ed36cfc6cae 100644 --- a/drivers/firewire/ohci.h +++ b/drivers/firewire/ohci.h @@ -153,7 +153,6 @@ #define OHCI1394_evt_unknown 0xe #define OHCI1394_evt_flushed 0xf -#define OHCI1394_phy_tcode 0xe // Self-ID DMA. From faa11b99c90f6faaaa7d6581b8ffd09abf3e9ce5 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 29 Jul 2024 22:46:30 +0900 Subject: [PATCH 02/55] firewire: ohci: minor code refactoring to localize text table The string table for tcode is just used by log_ar_at_event(). In the case, it is suitable to move the table inner the function definition. This commit is for the purpose. Additionally, the hard-coded value for tcode is replaced with defined macros as many as possible. Link: https://lore.kernel.org/r/20240729134631.127189-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index c3fff94b13e5..a0bb0e87e18a 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -531,20 +531,28 @@ static const char *evts[] = { [0x1e] = "ack_type_error", [0x1f] = "-reserved-", [0x20] = "pending/cancelled", }; -static const char *tcodes[] = { - [0x0] = "QW req", [0x1] = "BW req", - [0x2] = "W resp", [0x3] = "-reserved-", - [0x4] = "QR req", [0x5] = "BR req", - [0x6] = "QR resp", [0x7] = "BR resp", - [0x8] = "cycle start", [0x9] = "Lk req", - [0xa] = "async stream packet", [0xb] = "Lk resp", - [0xc] = "-reserved-", [0xd] = "-reserved-", - [0xe] = "link internal", [0xf] = "-reserved-", -}; static void log_ar_at_event(struct fw_ohci *ohci, char dir, int speed, u32 *header, int evt) { + static const char *const tcodes[] = { + [TCODE_WRITE_QUADLET_REQUEST] = "QW req", + [TCODE_WRITE_BLOCK_REQUEST] = "BW req", + [TCODE_WRITE_RESPONSE] = "W resp", + [0x3] = "-reserved-", + [TCODE_READ_QUADLET_REQUEST] = "QR req", + [TCODE_READ_BLOCK_REQUEST] = "BR req", + [TCODE_READ_QUADLET_RESPONSE] = "QR resp", + [TCODE_READ_BLOCK_RESPONSE] = "BR resp", + [TCODE_CYCLE_START] = "cycle start", + [TCODE_LOCK_REQUEST] = "Lk req", + [TCODE_STREAM_DATA] = "async stream packet", + [TCODE_LOCK_RESPONSE] = "Lk resp", + [0xc] = "-reserved-", + [0xd] = "-reserved-", + [TCODE_LINK_INTERNAL] = "link internal", + [0xf] = "-reserved-", + }; int tcode = async_header_get_tcode(header); char specific[12]; From 9b6ad6a0115e9a35da65abdc973b80539f983c26 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 29 Jul 2024 22:46:31 +0900 Subject: [PATCH 03/55] firewire: core: use common helper function to serialize phy configuration packet A common helper function is available to serialize the first quadlet of phy configuration packet. This commit is for the purpose. Link: https://lore.kernel.org/r/20240729134631.127189-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 4 +++- drivers/firewire/core-transaction.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 9a7dc90330a3..619048dcfd72 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -37,6 +37,8 @@ #include "core.h" #include +#include "packet-header-definitions.h" + /* * ABI version history is documented in linux/firewire-cdev.h. */ @@ -1635,7 +1637,7 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) e->client = client; e->p.speed = SCODE_100; e->p.generation = a->generation; - e->p.header[0] = TCODE_LINK_INTERNAL << 4; + async_header_set_tcode(e->p.header, TCODE_LINK_INTERNAL); e->p.header[1] = a->data[0]; e->p.header[2] = a->data[1]; e->p.header_length = 12; diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 4d2fc1f31fec..a89c841a7dbe 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -464,7 +464,6 @@ static void transmit_phy_packet_callback(struct fw_packet *packet, static struct fw_packet phy_config_packet = { .header_length = 12, - .header[0] = TCODE_LINK_INTERNAL << 4, .payload_length = 0, .speed = SCODE_100, .callback = transmit_phy_packet_callback, @@ -497,6 +496,7 @@ void fw_send_phy_config(struct fw_card *card, mutex_lock(&phy_config_mutex); + async_header_set_tcode(phy_config_packet.header, TCODE_LINK_INTERNAL); phy_config_packet.header[1] = data; phy_config_packet.header[2] = ~data; phy_config_packet.generation = generation; From 3593b38a1367c3b57e986b0d2a9b6ceb84ec44ce Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 1 Aug 2024 11:26:29 +0900 Subject: [PATCH 04/55] firewire: core: utilize kref to maintain fw_node with reference counting Current implementation directly uses refcount_t to maintain the life time of fw_node, while kref is available for the same purpose. This commit replaces the implementation with kref. Link: https://lore.kernel.org/r/20240801022629.31857-1-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-topology.c | 2 +- drivers/firewire/core.h | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index b4e637aa6932..46e6eb287d24 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -39,7 +39,7 @@ static struct fw_node *fw_node_create(u32 sid, int port_count, int color) node->initiated_reset = phy_packet_self_id_zero_get_initiated_reset(sid); node->port_count = port_count; - refcount_set(&node->ref_count, 1); + kref_init(&node->kref); INIT_LIST_HEAD(&node->link); return node; diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 7c36d2628e37..189e15e6ba82 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -183,7 +183,8 @@ struct fw_node { * local node to this node. */ u8 max_depth:4; /* Maximum depth to any leaf node */ u8 max_hops:4; /* Max hops in this sub tree */ - refcount_t ref_count; + + struct kref kref; /* For serializing node topology into a list. */ struct list_head link; @@ -196,15 +197,21 @@ struct fw_node { static inline struct fw_node *fw_node_get(struct fw_node *node) { - refcount_inc(&node->ref_count); + kref_get(&node->kref); return node; } +static void release_node(struct kref *kref) +{ + struct fw_node *node = container_of(kref, struct fw_node, kref); + + kfree(node); +} + static inline void fw_node_put(struct fw_node *node) { - if (refcount_dec_and_test(&node->ref_count)) - kfree(node); + kref_put(&node->kref, release_node); } void fw_core_handle_bus_reset(struct fw_card *card, int node_id, From 8db9d1557122a714549b91e25fd22301bc3a5178 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 2 Aug 2024 09:36:03 +0900 Subject: [PATCH 05/55] firewire: ohci: add static inline functions to serialize/deserialize data of AT DMA In 1394 OHCI specification, the format of data for AT DMA is different from the format of asynchronous packet in IEEE 1394 specification, in its spd and srcBusID fields. This commit adds some static inline functions to serialize/deserialize the data of AT DMA. Link: https://lore.kernel.org/r/20240802003606.109402-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci-serdes-test.c | 34 ++++++++ drivers/firewire/ohci.h | 115 ++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) diff --git a/drivers/firewire/ohci-serdes-test.c b/drivers/firewire/ohci-serdes-test.c index 304a09ff528e..5d48d53b493a 100644 --- a/drivers/firewire/ohci-serdes-test.c +++ b/drivers/firewire/ohci-serdes-test.c @@ -40,9 +40,43 @@ static void test_self_id_receive_buffer_deserialization(struct kunit *test) KUNIT_EXPECT_EQ(test, 0xf38b, timestamp); } +static void test_at_data_serdes(struct kunit *test) +{ + static const __le32 expected[] = { + cpu_to_le32(0x00020e80), + cpu_to_le32(0xffc2ffff), + cpu_to_le32(0xe0000000), + }; + __le32 quadlets[] = {0, 0, 0}; + bool has_src_bus_id = ohci1394_at_data_get_src_bus_id(expected); + unsigned int speed = ohci1394_at_data_get_speed(expected); + unsigned int tlabel = ohci1394_at_data_get_tlabel(expected); + unsigned int retry = ohci1394_at_data_get_retry(expected); + unsigned int tcode = ohci1394_at_data_get_tcode(expected); + unsigned int destination_id = ohci1394_at_data_get_destination_id(expected); + u64 destination_offset = ohci1394_at_data_get_destination_offset(expected); + + KUNIT_EXPECT_FALSE(test, has_src_bus_id); + KUNIT_EXPECT_EQ(test, 0x02, speed); + KUNIT_EXPECT_EQ(test, 0x03, tlabel); + KUNIT_EXPECT_EQ(test, 0x02, retry); + KUNIT_EXPECT_EQ(test, 0x08, tcode); + + ohci1394_at_data_set_src_bus_id(quadlets, has_src_bus_id); + ohci1394_at_data_set_speed(quadlets, speed); + ohci1394_at_data_set_tlabel(quadlets, tlabel); + ohci1394_at_data_set_retry(quadlets, retry); + ohci1394_at_data_set_tcode(quadlets, tcode); + ohci1394_at_data_set_destination_id(quadlets, destination_id); + ohci1394_at_data_set_destination_offset(quadlets, destination_offset); + + KUNIT_EXPECT_MEMEQ(test, quadlets, expected, sizeof(expected)); +} + static struct kunit_case ohci_serdes_test_cases[] = { KUNIT_CASE(test_self_id_count_register_deserialization), KUNIT_CASE(test_self_id_receive_buffer_deserialization), + KUNIT_CASE(test_at_data_serdes), {} }; diff --git a/drivers/firewire/ohci.h b/drivers/firewire/ohci.h index 9ed36cfc6cae..a5501996137c 100644 --- a/drivers/firewire/ohci.h +++ b/drivers/firewire/ohci.h @@ -154,6 +154,121 @@ #define OHCI1394_evt_flushed 0xf +// Asynchronous Transmit DMA. +// +// The content of first two quadlets of data for AT DMA is different from the header for IEEE 1394 +// asynchronous packet. + +#define OHCI1394_AT_DATA_Q0_srcBusID_MASK 0x00800000 +#define OHCI1394_AT_DATA_Q0_srcBusID_SHIFT 23 +#define OHCI1394_AT_DATA_Q0_spd_MASK 0x00070000 +#define OHCI1394_AT_DATA_Q0_spd_SHIFT 16 +#define OHCI1394_AT_DATA_Q0_tLabel_MASK 0x0000fc00 +#define OHCI1394_AT_DATA_Q0_tLabel_SHIFT 10 +#define OHCI1394_AT_DATA_Q0_rt_MASK 0x00000300 +#define OHCI1394_AT_DATA_Q0_rt_SHIFT 8 +#define OHCI1394_AT_DATA_Q0_tCode_MASK 0x000000f0 +#define OHCI1394_AT_DATA_Q0_tCode_SHIFT 4 +#define OHCI1394_AT_DATA_Q1_destinationId_MASK 0xffff0000 +#define OHCI1394_AT_DATA_Q1_destinationId_SHIFT 16 +#define OHCI1394_AT_DATA_Q1_destinationOffsetHigh_MASK 0x0000ffff +#define OHCI1394_AT_DATA_Q1_destinationOffsetHigh_SHIFT 0 +#define OHCI1394_AT_DATA_Q1_rCode_MASK 0x0000f000 +#define OHCI1394_AT_DATA_Q1_rCode_SHIFT 12 + +static inline bool ohci1394_at_data_get_src_bus_id(const __le32 *data) +{ + return !!((data[0] & OHCI1394_AT_DATA_Q0_srcBusID_MASK) >> OHCI1394_AT_DATA_Q0_srcBusID_SHIFT); +} + +static inline void ohci1394_at_data_set_src_bus_id(__le32 *data, bool src_bus_id) +{ + data[0] &= cpu_to_le32(~OHCI1394_AT_DATA_Q0_srcBusID_MASK); + data[0] |= cpu_to_le32((src_bus_id << OHCI1394_AT_DATA_Q0_srcBusID_SHIFT) & OHCI1394_AT_DATA_Q0_srcBusID_MASK); +} + +static inline unsigned int ohci1394_at_data_get_speed(const __le32 *data) +{ + return (le32_to_cpu(data[0]) & OHCI1394_AT_DATA_Q0_spd_MASK) >> OHCI1394_AT_DATA_Q0_spd_SHIFT; +} + +static inline void ohci1394_at_data_set_speed(__le32 *data, unsigned int scode) +{ + data[0] &= cpu_to_le32(~OHCI1394_AT_DATA_Q0_spd_MASK); + data[0] |= cpu_to_le32((scode << OHCI1394_AT_DATA_Q0_spd_SHIFT) & OHCI1394_AT_DATA_Q0_spd_MASK); +} + +static inline unsigned int ohci1394_at_data_get_tlabel(const __le32 *data) +{ + return (le32_to_cpu(data[0]) & OHCI1394_AT_DATA_Q0_tLabel_MASK) >> OHCI1394_AT_DATA_Q0_tLabel_SHIFT; +} + +static inline void ohci1394_at_data_set_tlabel(__le32 *data, unsigned int tlabel) +{ + data[0] &= cpu_to_le32(~OHCI1394_AT_DATA_Q0_tLabel_MASK); + data[0] |= cpu_to_le32((tlabel << OHCI1394_AT_DATA_Q0_tLabel_SHIFT) & OHCI1394_AT_DATA_Q0_tLabel_MASK); +} + +static inline unsigned int ohci1394_at_data_get_retry(const __le32 *data) +{ + return (le32_to_cpu(data[0]) & OHCI1394_AT_DATA_Q0_rt_MASK) >> OHCI1394_AT_DATA_Q0_rt_SHIFT; +} + +static inline void ohci1394_at_data_set_retry(__le32 *data, unsigned int retry) +{ + data[0] &= cpu_to_le32(~OHCI1394_AT_DATA_Q0_rt_MASK); + data[0] |= cpu_to_le32((retry << OHCI1394_AT_DATA_Q0_rt_SHIFT) & OHCI1394_AT_DATA_Q0_rt_MASK); +} + +static inline unsigned int ohci1394_at_data_get_tcode(const __le32 *data) +{ + return (le32_to_cpu(data[0]) & OHCI1394_AT_DATA_Q0_tCode_MASK) >> OHCI1394_AT_DATA_Q0_tCode_SHIFT; +} + +static inline void ohci1394_at_data_set_tcode(__le32 *data, unsigned int tcode) +{ + data[0] &= cpu_to_le32(~OHCI1394_AT_DATA_Q0_tCode_MASK); + data[0] |= cpu_to_le32((tcode << OHCI1394_AT_DATA_Q0_tCode_SHIFT) & OHCI1394_AT_DATA_Q0_tCode_MASK); +} + +static inline unsigned int ohci1394_at_data_get_destination_id(const __le32 *data) +{ + return (le32_to_cpu(data[1]) & OHCI1394_AT_DATA_Q1_destinationId_MASK) >> OHCI1394_AT_DATA_Q1_destinationId_SHIFT; +} + +static inline void ohci1394_at_data_set_destination_id(__le32 *data, unsigned int destination_id) +{ + data[1] &= cpu_to_le32(~OHCI1394_AT_DATA_Q1_destinationId_MASK); + data[1] |= cpu_to_le32((destination_id << OHCI1394_AT_DATA_Q1_destinationId_SHIFT) & OHCI1394_AT_DATA_Q1_destinationId_MASK); +} + +static inline u64 ohci1394_at_data_get_destination_offset(const __le32 *data) +{ + u64 hi = (u64)((le32_to_cpu(data[1]) & OHCI1394_AT_DATA_Q1_destinationOffsetHigh_MASK) >> OHCI1394_AT_DATA_Q1_destinationOffsetHigh_SHIFT); + u64 lo = (u64)le32_to_cpu(data[2]); + return (hi << 32) | lo; +} + +static inline void ohci1394_at_data_set_destination_offset(__le32 *data, u64 offset) +{ + u32 hi = (u32)(offset >> 32); + u32 lo = (u32)(offset & 0x00000000ffffffff); + data[1] &= cpu_to_le32(~OHCI1394_AT_DATA_Q1_destinationOffsetHigh_MASK); + data[1] |= cpu_to_le32((hi << OHCI1394_AT_DATA_Q1_destinationOffsetHigh_SHIFT) & OHCI1394_AT_DATA_Q1_destinationOffsetHigh_MASK); + data[2] = cpu_to_le32(lo); +} + +static inline unsigned int ohci1394_at_data_get_rcode(const __le32 *data) +{ + return (le32_to_cpu(data[1]) & OHCI1394_AT_DATA_Q1_rCode_MASK) >> OHCI1394_AT_DATA_Q1_rCode_SHIFT; +} + +static inline void ohci1394_at_data_set_rcode(__le32 *data, unsigned int rcode) +{ + data[1] &= cpu_to_le32(~OHCI1394_AT_DATA_Q1_rCode_MASK); + data[1] |= cpu_to_le32((rcode << OHCI1394_AT_DATA_Q1_rCode_SHIFT) & OHCI1394_AT_DATA_Q1_rCode_MASK); +} + // Self-ID DMA. #define OHCI1394_SelfIDCount_selfIDError_MASK 0x80000000 From db7a8f5519a30a0c3361741e44595d47cb7843ab Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 2 Aug 2024 09:36:04 +0900 Subject: [PATCH 06/55] firewire: ohci: use static inline functions to serialize data of AT DMA This commit replaces current implementation with the helper functions added in the former commit. Link: https://lore.kernel.org/r/20240802003606.109402-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index a0bb0e87e18a..e8429dbbc60d 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1409,12 +1409,6 @@ static int at_context_queue_packet(struct context *ctx, d[0].control = cpu_to_le16(DESCRIPTOR_KEY_IMMEDIATE); d[0].res_count = cpu_to_le16(packet->timestamp); - /* - * The DMA format for asynchronous link packets is different - * from the IEEE1394 layout, so shift the fields around - * accordingly. - */ - tcode = async_header_get_tcode(packet->header); header = (__le32 *) &d[1]; switch (tcode) { @@ -1427,11 +1421,21 @@ static int at_context_queue_packet(struct context *ctx, case TCODE_READ_BLOCK_RESPONSE: case TCODE_LOCK_REQUEST: case TCODE_LOCK_RESPONSE: - header[0] = cpu_to_le32((packet->header[0] & 0xffff) | - (packet->speed << 16)); - header[1] = cpu_to_le32((packet->header[1] & 0xffff) | - (packet->header[0] & 0xffff0000)); - header[2] = cpu_to_le32(packet->header[2]); + ohci1394_at_data_set_src_bus_id(header, false); + ohci1394_at_data_set_speed(header, packet->speed); + ohci1394_at_data_set_tlabel(header, async_header_get_tlabel(packet->header)); + ohci1394_at_data_set_retry(header, async_header_get_retry(packet->header)); + ohci1394_at_data_set_tcode(header, tcode); + + ohci1394_at_data_set_destination_id(header, + async_header_get_destination(packet->header)); + + if (ctx == &ctx->ohci->at_response_ctx) { + ohci1394_at_data_set_rcode(header, async_header_get_rcode(packet->header)); + } else { + ohci1394_at_data_set_destination_offset(header, + async_header_get_offset(packet->header)); + } if (tcode_is_block_packet(tcode)) header[3] = cpu_to_le32(packet->header[3]); @@ -1440,10 +1444,10 @@ static int at_context_queue_packet(struct context *ctx, d[0].req_count = cpu_to_le16(packet->header_length); break; - case TCODE_LINK_INTERNAL: - header[0] = cpu_to_le32((TCODE_LINK_INTERNAL << 4) | - (packet->speed << 16)); + ohci1394_at_data_set_speed(header, packet->speed); + ohci1394_at_data_set_tcode(header, TCODE_LINK_INTERNAL); + header[1] = cpu_to_le32(packet->header[1]); header[2] = cpu_to_le32(packet->header[2]); d[0].req_count = cpu_to_le16(12); From 1ce2a92b5389266cc5b79b5f4042a81ee15f503a Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 2 Aug 2024 09:36:05 +0900 Subject: [PATCH 07/55] firewire: ohci: add static inline functions to serialize/deserialize data of IT DMA In 1394 OHCI specification, the format of data for IT DMA is different from the format of isochronous packet in IEEE 1394 specification, in its spd and srcBusID fields. This commit adds some static inline functions to serialize/deserialize the data of IT DMA. Link: https://lore.kernel.org/r/20240802003606.109402-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci-serdes-test.c | 32 +++++++++++ drivers/firewire/ohci.h | 84 +++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/drivers/firewire/ohci-serdes-test.c b/drivers/firewire/ohci-serdes-test.c index 5d48d53b493a..258f668619ef 100644 --- a/drivers/firewire/ohci-serdes-test.c +++ b/drivers/firewire/ohci-serdes-test.c @@ -73,10 +73,42 @@ static void test_at_data_serdes(struct kunit *test) KUNIT_EXPECT_MEMEQ(test, quadlets, expected, sizeof(expected)); } +static void test_it_data_serdes(struct kunit *test) +{ + static const __le32 expected[] = { + cpu_to_le32(0x000349a7), + cpu_to_le32(0x02300000), + }; + __le32 quadlets[] = {0, 0}; + unsigned int scode = ohci1394_it_data_get_speed(expected); + unsigned int tag = ohci1394_it_data_get_tag(expected); + unsigned int channel = ohci1394_it_data_get_channel(expected); + unsigned int tcode = ohci1394_it_data_get_tcode(expected); + unsigned int sync = ohci1394_it_data_get_sync(expected); + unsigned int data_length = ohci1394_it_data_get_data_length(expected); + + KUNIT_EXPECT_EQ(test, 0x03, scode); + KUNIT_EXPECT_EQ(test, 0x01, tag); + KUNIT_EXPECT_EQ(test, 0x09, channel); + KUNIT_EXPECT_EQ(test, 0x0a, tcode); + KUNIT_EXPECT_EQ(test, 0x7, sync); + KUNIT_EXPECT_EQ(test, 0x0230, data_length); + + ohci1394_it_data_set_speed(quadlets, scode); + ohci1394_it_data_set_tag(quadlets, tag); + ohci1394_it_data_set_channel(quadlets, channel); + ohci1394_it_data_set_tcode(quadlets, tcode); + ohci1394_it_data_set_sync(quadlets, sync); + ohci1394_it_data_set_data_length(quadlets, data_length); + + KUNIT_EXPECT_MEMEQ(test, quadlets, expected, sizeof(expected)); +} + static struct kunit_case ohci_serdes_test_cases[] = { KUNIT_CASE(test_self_id_count_register_deserialization), KUNIT_CASE(test_self_id_receive_buffer_deserialization), KUNIT_CASE(test_at_data_serdes), + KUNIT_CASE(test_it_data_serdes), {} }; diff --git a/drivers/firewire/ohci.h b/drivers/firewire/ohci.h index a5501996137c..218666cfe14a 100644 --- a/drivers/firewire/ohci.h +++ b/drivers/firewire/ohci.h @@ -269,6 +269,90 @@ static inline void ohci1394_at_data_set_rcode(__le32 *data, unsigned int rcode) data[1] |= cpu_to_le32((rcode << OHCI1394_AT_DATA_Q1_rCode_SHIFT) & OHCI1394_AT_DATA_Q1_rCode_MASK); } +// Isochronous Transmit DMA. +// +// The content of first two quadlets of data for IT DMA is different from the header for IEEE 1394 +// isochronous packet. + +#define OHCI1394_IT_DATA_Q0_spd_MASK 0x00070000 +#define OHCI1394_IT_DATA_Q0_spd_SHIFT 16 +#define OHCI1394_IT_DATA_Q0_tag_MASK 0x0000c000 +#define OHCI1394_IT_DATA_Q0_tag_SHIFT 14 +#define OHCI1394_IT_DATA_Q0_chanNum_MASK 0x00003f00 +#define OHCI1394_IT_DATA_Q0_chanNum_SHIFT 8 +#define OHCI1394_IT_DATA_Q0_tcode_MASK 0x000000f0 +#define OHCI1394_IT_DATA_Q0_tcode_SHIFT 4 +#define OHCI1394_IT_DATA_Q0_sy_MASK 0x0000000f +#define OHCI1394_IT_DATA_Q0_sy_SHIFT 0 +#define OHCI1394_IT_DATA_Q1_dataLength_MASK 0xffff0000 +#define OHCI1394_IT_DATA_Q1_dataLength_SHIFT 16 + +static inline unsigned int ohci1394_it_data_get_speed(const __le32 *data) +{ + return (le32_to_cpu(data[0]) & OHCI1394_IT_DATA_Q0_spd_MASK) >> OHCI1394_IT_DATA_Q0_spd_SHIFT; +} + +static inline void ohci1394_it_data_set_speed(__le32 *data, unsigned int scode) +{ + data[0] &= cpu_to_le32(~OHCI1394_IT_DATA_Q0_spd_MASK); + data[0] |= cpu_to_le32((scode << OHCI1394_IT_DATA_Q0_spd_SHIFT) & OHCI1394_IT_DATA_Q0_spd_MASK); +} + +static inline unsigned int ohci1394_it_data_get_tag(const __le32 *data) +{ + return (le32_to_cpu(data[0]) & OHCI1394_IT_DATA_Q0_tag_MASK) >> OHCI1394_IT_DATA_Q0_tag_SHIFT; +} + +static inline void ohci1394_it_data_set_tag(__le32 *data, unsigned int tag) +{ + data[0] &= cpu_to_le32(~OHCI1394_IT_DATA_Q0_tag_MASK); + data[0] |= cpu_to_le32((tag << OHCI1394_IT_DATA_Q0_tag_SHIFT) & OHCI1394_IT_DATA_Q0_tag_MASK); +} + +static inline unsigned int ohci1394_it_data_get_channel(const __le32 *data) +{ + return (le32_to_cpu(data[0]) & OHCI1394_IT_DATA_Q0_chanNum_MASK) >> OHCI1394_IT_DATA_Q0_chanNum_SHIFT; +} + +static inline void ohci1394_it_data_set_channel(__le32 *data, unsigned int channel) +{ + data[0] &= cpu_to_le32(~OHCI1394_IT_DATA_Q0_chanNum_MASK); + data[0] |= cpu_to_le32((channel << OHCI1394_IT_DATA_Q0_chanNum_SHIFT) & OHCI1394_IT_DATA_Q0_chanNum_MASK); +} + +static inline unsigned int ohci1394_it_data_get_tcode(const __le32 *data) +{ + return (le32_to_cpu(data[0]) & OHCI1394_IT_DATA_Q0_tcode_MASK) >> OHCI1394_IT_DATA_Q0_tcode_SHIFT; +} + +static inline void ohci1394_it_data_set_tcode(__le32 *data, unsigned int tcode) +{ + data[0] &= cpu_to_le32(~OHCI1394_IT_DATA_Q0_tcode_MASK); + data[0] |= cpu_to_le32((tcode << OHCI1394_IT_DATA_Q0_tcode_SHIFT) & OHCI1394_IT_DATA_Q0_tcode_MASK); +} + +static inline unsigned int ohci1394_it_data_get_sync(const __le32 *data) +{ + return (le32_to_cpu(data[0]) & OHCI1394_IT_DATA_Q0_sy_MASK) >> OHCI1394_IT_DATA_Q0_sy_SHIFT; +} + +static inline void ohci1394_it_data_set_sync(__le32 *data, unsigned int sync) +{ + data[0] &= cpu_to_le32(~OHCI1394_IT_DATA_Q0_sy_MASK); + data[0] |= cpu_to_le32((sync << OHCI1394_IT_DATA_Q0_sy_SHIFT) & OHCI1394_IT_DATA_Q0_sy_MASK); +} + +static inline unsigned int ohci1394_it_data_get_data_length(const __le32 *data) +{ + return (le32_to_cpu(data[1]) & OHCI1394_IT_DATA_Q1_dataLength_MASK) >> OHCI1394_IT_DATA_Q1_dataLength_SHIFT; +} + +static inline void ohci1394_it_data_set_data_length(__le32 *data, unsigned int data_length) +{ + data[1] &= cpu_to_le32(~OHCI1394_IT_DATA_Q1_dataLength_MASK); + data[1] |= cpu_to_le32((data_length << OHCI1394_IT_DATA_Q1_dataLength_SHIFT) & OHCI1394_IT_DATA_Q1_dataLength_MASK); +} + // Self-ID DMA. #define OHCI1394_SelfIDCount_selfIDError_MASK 0x80000000 From 8a96e7be8c33cbd809ca13c9590167a336e2e322 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 2 Aug 2024 09:36:06 +0900 Subject: [PATCH 08/55] firewire: ohci: use static inline functions to serialize data of IT DMA THis commit replaces current implementation with the helper functions added in the former commit. Link: https://lore.kernel.org/r/20240802003606.109402-5-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index e8429dbbc60d..8f2bbd0569fb 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -162,13 +162,6 @@ struct context { struct tasklet_struct tasklet; }; -#define IT_HEADER_SY(v) ((v) << 0) -#define IT_HEADER_TCODE(v) ((v) << 4) -#define IT_HEADER_CHANNEL(v) ((v) << 8) -#define IT_HEADER_TAG(v) ((v) << 14) -#define IT_HEADER_SPEED(v) ((v) << 16) -#define IT_HEADER_DATA_LENGTH(v) ((v) << 16) - struct iso_context { struct fw_iso_context base; struct context context; @@ -1457,9 +1450,14 @@ static int at_context_queue_packet(struct context *ctx, break; case TCODE_STREAM_DATA: - header[0] = cpu_to_le32((packet->header[0] & 0xffff) | - (packet->speed << 16)); - header[1] = cpu_to_le32(packet->header[0] & 0xffff0000); + ohci1394_it_data_set_speed(header, packet->speed); + ohci1394_it_data_set_tag(header, isoc_header_get_tag(packet->header[0])); + ohci1394_it_data_set_channel(header, isoc_header_get_channel(packet->header[0])); + ohci1394_it_data_set_tcode(header, TCODE_STREAM_DATA); + ohci1394_it_data_set_sync(header, isoc_header_get_sy(packet->header[0])); + + ohci1394_it_data_set_data_length(header, isoc_header_get_data_length(packet->header[0])); + d[0].req_count = cpu_to_le16(8); break; @@ -3403,14 +3401,14 @@ static int queue_iso_transmit(struct iso_context *ctx, d[0].branch_address = cpu_to_le32(d_bus | z); header = (__le32 *) &d[1]; - header[0] = cpu_to_le32(IT_HEADER_SY(p->sy) | - IT_HEADER_TAG(p->tag) | - IT_HEADER_TCODE(TCODE_STREAM_DATA) | - IT_HEADER_CHANNEL(ctx->base.channel) | - IT_HEADER_SPEED(ctx->base.speed)); - header[1] = - cpu_to_le32(IT_HEADER_DATA_LENGTH(p->header_length + - p->payload_length)); + + ohci1394_it_data_set_speed(header, ctx->base.speed); + ohci1394_it_data_set_tag(header, p->tag); + ohci1394_it_data_set_channel(header, ctx->base.channel); + ohci1394_it_data_set_tcode(header, TCODE_STREAM_DATA); + ohci1394_it_data_set_sync(header, p->sy); + + ohci1394_it_data_set_data_length(header, p->header_length + p->payload_length); } if (p->header_length > 0) { From 232f72b10da74054055558b05235a725c3b90468 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:53:52 +0900 Subject: [PATCH 09/55] firewire: core: use guard macro to maintain static packet data for phy configuration The core function provide a kernel API to send phy configuration packet. Current implementation of the feature uses packet object allocated statically. The concurrent access to the object is protected by static mutex. This commit uses guard macro to maintain the mutex. Link: https://lore.kernel.org/r/20240805085408.251763-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-transaction.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index a89c841a7dbe..2a2cbd6e2f9b 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -494,7 +494,7 @@ void fw_send_phy_config(struct fw_card *card, phy_packet_phy_config_set_gap_count(&data, gap_count); phy_packet_phy_config_set_gap_count_optimization(&data, true); - mutex_lock(&phy_config_mutex); + guard(mutex)(&phy_config_mutex); async_header_set_tcode(phy_config_packet.header, TCODE_LINK_INTERNAL); phy_config_packet.header[1] = data; @@ -508,8 +508,6 @@ void fw_send_phy_config(struct fw_card *card, card->driver->send_request(card, &phy_config_packet); wait_for_completion_timeout(&phy_config_done, timeout); - - mutex_unlock(&phy_config_mutex); } static struct fw_address_handler *lookup_overlapping_address_handler( From 57b40ec6db94dd325a76ad0a0fd1173a7cbfcc81 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:53:53 +0900 Subject: [PATCH 10/55] firewire: core: use guard macro to maintain the list of card The core function maintains registered cards by list. The concurrent access to the list is protected by static mutex. This commit uses guard macro to maintain the mutex. Link: https://lore.kernel.org/r/20240805085408.251763-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-card.c | 44 +++++++++++++++--------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index f8b99dd6cd82..79a5b19e9d18 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -168,7 +168,6 @@ static size_t required_space(struct fw_descriptor *desc) int fw_core_add_descriptor(struct fw_descriptor *desc) { size_t i; - int ret; /* * Check descriptor is valid; the length of all blocks in the @@ -182,29 +181,25 @@ int fw_core_add_descriptor(struct fw_descriptor *desc) if (i != desc->length) return -EINVAL; - mutex_lock(&card_mutex); + guard(mutex)(&card_mutex); - if (config_rom_length + required_space(desc) > 256) { - ret = -EBUSY; - } else { - list_add_tail(&desc->link, &descriptor_list); - config_rom_length += required_space(desc); + if (config_rom_length + required_space(desc) > 256) + return -EBUSY; + + list_add_tail(&desc->link, &descriptor_list); + config_rom_length += required_space(desc); + descriptor_count++; + if (desc->immediate > 0) descriptor_count++; - if (desc->immediate > 0) - descriptor_count++; - update_config_roms(); - ret = 0; - } + update_config_roms(); - mutex_unlock(&card_mutex); - - return ret; + return 0; } EXPORT_SYMBOL(fw_core_add_descriptor); void fw_core_remove_descriptor(struct fw_descriptor *desc) { - mutex_lock(&card_mutex); + guard(mutex)(&card_mutex); list_del(&desc->link); config_rom_length -= required_space(desc); @@ -212,8 +207,6 @@ void fw_core_remove_descriptor(struct fw_descriptor *desc) if (desc->immediate > 0) descriptor_count--; update_config_roms(); - - mutex_unlock(&card_mutex); } EXPORT_SYMBOL(fw_core_remove_descriptor); @@ -587,16 +580,16 @@ int fw_card_add(struct fw_card *card, card->link_speed = link_speed; card->guid = guid; - mutex_lock(&card_mutex); + guard(mutex)(&card_mutex); generate_config_rom(card, tmp_config_rom); ret = card->driver->enable(card, tmp_config_rom, config_rom_length); - if (ret == 0) - list_add_tail(&card->link, &card_list); + if (ret < 0) + return ret; - mutex_unlock(&card_mutex); + list_add_tail(&card->link, &card_list); - return ret; + return 0; } EXPORT_SYMBOL(fw_card_add); @@ -720,9 +713,8 @@ void fw_core_remove_card(struct fw_card *card) PHY_LINK_ACTIVE | PHY_CONTENDER, 0); fw_schedule_bus_reset(card, false, true); - mutex_lock(&card_mutex); - list_del_init(&card->link); - mutex_unlock(&card_mutex); + scoped_guard(mutex, &card_mutex) + list_del_init(&card->link); /* Switch off most of the card driver interface. */ dummy_driver.free_iso_context = card->driver->free_iso_context; From 044ce581ab28f640d3997cb38e1ddda782238abb Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:53:54 +0900 Subject: [PATCH 11/55] firewire: core: use guard macro to maintain the list of cdev clients The core function maintains userspace clients by the list in fw_device object associated to the operated character device. The concurrent access to the list is protected by mutex in the object. This commit uses guard macro to maintain the mutex. Link: https://lore.kernel.org/r/20240805085408.251763-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 619048dcfd72..a51aabb963fb 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -375,10 +375,10 @@ static void for_each_client(struct fw_device *device, { struct client *c; - mutex_lock(&device->client_list_mutex); + guard(mutex)(&device->client_list_mutex); + list_for_each_entry(c, &device->client_list, link) callback(c); - mutex_unlock(&device->client_list_mutex); } static int schedule_reallocations(int id, void *p, void *data) @@ -470,7 +470,7 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg) if (ret != 0) return -EFAULT; - mutex_lock(&client->device->client_list_mutex); + guard(mutex)(&client->device->client_list_mutex); client->bus_reset_closure = a->bus_reset_closure; if (a->bus_reset != 0) { @@ -481,8 +481,6 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg) if (ret == 0 && list_empty(&client->link)) list_add_tail(&client->link, &client->device->client_list); - mutex_unlock(&client->device->client_list_mutex); - return ret ? -EFAULT : 0; } @@ -1884,9 +1882,8 @@ static int fw_device_op_release(struct inode *inode, struct file *file) list_del(&client->phy_receiver_link); spin_unlock_irq(&client->device->card->lock); - mutex_lock(&client->device->client_list_mutex); - list_del(&client->link); - mutex_unlock(&client->device->client_list_mutex); + scoped_guard(mutex, &client->device->client_list_mutex) + list_del(&client->link); if (client->iso_context) fw_iso_context_destroy(client->iso_context); From 6d72fbc81634b3c9423bf96207121ed7dd6bdc11 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:53:55 +0900 Subject: [PATCH 12/55] firewire: ohci: use guard macro to serialize accesses to phy registers The 1394 OHCI driver protects concurrent accesses to phy registers by mutex object in fw_ohci structure. This commit uses guard macro to maintain the mutex. Link: https://lore.kernel.org/r/20240805085408.251763-5-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 71 +++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 8f2bbd0569fb..1461e008d265 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -713,26 +713,20 @@ static int read_paged_phy_reg(struct fw_ohci *ohci, int page, int addr) static int ohci_read_phy_reg(struct fw_card *card, int addr) { struct fw_ohci *ohci = fw_ohci(card); - int ret; - mutex_lock(&ohci->phy_reg_mutex); - ret = read_phy_reg(ohci, addr); - mutex_unlock(&ohci->phy_reg_mutex); + guard(mutex)(&ohci->phy_reg_mutex); - return ret; + return read_phy_reg(ohci, addr); } static int ohci_update_phy_reg(struct fw_card *card, int addr, int clear_bits, int set_bits) { struct fw_ohci *ohci = fw_ohci(card); - int ret; - mutex_lock(&ohci->phy_reg_mutex); - ret = update_phy_reg(ohci, addr, clear_bits, set_bits); - mutex_unlock(&ohci->phy_reg_mutex); + guard(mutex)(&ohci->phy_reg_mutex); - return ret; + return update_phy_reg(ohci, addr, clear_bits, set_bits); } static inline dma_addr_t ar_buffer_bus(struct ar_context *ctx, unsigned int i) @@ -1882,13 +1876,15 @@ static int get_status_for_port(struct fw_ohci *ohci, int port_index, { int reg; - mutex_lock(&ohci->phy_reg_mutex); - reg = write_phy_reg(ohci, 7, port_index); - if (reg >= 0) + scoped_guard(mutex, &ohci->phy_reg_mutex) { + reg = write_phy_reg(ohci, 7, port_index); + if (reg < 0) + return reg; + reg = read_phy_reg(ohci, 8); - mutex_unlock(&ohci->phy_reg_mutex); - if (reg < 0) - return reg; + if (reg < 0) + return reg; + } switch (reg & 0x0f) { case 0x06: @@ -1929,26 +1925,31 @@ static int get_self_id_pos(struct fw_ohci *ohci, u32 self_id, static bool initiated_reset(struct fw_ohci *ohci) { int reg; - int ret = false; - mutex_lock(&ohci->phy_reg_mutex); - reg = write_phy_reg(ohci, 7, 0xe0); /* Select page 7 */ - if (reg >= 0) { - reg = read_phy_reg(ohci, 8); - reg |= 0x40; - reg = write_phy_reg(ohci, 8, reg); /* set PMODE bit */ - if (reg >= 0) { - reg = read_phy_reg(ohci, 12); /* read register 12 */ - if (reg >= 0) { - if ((reg & 0x08) == 0x08) { - /* bit 3 indicates "initiated reset" */ - ret = true; - } - } - } - } - mutex_unlock(&ohci->phy_reg_mutex); - return ret; + guard(mutex)(&ohci->phy_reg_mutex); + + // Select page 7 + reg = write_phy_reg(ohci, 7, 0xe0); + if (reg < 0) + return reg; + + reg = read_phy_reg(ohci, 8); + if (reg < 0) + return reg; + + // set PMODE bit + reg |= 0x40; + reg = write_phy_reg(ohci, 8, reg); + if (reg < 0) + return reg; + + // read register 12 + reg = read_phy_reg(ohci, 12); + if (reg < 0) + return reg; + + // bit 3 indicates "initiated reset" + return !!((reg & 0x08) == 0x08); } /* From eade1e1ba2236f8f51e357a690ca70a41fe34d2d Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:53:56 +0900 Subject: [PATCH 13/55] firewire: core: use guard macro to maintain RCU scope for transaction address handler The core function maintains address handlers by list. RCU is utilized for efficient read access to any entries in the list. This commit uses guard macro to maintain RCU locking and releasing. Link: https://lore.kernel.org/r/20240805085408.251763-6-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-transaction.c | 35 +++++++++++++---------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 2a2cbd6e2f9b..a0224d4d8e11 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -925,16 +925,14 @@ static void handle_exclusive_region_request(struct fw_card *card, if (tcode == TCODE_LOCK_REQUEST) tcode = 0x10 + async_header_get_extended_tcode(p->header); - rcu_read_lock(); - handler = lookup_enclosing_address_handler(&address_handler_list, - offset, request->length); - if (handler) - handler->address_callback(card, request, - tcode, destination, source, - p->generation, offset, - request->data, request->length, - handler->callback_data); - rcu_read_unlock(); + scoped_guard(rcu) { + handler = lookup_enclosing_address_handler(&address_handler_list, offset, + request->length); + if (handler) + handler->address_callback(card, request, tcode, destination, source, + p->generation, offset, request->data, + request->length, handler->callback_data); + } if (!handler) fw_send_response(card, request, RCODE_ADDRESS_ERROR); @@ -967,17 +965,14 @@ static void handle_fcp_region_request(struct fw_card *card, return; } - rcu_read_lock(); - list_for_each_entry_rcu(handler, &address_handler_list, link) { - if (is_enclosing_handler(handler, offset, request->length)) - handler->address_callback(card, request, tcode, - destination, source, - p->generation, offset, - request->data, - request->length, - handler->callback_data); + scoped_guard(rcu) { + list_for_each_entry_rcu(handler, &address_handler_list, link) { + if (is_enclosing_handler(handler, offset, request->length)) + handler->address_callback(card, request, tcode, destination, source, + p->generation, offset, request->data, + request->length, handler->callback_data); + } } - rcu_read_unlock(); fw_send_response(card, request, RCODE_COMPLETE); } From 2a6a58f06bd5d123a5b248a565b90b5df26c9ea8 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:53:57 +0900 Subject: [PATCH 14/55] firewire: core: use guard macro to access to IDR for fw_device The core function maintains the instance of fw_device structure by IDR. The concurrent access to IDR is protected by static read/write semaphore. The semaphore is also utilized to protect concurrent access to the content of configuration ROM cached to the instance so that the cache is swapped to the latest one. This commit uses guard macro to maintain the mutex. Link: https://lore.kernel.org/r/20240805085408.251763-7-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 23 +++++------ drivers/firewire/core-device.c | 73 +++++++++++++++------------------- 2 files changed, 41 insertions(+), 55 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index a51aabb963fb..c3baf688bb70 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -454,21 +454,18 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg) a->version = FW_CDEV_KERNEL_VERSION; a->card = client->device->card->index; - down_read(&fw_device_rwsem); + scoped_guard(rwsem_read, &fw_device_rwsem) { + if (a->rom != 0) { + size_t want = a->rom_length; + size_t have = client->device->config_rom_length * 4; - if (a->rom != 0) { - size_t want = a->rom_length; - size_t have = client->device->config_rom_length * 4; - - ret = copy_to_user(u64_to_uptr(a->rom), - client->device->config_rom, min(want, have)); + ret = copy_to_user(u64_to_uptr(a->rom), client->device->config_rom, + min(want, have)); + if (ret != 0) + return -EFAULT; + } + a->rom_length = client->device->config_rom_length * 4; } - a->rom_length = client->device->config_rom_length * 4; - - up_read(&fw_device_rwsem); - - if (ret != 0) - return -EFAULT; guard(mutex)(&client->device->client_list_mutex); diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 00e9a13e6c45..d695ec2f1efe 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -288,7 +288,7 @@ static ssize_t show_immediate(struct device *dev, const u32 *directories[] = {NULL, NULL}; int i, value = -1; - down_read(&fw_device_rwsem); + guard(rwsem_read)(&fw_device_rwsem); if (is_fw_unit(dev)) { directories[0] = fw_unit(dev)->directory; @@ -317,8 +317,6 @@ static ssize_t show_immediate(struct device *dev, } } - up_read(&fw_device_rwsem); - if (value < 0) return -ENOENT; @@ -339,7 +337,7 @@ static ssize_t show_text_leaf(struct device *dev, char dummy_buf[2]; int i, ret = -ENOENT; - down_read(&fw_device_rwsem); + guard(rwsem_read)(&fw_device_rwsem); if (is_fw_unit(dev)) { directories[0] = fw_unit(dev)->directory; @@ -382,15 +380,14 @@ static ssize_t show_text_leaf(struct device *dev, } } - if (ret >= 0) { - /* Strip trailing whitespace and add newline. */ - while (ret > 0 && isspace(buf[ret - 1])) - ret--; - strcpy(buf + ret, "\n"); - ret++; - } + if (ret < 0) + return ret; - up_read(&fw_device_rwsem); + // Strip trailing whitespace and add newline. + while (ret > 0 && isspace(buf[ret - 1])) + ret--; + strcpy(buf + ret, "\n"); + ret++; return ret; } @@ -466,10 +463,10 @@ static ssize_t config_rom_show(struct device *dev, struct fw_device *device = fw_device(dev); size_t length; - down_read(&fw_device_rwsem); + guard(rwsem_read)(&fw_device_rwsem); + length = device->config_rom_length * 4; memcpy(buf, device->config_rom, length); - up_read(&fw_device_rwsem); return length; } @@ -478,13 +475,10 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fw_device *device = fw_device(dev); - int ret; - down_read(&fw_device_rwsem); - ret = sysfs_emit(buf, "0x%08x%08x\n", device->config_rom[3], device->config_rom[4]); - up_read(&fw_device_rwsem); + guard(rwsem_read)(&fw_device_rwsem); - return ret; + return sysfs_emit(buf, "0x%08x%08x\n", device->config_rom[3], device->config_rom[4]); } static ssize_t is_local_show(struct device *dev, @@ -524,7 +518,8 @@ static ssize_t units_show(struct device *dev, struct fw_csr_iterator ci; int key, value, i = 0; - down_read(&fw_device_rwsem); + guard(rwsem_read)(&fw_device_rwsem); + fw_csr_iterator_init(&ci, &device->config_rom[ROOT_DIR_OFFSET]); while (fw_csr_iterator_next(&ci, &key, &value)) { if (key != (CSR_UNIT | CSR_DIRECTORY)) @@ -533,7 +528,6 @@ static ssize_t units_show(struct device *dev, if (i >= PAGE_SIZE - (8 + 1 + 8 + 1)) break; } - up_read(&fw_device_rwsem); if (i) buf[i - 1] = '\n'; @@ -729,10 +723,10 @@ static int read_config_rom(struct fw_device *device, int generation) goto out; } - down_write(&fw_device_rwsem); - device->config_rom = new_rom; - device->config_rom_length = length; - up_write(&fw_device_rwsem); + scoped_guard(rwsem_write, &fw_device_rwsem) { + device->config_rom = new_rom; + device->config_rom_length = length; + } kfree(old_rom); ret = RCODE_COMPLETE; @@ -826,11 +820,11 @@ struct fw_device *fw_device_get_by_devt(dev_t devt) { struct fw_device *device; - down_read(&fw_device_rwsem); + guard(rwsem_read)(&fw_device_rwsem); + device = idr_find(&fw_device_idr, MINOR(devt)); if (device) fw_device_get(device); - up_read(&fw_device_rwsem); return device; } @@ -882,9 +876,8 @@ static void fw_device_shutdown(struct work_struct *work) device_for_each_child(&device->device, NULL, shutdown_unit); device_unregister(&device->device); - down_write(&fw_device_rwsem); - idr_remove(&fw_device_idr, minor); - up_write(&fw_device_rwsem); + scoped_guard(rwsem_write, &fw_device_rwsem) + idr_remove(&fw_device_idr, minor); fw_device_put(device); } @@ -958,7 +951,7 @@ static int lookup_existing_device(struct device *dev, void *data) if (!is_fw_device(dev)) return 0; - down_read(&fw_device_rwsem); /* serialize config_rom access */ + guard(rwsem_read)(&fw_device_rwsem); // serialize config_rom access spin_lock_irq(&card->lock); /* serialize node access */ if (memcmp(old->config_rom, new->config_rom, 6 * 4) == 0 && @@ -990,7 +983,6 @@ static int lookup_existing_device(struct device *dev, void *data) } spin_unlock_irq(&card->lock); - up_read(&fw_device_rwsem); return match; } @@ -1099,13 +1091,11 @@ static void fw_device_init(struct work_struct *work) device_initialize(&device->device); fw_device_get(device); - down_write(&fw_device_rwsem); - minor = idr_alloc(&fw_device_idr, device, 0, 1 << MINORBITS, - GFP_KERNEL); - up_write(&fw_device_rwsem); - - if (minor < 0) - goto error; + scoped_guard(rwsem_write, &fw_device_rwsem) { + minor = idr_alloc(&fw_device_idr, device, 0, 1 << MINORBITS, GFP_KERNEL); + if (minor < 0) + goto error; + } device->device.bus = &fw_bus_type; device->device.type = &fw_device_type; @@ -1165,9 +1155,8 @@ static void fw_device_init(struct work_struct *work) return; error_with_cdev: - down_write(&fw_device_rwsem); - idr_remove(&fw_device_idr, minor); - up_write(&fw_device_rwsem); + scoped_guard(rwsem_write, &fw_device_rwsem) + idr_remove(&fw_device_idr, minor); error: fw_device_put(device); /* fw_device_idr's reference */ From 3a335229c5eb13b8b9b8f7ede386df31cc94c1b1 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:53:58 +0900 Subject: [PATCH 15/55] firewire: core: use guard macro to maintain the list of address handler for transaction The core function maintains address handlers by list. It is protected by spinlock to insert and remove entry to the list. This commit uses guard macro to maintain the spinlock. Link: https://lore.kernel.org/r/20240805085408.251763-8-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-transaction.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index a0224d4d8e11..a006daf385e9 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -596,7 +596,7 @@ int fw_core_add_address_handler(struct fw_address_handler *handler, handler->length == 0) return -EINVAL; - spin_lock(&address_handler_list_lock); + guard(spinlock)(&address_handler_list_lock); handler->offset = region->start; while (handler->offset + handler->length <= region->end) { @@ -615,8 +615,6 @@ int fw_core_add_address_handler(struct fw_address_handler *handler, } } - spin_unlock(&address_handler_list_lock); - return ret; } EXPORT_SYMBOL(fw_core_add_address_handler); @@ -632,9 +630,9 @@ EXPORT_SYMBOL(fw_core_add_address_handler); */ void fw_core_remove_address_handler(struct fw_address_handler *handler) { - spin_lock(&address_handler_list_lock); - list_del_rcu(&handler->link); - spin_unlock(&address_handler_list_lock); + scoped_guard(spinlock, &address_handler_list_lock) + list_del_rcu(&handler->link); + synchronize_rcu(); } EXPORT_SYMBOL(fw_core_remove_address_handler); From bacf921c42bbec7974ffb2b49e30f06aa602580e Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:53:59 +0900 Subject: [PATCH 16/55] firewire: core: use guard macro to disable local IRQ The core function provides an operation for userspace application to retrieve current value of CYCLE_TIMER register with several types of system time. In the operation, local interrupt is disables so that the access of the register and ktime are done atomically. This commit uses guard macro to disable/enable local interrupts. Link: https://lore.kernel.org/r/20240805085408.251763-9-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index c3baf688bb70..90e9dfed8681 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1263,29 +1263,27 @@ static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) struct fw_card *card = client->device->card; struct timespec64 ts = {0, 0}; u32 cycle_time = 0; - int ret = 0; + int ret; - local_irq_disable(); + guard(irq)(); ret = fw_card_read_cycle_time(card, &cycle_time); if (ret < 0) - goto end; + return ret; switch (a->clk_id) { case CLOCK_REALTIME: ktime_get_real_ts64(&ts); break; case CLOCK_MONOTONIC: ktime_get_ts64(&ts); break; case CLOCK_MONOTONIC_RAW: ktime_get_raw_ts64(&ts); break; default: - ret = -EINVAL; + return -EINVAL; } -end: - local_irq_enable(); a->tv_sec = ts.tv_sec; a->tv_nsec = ts.tv_nsec; a->cycle_timer = cycle_time; - return ret; + return 0; } static int ioctl_get_cycle_timer(struct client *client, union ioctl_arg *arg) From 4f1f91aeca50c8ca0bd84baab3c92a0a3b4db2d7 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:54:00 +0900 Subject: [PATCH 17/55] firewire: core: use guard macro to maintain list of events for userspace clients The core function maintains events to userspace by list in the instance of client. The concurrent access to the list is protected by spinlock in the instance. This commit uses guard macro to maintain the spinlock. Link: https://lore.kernel.org/r/20240805085408.251763-10-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 90e9dfed8681..2e2199eaa05b 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -287,19 +287,17 @@ static int fw_device_op_open(struct inode *inode, struct file *file) static void queue_event(struct client *client, struct event *event, void *data0, size_t size0, void *data1, size_t size1) { - unsigned long flags; - event->v[0].data = data0; event->v[0].size = size0; event->v[1].data = data1; event->v[1].size = size1; - spin_lock_irqsave(&client->lock, flags); - if (client->in_shutdown) - kfree(event); - else - list_add_tail(&event->link, &client->event_list); - spin_unlock_irqrestore(&client->lock, flags); + scoped_guard(spinlock_irqsave, &client->lock) { + if (client->in_shutdown) + kfree(event); + else + list_add_tail(&event->link, &client->event_list); + } wake_up_interruptible(&client->wait); } @@ -321,10 +319,10 @@ static int dequeue_event(struct client *client, fw_device_is_shutdown(client->device)) return -ENODEV; - spin_lock_irq(&client->lock); - event = list_first_entry(&client->event_list, struct event, link); - list_del(&event->link); - spin_unlock_irq(&client->lock); + scoped_guard(spinlock_irq, &client->lock) { + event = list_first_entry(&client->event_list, struct event, link); + list_del(&event->link); + } total = 0; for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) { @@ -1887,9 +1885,8 @@ static int fw_device_op_release(struct inode *inode, struct file *file) fw_iso_buffer_destroy(&client->buffer, client->device->card); /* Freeze client->resource_idr and client->event_list */ - spin_lock_irq(&client->lock); - client->in_shutdown = true; - spin_unlock_irq(&client->lock); + scoped_guard(spinlock_irq, &client->lock) + client->in_shutdown = true; wait_event(client->tx_flush_wait, !has_outbound_transactions(client)); From d3816b8b988038bd1c4b7fe08f130ba3783a3432 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:54:01 +0900 Subject: [PATCH 18/55] firewire: core: use guard macro to maintain IDR of isochronous resources for userspace clients The core function provides UAPI to maintain isochronous resources allocated by userspace clients across bus resets automatically. The resources are maintained by IDR and the concurrent access to it is protected by spinlock in the instance of client. This commit uses guard macro to maintain the spinlock. Link: https://lore.kernel.org/r/20240805085408.251763-11-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 131 ++++++++++++++++------------------- 1 file changed, 59 insertions(+), 72 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 2e2199eaa05b..c2d24cc5c1f1 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -399,9 +399,9 @@ static void queue_bus_reset_event(struct client *client) queue_event(client, &e->event, &e->reset, sizeof(e->reset), NULL, 0); - spin_lock_irq(&client->lock); + guard(spinlock_irq)(&client->lock); + idr_for_each(&client->resource_idr, schedule_reallocations, client); - spin_unlock_irq(&client->lock); } void fw_device_cdev_update(struct fw_device *device) @@ -483,25 +483,23 @@ static int add_client_resource(struct client *client, struct client_resource *resource, gfp_t gfp_mask) { bool preload = gfpflags_allow_blocking(gfp_mask); - unsigned long flags; int ret; if (preload) idr_preload(gfp_mask); - spin_lock_irqsave(&client->lock, flags); - if (client->in_shutdown) - ret = -ECANCELED; - else - ret = idr_alloc(&client->resource_idr, resource, 0, 0, - GFP_NOWAIT); - if (ret >= 0) { - resource->handle = ret; - client_get(client); - schedule_if_iso_resource(resource); + scoped_guard(spinlock_irqsave, &client->lock) { + if (client->in_shutdown) + ret = -ECANCELED; + else + ret = idr_alloc(&client->resource_idr, resource, 0, 0, GFP_NOWAIT); + if (ret >= 0) { + resource->handle = ret; + client_get(client); + schedule_if_iso_resource(resource); + } } - spin_unlock_irqrestore(&client->lock, flags); if (preload) idr_preload_end(); @@ -514,14 +512,14 @@ static int release_client_resource(struct client *client, u32 handle, { struct client_resource *resource; - spin_lock_irq(&client->lock); - if (client->in_shutdown) - resource = NULL; - else - resource = idr_find(&client->resource_idr, handle); - if (resource && resource->release == release) - idr_remove(&client->resource_idr, handle); - spin_unlock_irq(&client->lock); + scoped_guard(spinlock_irq, &client->lock) { + if (client->in_shutdown) + resource = NULL; + else + resource = idr_find(&client->resource_idr, handle); + if (resource && resource->release == release) + idr_remove(&client->resource_idr, handle); + } if (!(resource && resource->release == release)) return -EINVAL; @@ -546,13 +544,12 @@ static void complete_transaction(struct fw_card *card, int rcode, u32 request_ts { struct outbound_transaction_event *e = data; struct client *client = e->client; - unsigned long flags; - spin_lock_irqsave(&client->lock, flags); - idr_remove(&client->resource_idr, e->r.resource.handle); - if (client->in_shutdown) - wake_up(&client->tx_flush_wait); - spin_unlock_irqrestore(&client->lock, flags); + scoped_guard(spinlock_irqsave, &client->lock) { + idr_remove(&client->resource_idr, e->r.resource.handle); + if (client->in_shutdown) + wake_up(&client->tx_flush_wait); + } switch (e->rsp.without_tstamp.type) { case FW_CDEV_EVENT_RESPONSE: @@ -1307,25 +1304,24 @@ static void iso_resource_work(struct work_struct *work) int generation, channel, bandwidth, todo; bool skip, free, success; - spin_lock_irq(&client->lock); - generation = client->device->generation; - todo = r->todo; - /* Allow 1000ms grace period for other reallocations. */ - if (todo == ISO_RES_ALLOC && - time_before64(get_jiffies_64(), - client->device->card->reset_jiffies + HZ)) { - schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3)); - skip = true; - } else { - /* We could be called twice within the same generation. */ - skip = todo == ISO_RES_REALLOC && - r->generation == generation; + scoped_guard(spinlock_irq, &client->lock) { + generation = client->device->generation; + todo = r->todo; + // Allow 1000ms grace period for other reallocations. + if (todo == ISO_RES_ALLOC && + time_before64(get_jiffies_64(), client->device->card->reset_jiffies + HZ)) { + schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3)); + skip = true; + } else { + // We could be called twice within the same generation. + skip = todo == ISO_RES_REALLOC && + r->generation == generation; + } + free = todo == ISO_RES_DEALLOC || + todo == ISO_RES_ALLOC_ONCE || + todo == ISO_RES_DEALLOC_ONCE; + r->generation = generation; } - free = todo == ISO_RES_DEALLOC || - todo == ISO_RES_ALLOC_ONCE || - todo == ISO_RES_DEALLOC_ONCE; - r->generation = generation; - spin_unlock_irq(&client->lock); if (skip) goto out; @@ -1348,24 +1344,20 @@ static void iso_resource_work(struct work_struct *work) success = channel >= 0 || bandwidth > 0; - spin_lock_irq(&client->lock); - /* - * Transit from allocation to reallocation, except if the client - * requested deallocation in the meantime. - */ - if (r->todo == ISO_RES_ALLOC) - r->todo = ISO_RES_REALLOC; - /* - * Allocation or reallocation failure? Pull this resource out of the - * idr and prepare for deletion, unless the client is shutting down. - */ - if (r->todo == ISO_RES_REALLOC && !success && - !client->in_shutdown && - idr_remove(&client->resource_idr, r->resource.handle)) { - client_put(client); - free = true; + scoped_guard(spinlock_irq, &client->lock) { + // Transit from allocation to reallocation, except if the client + // requested deallocation in the meantime. + if (r->todo == ISO_RES_ALLOC) + r->todo = ISO_RES_REALLOC; + // Allocation or reallocation failure? Pull this resource out of the + // idr and prepare for deletion, unless the client is shutting down. + if (r->todo == ISO_RES_REALLOC && !success && + !client->in_shutdown && + idr_remove(&client->resource_idr, r->resource.handle)) { + client_put(client); + free = true; + } } - spin_unlock_irq(&client->lock); if (todo == ISO_RES_ALLOC && channel >= 0) r->channels = 1ULL << channel; @@ -1403,10 +1395,10 @@ static void release_iso_resource(struct client *client, struct iso_resource *r = container_of(resource, struct iso_resource, resource); - spin_lock_irq(&client->lock); + guard(spinlock_irq)(&client->lock); + r->todo = ISO_RES_DEALLOC; schedule_iso_resource(r, 0); - spin_unlock_irq(&client->lock); } static int init_iso_resource(struct client *client, @@ -1845,14 +1837,9 @@ static int is_outbound_transaction_resource(int id, void *p, void *data) static int has_outbound_transactions(struct client *client) { - int ret; + guard(spinlock_irq)(&client->lock); - spin_lock_irq(&client->lock); - ret = idr_for_each(&client->resource_idr, - is_outbound_transaction_resource, NULL); - spin_unlock_irq(&client->lock); - - return ret; + return idr_for_each(&client->resource_idr, is_outbound_transaction_resource, NULL); } static int shutdown_resource(int id, void *p, void *data) From cf123b01286085f178f20454ec920526807f9fa0 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:54:02 +0900 Subject: [PATCH 19/55] firewire: core: use guard macro to maintain isochronous context for userspace client The core function allows one isochronous contexts per userspace client. The concurrent access to the context is protected by spinlock in the instance of client. This commit uses guard macro to maintain the spinlock. Link: https://lore.kernel.org/r/20240805085408.251763-12-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index c2d24cc5c1f1..ac6f9ad9e88e 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1062,10 +1062,10 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) if (client->version < FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW) context->drop_overflow_headers = true; - /* We only support one context at this time. */ - spin_lock_irq(&client->lock); + // We only support one context at this time. + guard(spinlock_irq)(&client->lock); + if (client->iso_context != NULL) { - spin_unlock_irq(&client->lock); fw_iso_context_destroy(context); return -EBUSY; @@ -1075,7 +1075,6 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) client->device->card, iso_dma_direction(context)); if (ret < 0) { - spin_unlock_irq(&client->lock); fw_iso_context_destroy(context); return ret; @@ -1084,7 +1083,6 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) } client->iso_closure = a->closure; client->iso_context = context; - spin_unlock_irq(&client->lock); a->handle = 0; @@ -1806,16 +1804,15 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) if (ret < 0) return ret; - spin_lock_irq(&client->lock); - if (client->iso_context) { - ret = fw_iso_buffer_map_dma(&client->buffer, - client->device->card, - iso_dma_direction(client->iso_context)); - client->buffer_is_mapped = (ret == 0); + scoped_guard(spinlock_irq, &client->lock) { + if (client->iso_context) { + ret = fw_iso_buffer_map_dma(&client->buffer, client->device->card, + iso_dma_direction(client->iso_context)); + if (ret < 0) + goto fail; + client->buffer_is_mapped = true; + } } - spin_unlock_irq(&client->lock); - if (ret < 0) - goto fail; ret = vm_map_pages_zero(vma, client->buffer.pages, client->buffer.page_count); From b9545448f095f23f72fc6b28aa9bfb84a4ac9d40 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:54:03 +0900 Subject: [PATCH 20/55] firewire: core: use guard macro to maintain list of receivers for phy configuration packets The core function maintains clients to receive phy configuration packets by list in the instance of fw_card. The concurrent access to the list is protected by spinlock in the instance. This commit uses guard macro to maintain the list. Link: https://lore.kernel.org/r/20240805085408.251763-13-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index ac6f9ad9e88e..f32f8667ef2c 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1659,26 +1659,22 @@ static int ioctl_receive_phy_packets(struct client *client, union ioctl_arg *arg if (!client->device->is_local) return -ENOSYS; - spin_lock_irq(&card->lock); + guard(spinlock_irq)(&card->lock); list_move_tail(&client->phy_receiver_link, &card->phy_receiver_list); client->phy_receiver_closure = a->closure; - spin_unlock_irq(&card->lock); - return 0; } void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p) { struct client *client; - struct inbound_phy_packet_event *e; - unsigned long flags; - spin_lock_irqsave(&card->lock, flags); + guard(spinlock_irqsave)(&card->lock); list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) { - e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC); + struct inbound_phy_packet_event *e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC); if (e == NULL) break; @@ -1706,8 +1702,6 @@ void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p) queue_event(client, &e->event, &e->phy_packet, sizeof(*pp) + 8, NULL, 0); } } - - spin_unlock_irqrestore(&card->lock, flags); } static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { @@ -1855,9 +1849,8 @@ static int fw_device_op_release(struct inode *inode, struct file *file) struct client *client = file->private_data; struct event *event, *next_event; - spin_lock_irq(&client->device->card->lock); - list_del(&client->phy_receiver_link); - spin_unlock_irq(&client->device->card->lock); + scoped_guard(spinlock_irq, &client->device->card->lock) + list_del(&client->phy_receiver_link); scoped_guard(mutex, &client->device->client_list_mutex) list_del(&client->link); From d320bac904f9e3a0d1af0c314b768fb1f545704e Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:54:04 +0900 Subject: [PATCH 21/55] firewire: core: use guard macro to maintain list of asynchronous transaction The core function maintains pending asynchronous transactions by list in the instance of fw_card. The concurrent access to the list is protected by spinlock in the instance. This commit uses guard macro to maintain the spinlock. Link: https://lore.kernel.org/r/20240805085408.251763-14-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-transaction.c | 85 ++++++++++++----------------- 1 file changed, 34 insertions(+), 51 deletions(-) diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index a006daf385e9..0f58a5d13d28 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -49,35 +49,31 @@ static int close_transaction(struct fw_transaction *transaction, struct fw_card u32 response_tstamp) { struct fw_transaction *t = NULL, *iter; - unsigned long flags; - spin_lock_irqsave(&card->lock, flags); - list_for_each_entry(iter, &card->transaction_list, link) { - if (iter == transaction) { - if (!try_cancel_split_timeout(iter)) { - spin_unlock_irqrestore(&card->lock, flags); - goto timed_out; + scoped_guard(spinlock_irqsave, &card->lock) { + list_for_each_entry(iter, &card->transaction_list, link) { + if (iter == transaction) { + if (try_cancel_split_timeout(iter)) { + list_del_init(&iter->link); + card->tlabel_mask &= ~(1ULL << iter->tlabel); + t = iter; + } + break; } - list_del_init(&iter->link); - card->tlabel_mask &= ~(1ULL << iter->tlabel); - t = iter; - break; } } - spin_unlock_irqrestore(&card->lock, flags); - if (t) { - if (!t->with_tstamp) { - t->callback.without_tstamp(card, rcode, NULL, 0, t->callback_data); - } else { - t->callback.with_tstamp(card, rcode, t->packet.timestamp, response_tstamp, - NULL, 0, t->callback_data); - } - return 0; + if (!t) + return -ENOENT; + + if (!t->with_tstamp) { + t->callback.without_tstamp(card, rcode, NULL, 0, t->callback_data); + } else { + t->callback.with_tstamp(card, rcode, t->packet.timestamp, response_tstamp, NULL, 0, + t->callback_data); } - timed_out: - return -ENOENT; + return 0; } /* @@ -121,16 +117,13 @@ static void split_transaction_timeout_callback(struct timer_list *timer) { struct fw_transaction *t = from_timer(t, timer, split_timeout_timer); struct fw_card *card = t->card; - unsigned long flags; - spin_lock_irqsave(&card->lock, flags); - if (list_empty(&t->link)) { - spin_unlock_irqrestore(&card->lock, flags); - return; + scoped_guard(spinlock_irqsave, &card->lock) { + if (list_empty(&t->link)) + return; + list_del(&t->link); + card->tlabel_mask &= ~(1ULL << t->tlabel); } - list_del(&t->link); - card->tlabel_mask &= ~(1ULL << t->tlabel); - spin_unlock_irqrestore(&card->lock, flags); if (!t->with_tstamp) { t->callback.without_tstamp(card, RCODE_CANCELLED, NULL, 0, t->callback_data); @@ -143,20 +136,14 @@ static void split_transaction_timeout_callback(struct timer_list *timer) static void start_split_transaction_timeout(struct fw_transaction *t, struct fw_card *card) { - unsigned long flags; + guard(spinlock_irqsave)(&card->lock); - spin_lock_irqsave(&card->lock, flags); - - if (list_empty(&t->link) || WARN_ON(t->is_split_transaction)) { - spin_unlock_irqrestore(&card->lock, flags); + if (list_empty(&t->link) || WARN_ON(t->is_split_transaction)) return; - } t->is_split_transaction = true; mod_timer(&t->split_timeout_timer, jiffies + card->split_timeout_jiffies); - - spin_unlock_irqrestore(&card->lock, flags); } static u32 compute_split_timeout_timestamp(struct fw_card *card, u32 request_timestamp); @@ -1015,7 +1002,6 @@ EXPORT_SYMBOL(fw_core_handle_request); void fw_core_handle_response(struct fw_card *card, struct fw_packet *p) { struct fw_transaction *t = NULL, *iter; - unsigned long flags; u32 *data; size_t data_length; int tcode, tlabel, source, rcode; @@ -1054,26 +1040,23 @@ void fw_core_handle_response(struct fw_card *card, struct fw_packet *p) break; } - spin_lock_irqsave(&card->lock, flags); - list_for_each_entry(iter, &card->transaction_list, link) { - if (iter->node_id == source && iter->tlabel == tlabel) { - if (!try_cancel_split_timeout(iter)) { - spin_unlock_irqrestore(&card->lock, flags); - goto timed_out; + scoped_guard(spinlock_irqsave, &card->lock) { + list_for_each_entry(iter, &card->transaction_list, link) { + if (iter->node_id == source && iter->tlabel == tlabel) { + if (try_cancel_split_timeout(iter)) { + list_del_init(&iter->link); + card->tlabel_mask &= ~(1ULL << iter->tlabel); + t = iter; + } + break; } - list_del_init(&iter->link); - card->tlabel_mask &= ~(1ULL << iter->tlabel); - t = iter; - break; } } - spin_unlock_irqrestore(&card->lock, flags); trace_async_response_inbound((uintptr_t)t, card->index, p->generation, p->speed, p->ack, p->timestamp, p->header, data, data_length / 4); if (!t) { - timed_out: fw_notice(card, "unsolicited response (source %x, tlabel %x)\n", source, tlabel); return; From 27310d5616227c2ba8c0cedc5cdbe236042738b7 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:54:05 +0900 Subject: [PATCH 22/55] firewire: core: use guard macro to maintain properties of fw_card The core functions uses spinlock in instance of fw_card structure to protect concurrent access to properties in the instance. This commit uses guard macro to maintain the spinlock. Link: https://lore.kernel.org/r/20240805085408.251763-15-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-card.c | 16 +++++++--------- drivers/firewire/core-cdev.c | 4 +--- drivers/firewire/core-device.c | 10 +++------- drivers/firewire/core-iso.c | 5 ++--- drivers/firewire/core-topology.c | 5 +---- drivers/firewire/core-transaction.c | 12 +++++------- 6 files changed, 19 insertions(+), 33 deletions(-) diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 79a5b19e9d18..e80b762888fa 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -374,11 +374,11 @@ static void bm_work(struct work_struct *work) bm_id = be32_to_cpu(transaction_data[0]); - spin_lock_irq(&card->lock); - if (rcode == RCODE_COMPLETE && generation == card->generation) - card->bm_node_id = - bm_id == 0x3f ? local_id : 0xffc0 | bm_id; - spin_unlock_irq(&card->lock); + scoped_guard(spinlock_irq, &card->lock) { + if (rcode == RCODE_COMPLETE && generation == card->generation) + card->bm_node_id = + bm_id == 0x3f ? local_id : 0xffc0 | bm_id; + } if (rcode == RCODE_COMPLETE && bm_id != 0x3f) { /* Somebody else is BM. Only act as IRM. */ @@ -707,7 +707,6 @@ EXPORT_SYMBOL_GPL(fw_card_release); void fw_core_remove_card(struct fw_card *card) { struct fw_card_driver dummy_driver = dummy_driver_template; - unsigned long flags; card->driver->update_phy_reg(card, 4, PHY_LINK_ACTIVE | PHY_CONTENDER, 0); @@ -721,9 +720,8 @@ void fw_core_remove_card(struct fw_card *card) dummy_driver.stop_iso = card->driver->stop_iso; card->driver = &dummy_driver; - spin_lock_irqsave(&card->lock, flags); - fw_destroy_nodes(card); - spin_unlock_irqrestore(&card->lock, flags); + scoped_guard(spinlock_irqsave, &card->lock) + fw_destroy_nodes(card); /* Wait for all users, especially device workqueue jobs, to finish. */ fw_card_put(card); diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index f32f8667ef2c..672a37fa8343 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -354,7 +354,7 @@ static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, { struct fw_card *card = client->device->card; - spin_lock_irq(&card->lock); + guard(spinlock_irq)(&card->lock); event->closure = client->bus_reset_closure; event->type = FW_CDEV_EVENT_BUS_RESET; @@ -364,8 +364,6 @@ static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, event->bm_node_id = card->bm_node_id; event->irm_node_id = card->irm_node->node_id; event->root_node_id = card->root_node->node_id; - - spin_unlock_irq(&card->lock); } static void for_each_client(struct fw_device *device, diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index d695ec2f1efe..bec7e05f6ab8 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -886,16 +886,14 @@ static void fw_device_release(struct device *dev) { struct fw_device *device = fw_device(dev); struct fw_card *card = device->card; - unsigned long flags; /* * Take the card lock so we don't set this to NULL while a * FW_NODE_UPDATED callback is being handled or while the * bus manager work looks at this node. */ - spin_lock_irqsave(&card->lock, flags); - device->node->data = NULL; - spin_unlock_irqrestore(&card->lock, flags); + scoped_guard(spinlock_irqsave, &card->lock) + device->node->data = NULL; fw_node_put(device->node); kfree(device->config_rom); @@ -952,7 +950,7 @@ static int lookup_existing_device(struct device *dev, void *data) return 0; guard(rwsem_read)(&fw_device_rwsem); // serialize config_rom access - spin_lock_irq(&card->lock); /* serialize node access */ + guard(spinlock_irq)(&card->lock); // serialize node access if (memcmp(old->config_rom, new->config_rom, 6 * 4) == 0 && atomic_cmpxchg(&old->state, @@ -982,8 +980,6 @@ static int lookup_existing_device(struct device *dev, void *data) match = 1; } - spin_unlock_irq(&card->lock); - return match; } diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index b3eda38a36f3..101433b8bb51 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -375,9 +375,8 @@ void fw_iso_resource_manage(struct fw_card *card, int generation, u32 channels_lo = channels_mask >> 32; /* channels 63...32 */ int irm_id, ret, c = -EINVAL; - spin_lock_irq(&card->lock); - irm_id = card->irm_node->node_id; - spin_unlock_irq(&card->lock); + scoped_guard(spinlock_irq, &card->lock) + irm_id = card->irm_node->node_id; if (channels_hi) c = manage_channel(card, irm_id, generation, channels_hi, diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index 46e6eb287d24..6adadb11962e 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -455,11 +455,10 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, int self_id_count, u32 *self_ids, bool bm_abdicate) { struct fw_node *local_node; - unsigned long flags; trace_bus_reset_handle(card->index, generation, node_id, bm_abdicate, self_ids, self_id_count); - spin_lock_irqsave(&card->lock, flags); + guard(spinlock_irqsave)(&card->lock); /* * If the selfID buffer is not the immediate successor of the @@ -500,7 +499,5 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, } else { update_tree(card, local_node); } - - spin_unlock_irqrestore(&card->lock, flags); } EXPORT_SYMBOL(fw_core_handle_bus_reset); diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 0f58a5d13d28..14af84541e83 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1160,7 +1160,6 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, int reg = offset & ~CSR_REGISTER_BASE; __be32 *data = payload; int rcode = RCODE_COMPLETE; - unsigned long flags; switch (reg) { case CSR_PRIORITY_BUDGET: @@ -1202,10 +1201,10 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, if (tcode == TCODE_READ_QUADLET_REQUEST) { *data = cpu_to_be32(card->split_timeout_hi); } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) { - spin_lock_irqsave(&card->lock, flags); + guard(spinlock_irqsave)(&card->lock); + card->split_timeout_hi = be32_to_cpu(*data) & 7; update_split_timeout(card); - spin_unlock_irqrestore(&card->lock, flags); } else { rcode = RCODE_TYPE_ERROR; } @@ -1215,11 +1214,10 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, if (tcode == TCODE_READ_QUADLET_REQUEST) { *data = cpu_to_be32(card->split_timeout_lo); } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) { - spin_lock_irqsave(&card->lock, flags); - card->split_timeout_lo = - be32_to_cpu(*data) & 0xfff80000; + guard(spinlock_irqsave)(&card->lock); + + card->split_timeout_lo = be32_to_cpu(*data) & 0xfff80000; update_split_timeout(card); - spin_unlock_irqrestore(&card->lock, flags); } else { rcode = RCODE_TYPE_ERROR; } From b10e56fd0eae18d77afd3c859fc13f1d665a936c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:54:06 +0900 Subject: [PATCH 23/55] firewire: ohci: use guard macro to maintain bus time The 1394 OHCI driver maintains bus time to respond to querying request. The concurrent access to the bus time is protected by spinlock. This commit uses guard macro to maintain the spinlock. Link: https://lore.kernel.org/r/20240805085408.251763-16-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 1461e008d265..5cb7c7603c2c 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -2300,9 +2300,8 @@ static irqreturn_t irq_handler(int irq, void *data) handle_dead_contexts(ohci); if (event & OHCI1394_cycle64Seconds) { - spin_lock(&ohci->lock); + guard(spinlock)(&ohci->lock); update_bus_time(ohci); - spin_unlock(&ohci->lock); } else flush_writes(ohci); @@ -2762,7 +2761,6 @@ static int ohci_enable_phys_dma(struct fw_card *card, static u32 ohci_read_csr(struct fw_card *card, int csr_offset) { struct fw_ohci *ohci = fw_ohci(card); - unsigned long flags; u32 value; switch (csr_offset) { @@ -2786,16 +2784,14 @@ static u32 ohci_read_csr(struct fw_card *card, int csr_offset) return get_cycle_time(ohci); case CSR_BUS_TIME: - /* - * We might be called just after the cycle timer has wrapped - * around but just before the cycle64Seconds handler, so we - * better check here, too, if the bus time needs to be updated. - */ - spin_lock_irqsave(&ohci->lock, flags); - value = update_bus_time(ohci); - spin_unlock_irqrestore(&ohci->lock, flags); - return value; + { + // We might be called just after the cycle timer has wrapped around but just before + // the cycle64Seconds handler, so we better check here, too, if the bus time needs + // to be updated. + guard(spinlock_irqsave)(&ohci->lock); + return update_bus_time(ohci); + } case CSR_BUSY_TIMEOUT: value = reg_read(ohci, OHCI1394_ATRetries); return (value >> 4) & 0x0ffff00f; @@ -2813,7 +2809,6 @@ static u32 ohci_read_csr(struct fw_card *card, int csr_offset) static void ohci_write_csr(struct fw_card *card, int csr_offset, u32 value) { struct fw_ohci *ohci = fw_ohci(card); - unsigned long flags; switch (csr_offset) { case CSR_STATE_CLEAR: @@ -2849,12 +2844,11 @@ static void ohci_write_csr(struct fw_card *card, int csr_offset, u32 value) break; case CSR_BUS_TIME: - spin_lock_irqsave(&ohci->lock, flags); - ohci->bus_time = (update_bus_time(ohci) & 0x40) | - (value & ~0x7f); - spin_unlock_irqrestore(&ohci->lock, flags); + { + guard(spinlock_irqsave)(&ohci->lock); + ohci->bus_time = (update_bus_time(ohci) & 0x40) | (value & ~0x7f); break; - + } case CSR_BUSY_TIMEOUT: value = (value & 0xf) | ((value & 0xf) << 4) | ((value & 0xf) << 8) | ((value & 0x0ffff000) << 4); From 86baade948832b45a6b5ef1e47282d94dd99e2ba Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:54:07 +0900 Subject: [PATCH 24/55] firewire: ohci: use guard macro to maintain image of configuration ROM The 1394 OHCI driver uses spinlock for the process to update local configuration ROM. This commit uses guard macro to maintain the spinlock. Link: https://lore.kernel.org/r/20240805085408.251763-17-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 110 +++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 64 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 5cb7c7603c2c..368420e4b414 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -2139,53 +2139,42 @@ static void bus_reset_work(struct work_struct *work) at_context_flush(&ohci->at_request_ctx); at_context_flush(&ohci->at_response_ctx); - spin_lock_irq(&ohci->lock); + scoped_guard(spinlock_irq, &ohci->lock) { + ohci->generation = generation; + reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset); + reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset); - ohci->generation = generation; - reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset); - reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset); + if (ohci->quirks & QUIRK_RESET_PACKET) + ohci->request_generation = generation; - if (ohci->quirks & QUIRK_RESET_PACKET) - ohci->request_generation = generation; + // This next bit is unrelated to the AT context stuff but we have to do it under the + // spinlock also. If a new config rom was set up before this reset, the old one is + // now no longer in use and we can free it. Update the config rom pointers to point + // to the current config rom and clear the next_config_rom pointer so a new update + // can take place. + if (ohci->next_config_rom != NULL) { + if (ohci->next_config_rom != ohci->config_rom) { + free_rom = ohci->config_rom; + free_rom_bus = ohci->config_rom_bus; + } + ohci->config_rom = ohci->next_config_rom; + ohci->config_rom_bus = ohci->next_config_rom_bus; + ohci->next_config_rom = NULL; - /* - * This next bit is unrelated to the AT context stuff but we - * have to do it under the spinlock also. If a new config rom - * was set up before this reset, the old one is now no longer - * in use and we can free it. Update the config rom pointers - * to point to the current config rom and clear the - * next_config_rom pointer so a new update can take place. - */ - - if (ohci->next_config_rom != NULL) { - if (ohci->next_config_rom != ohci->config_rom) { - free_rom = ohci->config_rom; - free_rom_bus = ohci->config_rom_bus; + // Restore config_rom image and manually update config_rom registers. + // Writing the header quadlet will indicate that the config rom is ready, + // so we do that last. + reg_write(ohci, OHCI1394_BusOptions, be32_to_cpu(ohci->config_rom[2])); + ohci->config_rom[0] = ohci->next_header; + reg_write(ohci, OHCI1394_ConfigROMhdr, be32_to_cpu(ohci->next_header)); } - ohci->config_rom = ohci->next_config_rom; - ohci->config_rom_bus = ohci->next_config_rom_bus; - ohci->next_config_rom = NULL; - /* - * Restore config_rom image and manually update - * config_rom registers. Writing the header quadlet - * will indicate that the config rom is ready, so we - * do that last. - */ - reg_write(ohci, OHCI1394_BusOptions, - be32_to_cpu(ohci->config_rom[2])); - ohci->config_rom[0] = ohci->next_header; - reg_write(ohci, OHCI1394_ConfigROMhdr, - be32_to_cpu(ohci->next_header)); + if (param_remote_dma) { + reg_write(ohci, OHCI1394_PhyReqFilterHiSet, ~0); + reg_write(ohci, OHCI1394_PhyReqFilterLoSet, ~0); + } } - if (param_remote_dma) { - reg_write(ohci, OHCI1394_PhyReqFilterHiSet, ~0); - reg_write(ohci, OHCI1394_PhyReqFilterLoSet, ~0); - } - - spin_unlock_irq(&ohci->lock); - if (free_rom) dmam_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, free_rom, free_rom_bus); @@ -2626,34 +2615,27 @@ static int ohci_set_config_rom(struct fw_card *card, if (next_config_rom == NULL) return -ENOMEM; - spin_lock_irq(&ohci->lock); + scoped_guard(spinlock_irq, &ohci->lock) { + // If there is not an already pending config_rom update, push our new allocation + // into the ohci->next_config_rom and then mark the local variable as null so that + // we won't deallocate the new buffer. + // + // OTOH, if there is a pending config_rom update, just use that buffer with the new + // config_rom data, and let this routine free the unused DMA allocation. + if (ohci->next_config_rom == NULL) { + ohci->next_config_rom = next_config_rom; + ohci->next_config_rom_bus = next_config_rom_bus; + next_config_rom = NULL; + } - /* - * If there is not an already pending config_rom update, - * push our new allocation into the ohci->next_config_rom - * and then mark the local variable as null so that we - * won't deallocate the new buffer. - * - * OTOH, if there is a pending config_rom update, just - * use that buffer with the new config_rom data, and - * let this routine free the unused DMA allocation. - */ + copy_config_rom(ohci->next_config_rom, config_rom, length); - if (ohci->next_config_rom == NULL) { - ohci->next_config_rom = next_config_rom; - ohci->next_config_rom_bus = next_config_rom_bus; - next_config_rom = NULL; + ohci->next_header = config_rom[0]; + ohci->next_config_rom[0] = 0; + + reg_write(ohci, OHCI1394_ConfigROMmap, ohci->next_config_rom_bus); } - copy_config_rom(ohci->next_config_rom, config_rom, length); - - ohci->next_header = config_rom[0]; - ohci->next_config_rom[0] = 0; - - reg_write(ohci, OHCI1394_ConfigROMmap, ohci->next_config_rom_bus); - - spin_unlock_irq(&ohci->lock); - /* If we didn't use the DMA allocation, delete it. */ if (next_config_rom != NULL) { dmam_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, next_config_rom, From e4c8b8014f3fb6a324ba388fc676d176a35bcdb8 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 5 Aug 2024 17:54:08 +0900 Subject: [PATCH 25/55] firewire: ohci: use guard macro to serialize operations for isochronous contexts The 1394 OHCI driver uses spinlock to serialize operations for isochronous contexts. This commit uses guard macro to maintain the spinlock. Link: https://lore.kernel.org/r/20240805085408.251763-18-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 188 +++++++++++++++++----------------------- 1 file changed, 80 insertions(+), 108 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 368420e4b414..e1d24e0ec991 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1173,13 +1173,11 @@ static void context_tasklet(unsigned long data) break; if (old_desc != desc) { - /* If we've advanced to the next buffer, move the - * previous buffer to the free list. */ - unsigned long flags; + // If we've advanced to the next buffer, move the previous buffer to the + // free list. old_desc->used = 0; - spin_lock_irqsave(&ctx->ohci->lock, flags); + guard(spinlock_irqsave)(&ctx->ohci->lock); list_move_tail(&old_desc->list, &ctx->buffer_list); - spin_unlock_irqrestore(&ctx->ohci->lock, flags); } ctx->last = last; } @@ -2122,14 +2120,12 @@ static void bus_reset_work(struct work_struct *work) return; } - /* FIXME: Document how the locking works. */ - spin_lock_irq(&ohci->lock); - - ohci->generation = -1; /* prevent AT packet queueing */ - context_stop(&ohci->at_request_ctx); - context_stop(&ohci->at_response_ctx); - - spin_unlock_irq(&ohci->lock); + // FIXME: Document how the locking works. + scoped_guard(spinlock_irq, &ohci->lock) { + ohci->generation = -1; // prevent AT packet queueing + context_stop(&ohci->at_request_ctx); + context_stop(&ohci->at_response_ctx); + } /* * Per OHCI 1.2 draft, clause 7.2.3.3, hardware may leave unsent @@ -2704,7 +2700,6 @@ static int ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation) { struct fw_ohci *ohci = fw_ohci(card); - unsigned long flags; int n, ret = 0; if (param_remote_dma) @@ -2715,12 +2710,10 @@ static int ohci_enable_phys_dma(struct fw_card *card, * interrupt bit. Clear physReqResourceAllBuses on bus reset. */ - spin_lock_irqsave(&ohci->lock, flags); + guard(spinlock_irqsave)(&ohci->lock); - if (ohci->generation != generation) { - ret = -ESTALE; - goto out; - } + if (ohci->generation != generation) + return -ESTALE; /* * Note, if the node ID contains a non-local bus ID, physical DMA is @@ -2734,8 +2727,6 @@ static int ohci_enable_phys_dma(struct fw_card *card, reg_write(ohci, OHCI1394_PhyReqFilterHiSet, 1 << (n - 32)); flush_writes(ohci); - out: - spin_unlock_irqrestore(&ohci->lock, flags); return ret; } @@ -3076,55 +3067,53 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, u32 *mask, regs; int index, ret = -EBUSY; - spin_lock_irq(&ohci->lock); + scoped_guard(spinlock_irq, &ohci->lock) { + switch (type) { + case FW_ISO_CONTEXT_TRANSMIT: + mask = &ohci->it_context_mask; + callback = handle_it_packet; + index = ffs(*mask) - 1; + if (index >= 0) { + *mask &= ~(1 << index); + regs = OHCI1394_IsoXmitContextBase(index); + ctx = &ohci->it_context_list[index]; + } + break; - switch (type) { - case FW_ISO_CONTEXT_TRANSMIT: - mask = &ohci->it_context_mask; - callback = handle_it_packet; - index = ffs(*mask) - 1; - if (index >= 0) { - *mask &= ~(1 << index); - regs = OHCI1394_IsoXmitContextBase(index); - ctx = &ohci->it_context_list[index]; + case FW_ISO_CONTEXT_RECEIVE: + channels = &ohci->ir_context_channels; + mask = &ohci->ir_context_mask; + callback = handle_ir_packet_per_buffer; + index = *channels & 1ULL << channel ? ffs(*mask) - 1 : -1; + if (index >= 0) { + *channels &= ~(1ULL << channel); + *mask &= ~(1 << index); + regs = OHCI1394_IsoRcvContextBase(index); + ctx = &ohci->ir_context_list[index]; + } + break; + + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + mask = &ohci->ir_context_mask; + callback = handle_ir_buffer_fill; + index = !ohci->mc_allocated ? ffs(*mask) - 1 : -1; + if (index >= 0) { + ohci->mc_allocated = true; + *mask &= ~(1 << index); + regs = OHCI1394_IsoRcvContextBase(index); + ctx = &ohci->ir_context_list[index]; + } + break; + + default: + index = -1; + ret = -ENOSYS; } - break; - case FW_ISO_CONTEXT_RECEIVE: - channels = &ohci->ir_context_channels; - mask = &ohci->ir_context_mask; - callback = handle_ir_packet_per_buffer; - index = *channels & 1ULL << channel ? ffs(*mask) - 1 : -1; - if (index >= 0) { - *channels &= ~(1ULL << channel); - *mask &= ~(1 << index); - regs = OHCI1394_IsoRcvContextBase(index); - ctx = &ohci->ir_context_list[index]; - } - break; - - case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: - mask = &ohci->ir_context_mask; - callback = handle_ir_buffer_fill; - index = !ohci->mc_allocated ? ffs(*mask) - 1 : -1; - if (index >= 0) { - ohci->mc_allocated = true; - *mask &= ~(1 << index); - regs = OHCI1394_IsoRcvContextBase(index); - ctx = &ohci->ir_context_list[index]; - } - break; - - default: - index = -1; - ret = -ENOSYS; + if (index < 0) + return ERR_PTR(ret); } - spin_unlock_irq(&ohci->lock); - - if (index < 0) - return ERR_PTR(ret); - memset(ctx, 0, sizeof(*ctx)); ctx->header_length = 0; ctx->header = (void *) __get_free_page(GFP_KERNEL); @@ -3146,20 +3135,18 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, out_with_header: free_page((unsigned long)ctx->header); out: - spin_lock_irq(&ohci->lock); + scoped_guard(spinlock_irq, &ohci->lock) { + switch (type) { + case FW_ISO_CONTEXT_RECEIVE: + *channels |= 1ULL << channel; + break; - switch (type) { - case FW_ISO_CONTEXT_RECEIVE: - *channels |= 1ULL << channel; - break; - - case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: - ohci->mc_allocated = false; - break; + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + ohci->mc_allocated = false; + break; + } + *mask |= 1 << index; } - *mask |= 1 << index; - - spin_unlock_irq(&ohci->lock); return ERR_PTR(ret); } @@ -3243,14 +3230,13 @@ static void ohci_free_iso_context(struct fw_iso_context *base) { struct fw_ohci *ohci = fw_ohci(base->card); struct iso_context *ctx = container_of(base, struct iso_context, base); - unsigned long flags; int index; ohci_stop_iso(base); context_release(&ctx->context); free_page((unsigned long)ctx->header); - spin_lock_irqsave(&ohci->lock, flags); + guard(spinlock_irqsave)(&ohci->lock); switch (base->type) { case FW_ISO_CONTEXT_TRANSMIT: @@ -3272,38 +3258,29 @@ static void ohci_free_iso_context(struct fw_iso_context *base) ohci->mc_allocated = false; break; } - - spin_unlock_irqrestore(&ohci->lock, flags); } static int ohci_set_iso_channels(struct fw_iso_context *base, u64 *channels) { struct fw_ohci *ohci = fw_ohci(base->card); - unsigned long flags; - int ret; switch (base->type) { case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + { + guard(spinlock_irqsave)(&ohci->lock); - spin_lock_irqsave(&ohci->lock, flags); - - /* Don't allow multichannel to grab other contexts' channels. */ + // Don't allow multichannel to grab other contexts' channels. if (~ohci->ir_context_channels & ~ohci->mc_channels & *channels) { *channels = ohci->ir_context_channels; - ret = -EBUSY; + return -EBUSY; } else { set_multichannel_mask(ohci, *channels); - ret = 0; + return 0; } - - spin_unlock_irqrestore(&ohci->lock, flags); - - break; - default: - ret = -EINVAL; } - - return ret; + default: + return -EINVAL; + } } #ifdef CONFIG_PM @@ -3573,24 +3550,19 @@ static int ohci_queue_iso(struct fw_iso_context *base, unsigned long payload) { struct iso_context *ctx = container_of(base, struct iso_context, base); - unsigned long flags; - int ret = -ENOSYS; - spin_lock_irqsave(&ctx->context.ohci->lock, flags); + guard(spinlock_irqsave)(&ctx->context.ohci->lock); + switch (base->type) { case FW_ISO_CONTEXT_TRANSMIT: - ret = queue_iso_transmit(ctx, packet, buffer, payload); - break; + return queue_iso_transmit(ctx, packet, buffer, payload); case FW_ISO_CONTEXT_RECEIVE: - ret = queue_iso_packet_per_buffer(ctx, packet, buffer, payload); - break; + return queue_iso_packet_per_buffer(ctx, packet, buffer, payload); case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: - ret = queue_iso_buffer_fill(ctx, packet, buffer, payload); - break; + return queue_iso_buffer_fill(ctx, packet, buffer, payload); + default: + return -ENOSYS; } - spin_unlock_irqrestore(&ctx->context.ohci->lock, flags); - - return ret; } static void ohci_flush_queue_iso(struct fw_iso_context *base) From ebb9d3ca8f7efc1b6a2f1750d1058eda444883d0 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 10 Aug 2024 16:04:03 +0900 Subject: [PATCH 26/55] firewire: core: correct range of block for case of switch statement A commit d8527cab6c31 ("firewire: cdev: implement new event to notify response subaction with time stamp") adds an additional case, FW_CDEV_EVENT_RESPONSE2, into switch statement in complete_transaction(). However, the range of block is beyond to the case label and reaches neibour default label. This commit corrects the range of block. Fortunately, it has few impacts in practice since the local variable in the scope under the label is not used in codes under default label. Fixes: d8527cab6c31 ("firewire: cdev: implement new event to notify response subaction with time stamp") Link: https://lore.kernel.org/r/20240810070403.36801-1-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 672a37fa8343..c211bb19c94e 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -589,11 +589,11 @@ static void complete_transaction(struct fw_card *card, int rcode, u32 request_ts queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, NULL, 0); break; + } default: WARN_ON(1); break; } - } /* Drop the idr's reference */ client_put(client); From 7e5a7725a0e4030cf37f8b93ff9d4328166a3b85 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 12 Aug 2024 10:42:50 +0900 Subject: [PATCH 27/55] firewire: core: replace IDR with XArray to maintain fw_device In core function, the instances of fw_device corresponding to firewire device node in system are maintained by IDR. As of kernel v6.0, IDR has been superseded by XArray and deprecated. This commit replaces the usage of IDR with XArray to maintain the device instances. The instance of XArray is allocated statically, and initialized with XA_FLAGS_ALLOC so that the index of allocated entry starts with zero and available as the minor identifier of device node. Link: https://lore.kernel.org/r/20240812014251.165492-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-device.c | 21 +++++++++++---------- drivers/firewire/core-transaction.c | 3 +-- drivers/firewire/core.h | 3 ++- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index bec7e05f6ab8..9f3276aa463a 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -813,7 +812,7 @@ static int shutdown_unit(struct device *device, void *data) */ DECLARE_RWSEM(fw_device_rwsem); -DEFINE_IDR(fw_device_idr); +DEFINE_XARRAY_ALLOC(fw_device_xa); int fw_cdev_major; struct fw_device *fw_device_get_by_devt(dev_t devt) @@ -822,7 +821,7 @@ struct fw_device *fw_device_get_by_devt(dev_t devt) guard(rwsem_read)(&fw_device_rwsem); - device = idr_find(&fw_device_idr, MINOR(devt)); + device = xa_load(&fw_device_xa, MINOR(devt)); if (device) fw_device_get(device); @@ -858,7 +857,6 @@ static void fw_device_shutdown(struct work_struct *work) { struct fw_device *device = container_of(work, struct fw_device, work.work); - int minor = MINOR(device->device.devt); if (time_before64(get_jiffies_64(), device->card->reset_jiffies + SHUTDOWN_DELAY) @@ -877,7 +875,7 @@ static void fw_device_shutdown(struct work_struct *work) device_unregister(&device->device); scoped_guard(rwsem_write, &fw_device_rwsem) - idr_remove(&fw_device_idr, minor); + xa_erase(&fw_device_xa, MINOR(device->device.devt)); fw_device_put(device); } @@ -1049,7 +1047,8 @@ static void fw_device_init(struct work_struct *work) container_of(work, struct fw_device, work.work); struct fw_card *card = device->card; struct device *revived_dev; - int minor, ret; + u32 minor; + int ret; /* * All failure paths here set node->data to NULL, so that we @@ -1087,9 +1086,11 @@ static void fw_device_init(struct work_struct *work) device_initialize(&device->device); fw_device_get(device); + scoped_guard(rwsem_write, &fw_device_rwsem) { - minor = idr_alloc(&fw_device_idr, device, 0, 1 << MINORBITS, GFP_KERNEL); - if (minor < 0) + // The index of allocated entry is used for minor identifier of device node. + ret = xa_alloc(&fw_device_xa, &minor, device, XA_LIMIT(0, MINORMASK), GFP_KERNEL); + if (ret < 0) goto error; } @@ -1152,9 +1153,9 @@ static void fw_device_init(struct work_struct *work) error_with_cdev: scoped_guard(rwsem_write, &fw_device_rwsem) - idr_remove(&fw_device_idr, minor); + xa_erase(&fw_device_xa, minor); error: - fw_device_put(device); /* fw_device_idr's reference */ + fw_device_put(device); // fw_device_xa's reference. put_device(&device->device); /* our reference */ } diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 14af84541e83..e141d24a7644 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -1359,7 +1358,7 @@ static void __exit fw_core_cleanup(void) unregister_chrdev(fw_cdev_major, "firewire"); bus_unregister(&fw_bus_type); destroy_workqueue(fw_workqueue); - idr_destroy(&fw_device_idr); + xa_destroy(&fw_device_xa); } module_init(fw_core_init); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 189e15e6ba82..8cace026090c 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -133,7 +134,7 @@ void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p); /* -device */ extern struct rw_semaphore fw_device_rwsem; -extern struct idr fw_device_idr; +extern struct xarray fw_device_xa; extern int fw_cdev_major; static inline struct fw_device *fw_device_get(struct fw_device *device) From 7a0a57cff296bbd90451fbf1f5614ffc48da73cd Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 12 Aug 2024 10:42:51 +0900 Subject: [PATCH 28/55] firewire: core: use lock in Xarray instead of local R/W semaphore The data of XArray structure includes spinlock and requires no external lock, while the data is still under the critical section by fw_device_rwsem. This commit deletes the critical section. Link: https://lore.kernel.org/r/20240812014251.165492-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-device.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 9f3276aa463a..32ac0f115793 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -806,7 +806,6 @@ static int shutdown_unit(struct device *device, void *data) /* * fw_device_rwsem acts as dual purpose mutex: - * - serializes accesses to fw_device_idr, * - serializes accesses to fw_device.config_rom/.config_rom_length and * fw_unit.directory, unless those accesses happen at safe occasions */ @@ -819,8 +818,6 @@ struct fw_device *fw_device_get_by_devt(dev_t devt) { struct fw_device *device; - guard(rwsem_read)(&fw_device_rwsem); - device = xa_load(&fw_device_xa, MINOR(devt)); if (device) fw_device_get(device); @@ -874,8 +871,7 @@ static void fw_device_shutdown(struct work_struct *work) device_for_each_child(&device->device, NULL, shutdown_unit); device_unregister(&device->device); - scoped_guard(rwsem_write, &fw_device_rwsem) - xa_erase(&fw_device_xa, MINOR(device->device.devt)); + xa_erase(&fw_device_xa, MINOR(device->device.devt)); fw_device_put(device); } @@ -1087,12 +1083,10 @@ static void fw_device_init(struct work_struct *work) fw_device_get(device); - scoped_guard(rwsem_write, &fw_device_rwsem) { - // The index of allocated entry is used for minor identifier of device node. - ret = xa_alloc(&fw_device_xa, &minor, device, XA_LIMIT(0, MINORMASK), GFP_KERNEL); - if (ret < 0) - goto error; - } + // The index of allocated entry is used for minor identifier of device node. + ret = xa_alloc(&fw_device_xa, &minor, device, XA_LIMIT(0, MINORMASK), GFP_KERNEL); + if (ret < 0) + goto error; device->device.bus = &fw_bus_type; device->device.type = &fw_device_type; @@ -1152,8 +1146,7 @@ static void fw_device_init(struct work_struct *work) return; error_with_cdev: - scoped_guard(rwsem_write, &fw_device_rwsem) - xa_erase(&fw_device_xa, minor); + xa_erase(&fw_device_xa, minor); error: fw_device_put(device); // fw_device_xa's reference. From 3b443fe087889cf6c9c133638f36da5617431703 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 13 Aug 2024 08:52:06 +0900 Subject: [PATCH 29/55] firewire: core: minor code refactoring to release client resource Current implementation checks and validates the result to find resource entry two times. It is redundant. This commit refactors the redundancy. Link: https://lore.kernel.org/r/20240812235210.28458-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index c211bb19c94e..81fdb2be9063 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -512,15 +512,14 @@ static int release_client_resource(struct client *client, u32 handle, scoped_guard(spinlock_irq, &client->lock) { if (client->in_shutdown) - resource = NULL; - else - resource = idr_find(&client->resource_idr, handle); - if (resource && resource->release == release) - idr_remove(&client->resource_idr, handle); - } + return -EINVAL; - if (!(resource && resource->release == release)) - return -EINVAL; + resource = idr_find(&client->resource_idr, handle); + if (!resource || resource->release != release) + return -EINVAL; + + idr_remove(&client->resource_idr, handle); + } if (return_resource) *return_resource = resource; From ced2da31b87fc5389d6babfb755d0fc3355aba56 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 13 Aug 2024 08:52:07 +0900 Subject: [PATCH 30/55] firewire: core: add helper functions to convert to parent resource structure All of local resource structure commonly have data of client_resource type in its first member. This design sometimes requires usage of container_of to retrieve parent structure by the first member. This commit adds some helper functions for this purpose. Link: https://lore.kernel.org/r/20240812235210.28458-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 38 ++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 81fdb2be9063..e72f91cc3711 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -139,6 +139,26 @@ struct iso_resource { struct iso_resource_event *e_alloc, *e_dealloc; }; +static struct address_handler_resource *to_address_handler_resource(struct client_resource *resource) +{ + return container_of(resource, struct address_handler_resource, resource); +} + +static struct inbound_transaction_resource *to_inbound_transaction_resource(struct client_resource *resource) +{ + return container_of(resource, struct inbound_transaction_resource, resource); +} + +static struct descriptor_resource *to_descriptor_resource(struct client_resource *resource) +{ + return container_of(resource, struct descriptor_resource, resource); +} + +static struct iso_resource *to_iso_resource(struct client_resource *resource) +{ + return container_of(resource, struct iso_resource, resource); +} + static void release_iso_resource(struct client *, struct client_resource *); static void schedule_iso_resource(struct iso_resource *r, unsigned long delay) @@ -151,8 +171,7 @@ static void schedule_iso_resource(struct iso_resource *r, unsigned long delay) static void schedule_if_iso_resource(struct client_resource *resource) { if (resource->release == release_iso_resource) - schedule_iso_resource(container_of(resource, - struct iso_resource, resource), 0); + schedule_iso_resource(to_iso_resource(resource), 0); } /* @@ -682,8 +701,7 @@ static int ioctl_send_request(struct client *client, union ioctl_arg *arg) static void release_request(struct client *client, struct client_resource *resource) { - struct inbound_transaction_resource *r = container_of(resource, - struct inbound_transaction_resource, resource); + struct inbound_transaction_resource *r = to_inbound_transaction_resource(resource); if (r->is_fcp) fw_request_put(r->request); @@ -793,8 +811,7 @@ static void handle_request(struct fw_card *card, struct fw_request *request, static void release_address_handler(struct client *client, struct client_resource *resource) { - struct address_handler_resource *r = - container_of(resource, struct address_handler_resource, resource); + struct address_handler_resource *r = to_address_handler_resource(resource); fw_core_remove_address_handler(&r->handler); kfree(r); @@ -858,8 +875,7 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg) release_request, &resource) < 0) return -EINVAL; - r = container_of(resource, struct inbound_transaction_resource, - resource); + r = to_inbound_transaction_resource(resource); if (r->is_fcp) { fw_request_put(r->request); goto out; @@ -893,8 +909,7 @@ static int ioctl_initiate_bus_reset(struct client *client, union ioctl_arg *arg) static void release_descriptor(struct client *client, struct client_resource *resource) { - struct descriptor_resource *r = - container_of(resource, struct descriptor_resource, resource); + struct descriptor_resource *r = to_descriptor_resource(resource); fw_core_remove_descriptor(&r->descriptor); kfree(r); @@ -1387,8 +1402,7 @@ static void iso_resource_work(struct work_struct *work) static void release_iso_resource(struct client *client, struct client_resource *resource) { - struct iso_resource *r = - container_of(resource, struct iso_resource, resource); + struct iso_resource *r = to_iso_resource(resource); guard(spinlock_irq)(&client->lock); From 58ee62c2907f4a582edc98c811f23011c5607686 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 13 Aug 2024 08:52:08 +0900 Subject: [PATCH 31/55] firewire: core: add helper function to detect data of iso resource structure It depends on the function assigned to release member to identify resource structure. This commit adds a helper function to identify iso_resource structure. Link: https://lore.kernel.org/r/20240812235210.28458-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index e72f91cc3711..6fe2a2ea9869 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -161,6 +161,11 @@ static struct iso_resource *to_iso_resource(struct client_resource *resource) static void release_iso_resource(struct client *, struct client_resource *); +static int is_iso_resource(const struct client_resource *resource) +{ + return resource->release == release_iso_resource; +} + static void schedule_iso_resource(struct iso_resource *r, unsigned long delay) { client_get(r->client); @@ -170,7 +175,7 @@ static void schedule_iso_resource(struct iso_resource *r, unsigned long delay) static void schedule_if_iso_resource(struct client_resource *resource) { - if (resource->release == release_iso_resource) + if (is_iso_resource(resource)) schedule_iso_resource(to_iso_resource(resource), 0); } From 6ec9e9260fe405a667e0dfbdf706483d648a8779 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 13 Aug 2024 08:52:09 +0900 Subject: [PATCH 32/55] firewire: core: code refactoring to use idr_for_each_entry() macro instead of idr_for_each() function This commit is a preparation to use xa_for_each() macro. Current implementation uses idr_for_each() function and has a disadvantage to replace with the macro. The IDR framework has idr_for_each_entry() macro for the similar purpose. This commit replace the function with the macro with minor code refactoring. Link: https://lore.kernel.org/r/20240812235210.28458-5-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 64 +++++++++++++++++------------------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 6fe2a2ea9869..83d25327c1d3 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -166,6 +166,14 @@ static int is_iso_resource(const struct client_resource *resource) return resource->release == release_iso_resource; } +static void release_transaction(struct client *client, + struct client_resource *resource); + +static int is_outbound_transaction_resource(const struct client_resource *resource) +{ + return resource->release == release_transaction; +} + static void schedule_iso_resource(struct iso_resource *r, unsigned long delay) { client_get(r->client); @@ -173,12 +181,6 @@ static void schedule_iso_resource(struct iso_resource *r, unsigned long delay) client_put(r->client); } -static void schedule_if_iso_resource(struct client_resource *resource) -{ - if (is_iso_resource(resource)) - schedule_iso_resource(to_iso_resource(resource), 0); -} - /* * dequeue_event() just kfree()'s the event, so the event has to be * the first field in a struct XYZ_event. @@ -401,16 +403,11 @@ static void for_each_client(struct fw_device *device, callback(c); } -static int schedule_reallocations(int id, void *p, void *data) -{ - schedule_if_iso_resource(p); - - return 0; -} - static void queue_bus_reset_event(struct client *client) { struct bus_reset_event *e; + struct client_resource *resource; + int id; e = kzalloc(sizeof(*e), GFP_KERNEL); if (e == NULL) @@ -423,7 +420,10 @@ static void queue_bus_reset_event(struct client *client) guard(spinlock_irq)(&client->lock); - idr_for_each(&client->resource_idr, schedule_reallocations, client); + idr_for_each_entry(&client->resource_idr, resource, id) { + if (is_iso_resource(resource)) + schedule_iso_resource(to_iso_resource(resource), 0); + } } void fw_device_cdev_update(struct fw_device *device) @@ -518,7 +518,8 @@ static int add_client_resource(struct client *client, if (ret >= 0) { resource->handle = ret; client_get(client); - schedule_if_iso_resource(resource); + if (is_iso_resource(resource)) + schedule_iso_resource(to_iso_resource(resource), 0); } } @@ -1835,35 +1836,27 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) return ret; } -static int is_outbound_transaction_resource(int id, void *p, void *data) +static bool has_outbound_transactions(struct client *client) { - struct client_resource *resource = p; + struct client_resource *resource; + int id; - return resource->release == release_transaction; -} - -static int has_outbound_transactions(struct client *client) -{ guard(spinlock_irq)(&client->lock); - return idr_for_each(&client->resource_idr, is_outbound_transaction_resource, NULL); -} + idr_for_each_entry(&client->resource_idr, resource, id) { + if (is_outbound_transaction_resource(resource)) + return true; + } -static int shutdown_resource(int id, void *p, void *data) -{ - struct client_resource *resource = p; - struct client *client = data; - - resource->release(client, resource); - client_put(client); - - return 0; + return false; } static int fw_device_op_release(struct inode *inode, struct file *file) { struct client *client = file->private_data; struct event *event, *next_event; + struct client_resource *resource; + int id; scoped_guard(spinlock_irq, &client->device->card->lock) list_del(&client->phy_receiver_link); @@ -1883,7 +1876,10 @@ static int fw_device_op_release(struct inode *inode, struct file *file) wait_event(client->tx_flush_wait, !has_outbound_transactions(client)); - idr_for_each(&client->resource_idr, shutdown_resource, client); + idr_for_each_entry(&client->resource_idr, resource, id) { + resource->release(client, resource); + client_put(client); + } idr_destroy(&client->resource_idr); list_for_each_entry_safe(event, next_event, &client->event_list, link) From d9f6c64e03c2fd5bfe8e03f806b8e56d9cf112e4 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 13 Aug 2024 08:52:10 +0900 Subject: [PATCH 33/55] firewire: core: use xarray instead of idr to maintain client resource In core function, the instances of some client resource structures are maintained by IDR. As of kernel v6.0, IDR has been superseded by XArray and deprecated. This commit replaces the usage of IDR with XArray to maintain the resource instances. The instance of XArray is allocated per client with XA_FLAGS_ALLOC1 so that the index of allocated entry is greater than zero and returns to user space client as handle of the resource. Link: https://lore.kernel.org/r/20240812235210.28458-6-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 66 +++++++++++++++++++----------------- drivers/firewire/core.h | 1 - 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 83d25327c1d3..3ea220d96c31 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -54,7 +53,7 @@ struct client { spinlock_t lock; bool in_shutdown; - struct idr resource_idr; + struct xarray resource_xa; struct list_head event_list; wait_queue_head_t wait; wait_queue_head_t tx_flush_wait; @@ -297,7 +296,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file) client->device = device; spin_lock_init(&client->lock); - idr_init(&client->resource_idr); + xa_init_flags(&client->resource_xa, XA_FLAGS_ALLOC1 | XA_FLAGS_LOCK_BH); INIT_LIST_HEAD(&client->event_list); init_waitqueue_head(&client->wait); init_waitqueue_head(&client->tx_flush_wait); @@ -407,7 +406,7 @@ static void queue_bus_reset_event(struct client *client) { struct bus_reset_event *e; struct client_resource *resource; - int id; + unsigned long index; e = kzalloc(sizeof(*e), GFP_KERNEL); if (e == NULL) @@ -420,7 +419,7 @@ static void queue_bus_reset_event(struct client *client) guard(spinlock_irq)(&client->lock); - idr_for_each_entry(&client->resource_idr, resource, id) { + xa_for_each(&client->resource_xa, index, resource) { if (is_iso_resource(resource)) schedule_iso_resource(to_iso_resource(resource), 0); } @@ -501,31 +500,33 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg) return ret ? -EFAULT : 0; } -static int add_client_resource(struct client *client, - struct client_resource *resource, gfp_t gfp_mask) +static int add_client_resource(struct client *client, struct client_resource *resource, + gfp_t gfp_mask) { - bool preload = gfpflags_allow_blocking(gfp_mask); int ret; - if (preload) - idr_preload(gfp_mask); - scoped_guard(spinlock_irqsave, &client->lock) { - if (client->in_shutdown) + u32 index; + + if (client->in_shutdown) { ret = -ECANCELED; - else - ret = idr_alloc(&client->resource_idr, resource, 0, 0, GFP_NOWAIT); + } else { + if (gfpflags_allow_blocking(gfp_mask)) { + ret = xa_alloc(&client->resource_xa, &index, resource, xa_limit_32b, + GFP_NOWAIT); + } else { + ret = xa_alloc_bh(&client->resource_xa, &index, resource, + xa_limit_32b, GFP_NOWAIT); + } + } if (ret >= 0) { - resource->handle = ret; + resource->handle = index; client_get(client); if (is_iso_resource(resource)) schedule_iso_resource(to_iso_resource(resource), 0); } } - if (preload) - idr_preload_end(); - return ret < 0 ? ret : 0; } @@ -533,17 +534,18 @@ static int release_client_resource(struct client *client, u32 handle, client_resource_release_fn_t release, struct client_resource **return_resource) { + unsigned long index = handle; struct client_resource *resource; scoped_guard(spinlock_irq, &client->lock) { if (client->in_shutdown) return -EINVAL; - resource = idr_find(&client->resource_idr, handle); + resource = xa_load(&client->resource_xa, index); if (!resource || resource->release != release) return -EINVAL; - idr_remove(&client->resource_idr, handle); + xa_erase(&client->resource_xa, handle); } if (return_resource) @@ -566,9 +568,10 @@ static void complete_transaction(struct fw_card *card, int rcode, u32 request_ts { struct outbound_transaction_event *e = data; struct client *client = e->client; + unsigned long index = e->r.resource.handle; scoped_guard(spinlock_irqsave, &client->lock) { - idr_remove(&client->resource_idr, e->r.resource.handle); + xa_erase(&client->resource_xa, index); if (client->in_shutdown) wake_up(&client->tx_flush_wait); } @@ -619,7 +622,7 @@ static void complete_transaction(struct fw_card *card, int rcode, u32 request_ts break; } - /* Drop the idr's reference */ + // Drop the xarray's reference. client_put(client); } @@ -1317,6 +1320,7 @@ static void iso_resource_work(struct work_struct *work) struct iso_resource *r = container_of(work, struct iso_resource, work.work); struct client *client = r->client; + unsigned long index = r->resource.handle; int generation, channel, bandwidth, todo; bool skip, free, success; @@ -1351,7 +1355,7 @@ static void iso_resource_work(struct work_struct *work) todo == ISO_RES_ALLOC_ONCE); /* * Is this generation outdated already? As long as this resource sticks - * in the idr, it will be scheduled again for a newer generation or at + * in the xarray, it will be scheduled again for a newer generation or at * shutdown. */ if (channel == -EAGAIN && @@ -1366,10 +1370,10 @@ static void iso_resource_work(struct work_struct *work) if (r->todo == ISO_RES_ALLOC) r->todo = ISO_RES_REALLOC; // Allocation or reallocation failure? Pull this resource out of the - // idr and prepare for deletion, unless the client is shutting down. + // xarray and prepare for deletion, unless the client is shutting down. if (r->todo == ISO_RES_REALLOC && !success && !client->in_shutdown && - idr_remove(&client->resource_idr, r->resource.handle)) { + xa_erase(&client->resource_xa, index)) { client_put(client); free = true; } @@ -1839,11 +1843,11 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) static bool has_outbound_transactions(struct client *client) { struct client_resource *resource; - int id; + unsigned long index; guard(spinlock_irq)(&client->lock); - idr_for_each_entry(&client->resource_idr, resource, id) { + xa_for_each(&client->resource_xa, index, resource) { if (is_outbound_transaction_resource(resource)) return true; } @@ -1856,7 +1860,7 @@ static int fw_device_op_release(struct inode *inode, struct file *file) struct client *client = file->private_data; struct event *event, *next_event; struct client_resource *resource; - int id; + unsigned long index; scoped_guard(spinlock_irq, &client->device->card->lock) list_del(&client->phy_receiver_link); @@ -1870,17 +1874,17 @@ static int fw_device_op_release(struct inode *inode, struct file *file) if (client->buffer.pages) fw_iso_buffer_destroy(&client->buffer, client->device->card); - /* Freeze client->resource_idr and client->event_list */ + // Freeze client->resource_xa and client->event_list. scoped_guard(spinlock_irq, &client->lock) client->in_shutdown = true; wait_event(client->tx_flush_wait, !has_outbound_transactions(client)); - idr_for_each_entry(&client->resource_idr, resource, id) { + xa_for_each(&client->resource_xa, index, resource) { resource->release(client, resource); client_put(client); } - idr_destroy(&client->resource_idr); + xa_destroy(&client->resource_xa); list_for_each_entry_safe(event, next_event, &client->event_list, link) kfree(event); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 8cace026090c..57d101c01e36 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include From 56a4832c9f2e46e380ee1979a02f44b115598bf3 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 14 Aug 2024 22:12:20 +0900 Subject: [PATCH 34/55] firewire: ohci: use helper macro for compiler aligned attribute The __aligned() macro has been available since v4.19 kernel by a commit 815f0ddb346c ("include/linux/compiler*.h: make compiler-*.h mutually exclusive"). This commit replaces with the macro. Link: https://lore.kernel.org/r/20240814131222.69949-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index e1d24e0ec991..198c96d75155 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -77,7 +77,7 @@ struct descriptor { __le32 branch_address; __le16 res_count; __le16 transfer_status; -} __attribute__((aligned(16))); +} __aligned(16); #define CONTROL_SET(regs) (regs) #define CONTROL_CLEAR(regs) ((regs) + 4) From d4dcb339739e84bc1de335b567ee24f4f89fc19a Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 14 Aug 2024 22:12:21 +0900 Subject: [PATCH 35/55] firewire: ohci: remove unused wrapper macro for dev_info() The ohci_info() macro is a thin wrapper of dev_info(), while it is never used. Link: https://lore.kernel.org/r/20240814131222.69949-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 198c96d75155..979f1e1f2d16 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -50,7 +50,6 @@ static u32 cond_le32_to_cpu(__le32 value, bool has_be_header_quirk); #define CREATE_TRACE_POINTS #include -#define ohci_info(ohci, f, args...) dev_info(ohci->card.device, f, ##args) #define ohci_notice(ohci, f, args...) dev_notice(ohci->card.device, f, ##args) #define ohci_err(ohci, f, args...) dev_err(ohci->card.device, f, ##args) From e8b89bc158199b60f3ae6c655ced4461c42d650b Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 14 Aug 2024 22:12:22 +0900 Subject: [PATCH 36/55] firewire: core/ohci: minor refactoring for computation of configuration ROM size The size of space for configuration ROM is defined by IEEE 1212. The start and end offsets are available as some macros in UAPI header. This commit uses these macros to compute the size. Link: https://lore.kernel.org/r/20240814131222.69949-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-device.c | 3 ++- drivers/firewire/ohci.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 32ac0f115793..f71e118ef60a 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -564,7 +564,8 @@ static int read_rom(struct fw_device *device, return rcode; } -#define MAX_CONFIG_ROM_SIZE 256 +// By quadlet unit. +#define MAX_CONFIG_ROM_SIZE ((CSR_CONFIG_ROM_END - CSR_CONFIG_ROM) / sizeof(u32)) /* * Read the bus info block, perform a speed probe, and read all of the rest of diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 979f1e1f2d16..53132eae37fe 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -174,7 +174,7 @@ struct iso_context { u8 tags; }; -#define CONFIG_ROM_SIZE 1024 +#define CONFIG_ROM_SIZE (CSR_CONFIG_ROM_END - CSR_CONFIG_ROM) struct fw_ohci { struct fw_card card; From 52f9fcbc7b11b305306c879fcc545d075066bee0 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 17 Aug 2024 18:11:28 +0900 Subject: [PATCH 37/55] firewire: ohci: fix error path to detect initiated reset in TI TSB41BA3D phy A commit 404957c1e207 ("firewire: ohci: use guard macro to serialize accesses to phy registers") refactored initiated_reset() helper function, while the error path was changed wrongly. This commit fixes the bug. Reported-by: Dan Carpenter Fixes: 80f3401dfeb2 ("firewire: ohci: use guard macro to serialize accesses to phy registers") Link: https://lore.kernel.org/r/20240817091128.180303-1-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 53132eae37fe..a3a37955b174 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1919,7 +1919,7 @@ static int get_self_id_pos(struct fw_ohci *ohci, u32 self_id, return i; } -static bool initiated_reset(struct fw_ohci *ohci) +static int detect_initiated_reset(struct fw_ohci *ohci, bool *is_initiated_reset) { int reg; @@ -1946,7 +1946,9 @@ static bool initiated_reset(struct fw_ohci *ohci) return reg; // bit 3 indicates "initiated reset" - return !!((reg & 0x08) == 0x08); + *is_initiated_reset = !!((reg & 0x08) == 0x08); + + return 0; } /* @@ -1956,7 +1958,8 @@ static bool initiated_reset(struct fw_ohci *ohci) */ static int find_and_insert_self_id(struct fw_ohci *ohci, int self_id_count) { - int reg, i, pos; + int reg, i, pos, err; + bool is_initiated_reset; u32 self_id = 0; // link active 1, speed 3, bridge 0, contender 1, more packets 0. @@ -1985,7 +1988,6 @@ static int find_and_insert_self_id(struct fw_ohci *ohci, int self_id_count) for (i = 0; i < 3; i++) { enum phy_packet_self_id_port_status status; - int err; err = get_status_for_port(ohci, i, &status); if (err < 0) @@ -1994,7 +1996,10 @@ static int find_and_insert_self_id(struct fw_ohci *ohci, int self_id_count) self_id_sequence_set_port_status(&self_id, 1, i, status); } - phy_packet_self_id_zero_set_initiated_reset(&self_id, initiated_reset(ohci)); + err = detect_initiated_reset(ohci, &is_initiated_reset); + if (err < 0) + return err; + phy_packet_self_id_zero_set_initiated_reset(&self_id, is_initiated_reset); pos = get_self_id_pos(ohci, self_id, self_id_count); if (pos >= 0) { From e2c87f484190b12fedcea9c428906aafcb8783cf Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 20 Aug 2024 22:21:32 +0900 Subject: [PATCH 38/55] firewire: core: update fw_device outside of device_find_child() When detecting updates of bus topology, the data of fw_device is newly allocated and caches the content of configuration ROM from the corresponding node. Then, the tree of device is sought to find the previous data of fw_device corresponding to the node. If found, the previous data is updated and reused and the data of fw_device newly allocated is going to be released. The above procedure is done in the call of device_find_child(), however it is a bit abusing against the intention of the helper function, since it is preferable to find only without updating. This commit splits the update outside of the call. Cc: Zijun Hu Link: https://lore.kernel.org/r/20240820132132.28839-1-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-device.c | 116 +++++++++++++++++---------------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index f71e118ef60a..a99fe35f1f0d 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -928,56 +928,6 @@ static void fw_device_update(struct work_struct *work) device_for_each_child(&device->device, NULL, update_unit); } -/* - * If a device was pending for deletion because its node went away but its - * bus info block and root directory header matches that of a newly discovered - * device, revive the existing fw_device. - * The newly allocated fw_device becomes obsolete instead. - */ -static int lookup_existing_device(struct device *dev, void *data) -{ - struct fw_device *old = fw_device(dev); - struct fw_device *new = data; - struct fw_card *card = new->card; - int match = 0; - - if (!is_fw_device(dev)) - return 0; - - guard(rwsem_read)(&fw_device_rwsem); // serialize config_rom access - guard(spinlock_irq)(&card->lock); // serialize node access - - if (memcmp(old->config_rom, new->config_rom, 6 * 4) == 0 && - atomic_cmpxchg(&old->state, - FW_DEVICE_GONE, - FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { - struct fw_node *current_node = new->node; - struct fw_node *obsolete_node = old->node; - - new->node = obsolete_node; - new->node->data = new; - old->node = current_node; - old->node->data = old; - - old->max_speed = new->max_speed; - old->node_id = current_node->node_id; - smp_wmb(); /* update node_id before generation */ - old->generation = card->generation; - old->config_rom_retries = 0; - fw_notice(card, "rediscovered device %s\n", dev_name(dev)); - - old->workfn = fw_device_update; - fw_schedule_device_work(old, 0); - - if (current_node == card->root_node) - fw_schedule_bm_work(card, 0); - - match = 1; - } - - return match; -} - enum { BC_UNKNOWN = 0, BC_UNIMPLEMENTED, BC_IMPLEMENTED, }; static void set_broadcast_channel(struct fw_device *device, int generation) @@ -1038,12 +988,24 @@ int fw_device_set_broadcast_channel(struct device *dev, void *gen) return 0; } +static int compare_configuration_rom(struct device *dev, void *data) +{ + const struct fw_device *old = fw_device(dev); + const u32 *config_rom = data; + + if (!is_fw_device(dev)) + return 0; + + // Compare the bus information block and root_length/root_crc. + return !memcmp(old->config_rom, config_rom, 6 * 4); +} + static void fw_device_init(struct work_struct *work) { struct fw_device *device = container_of(work, struct fw_device, work.work); struct fw_card *card = device->card; - struct device *revived_dev; + struct device *found; u32 minor; int ret; @@ -1071,13 +1033,53 @@ static void fw_device_init(struct work_struct *work) return; } - revived_dev = device_find_child(card->device, - device, lookup_existing_device); - if (revived_dev) { - put_device(revived_dev); - fw_device_release(&device->device); + // If a device was pending for deletion because its node went away but its bus info block + // and root directory header matches that of a newly discovered device, revive the + // existing fw_device. The newly allocated fw_device becomes obsolete instead. + // + // serialize config_rom access. + scoped_guard(rwsem_read, &fw_device_rwsem) { + found = device_find_child(card->device, (void *)device->config_rom, + compare_configuration_rom); + } + if (found) { + struct fw_device *reused = fw_device(found); - return; + if (atomic_cmpxchg(&reused->state, + FW_DEVICE_GONE, + FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { + // serialize node access + scoped_guard(spinlock_irq, &card->lock) { + struct fw_node *current_node = device->node; + struct fw_node *obsolete_node = reused->node; + + device->node = obsolete_node; + device->node->data = device; + reused->node = current_node; + reused->node->data = reused; + + reused->max_speed = device->max_speed; + reused->node_id = current_node->node_id; + smp_wmb(); /* update node_id before generation */ + reused->generation = card->generation; + reused->config_rom_retries = 0; + fw_notice(card, "rediscovered device %s\n", + dev_name(found)); + + reused->workfn = fw_device_update; + fw_schedule_device_work(reused, 0); + + if (current_node == card->root_node) + fw_schedule_bm_work(card, 0); + } + + put_device(found); + fw_device_release(&device->device); + + return; + } + + put_device(found); } device_initialize(&device->device); From cd7023729877e41f958178e9aa9cc86e71246e34 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 3 Sep 2024 19:14:55 +0900 Subject: [PATCH 39/55] firewire: ohci: deprecate debug parameter Many tracepoints events have been added to 6.10 and 6.11 kernels. They are available as an alternative of debug parameter in firewire-ohci module. The logging messages enabled by the parameter require some cumbersomes in a point of maintenance; e.g. the code to decode transaction frame. This commit adds deprecation text to conduct users to them.. Link: https://lore.kernel.org/r/20240903101455.317067-1-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index a3a37955b174..e662dc30c21f 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -396,7 +396,7 @@ MODULE_PARM_DESC(quirks, "Chip quirks (default = 0" static int param_debug; module_param_named(debug, param_debug, int, 0644); -MODULE_PARM_DESC(debug, "Verbose logging (default = 0" +MODULE_PARM_DESC(debug, "Verbose logging, deprecated in v6.11 kernel or later. (default = 0" ", AT/AR events = " __stringify(OHCI_PARAM_DEBUG_AT_AR) ", self-IDs = " __stringify(OHCI_PARAM_DEBUG_SELFIDS) ", IRQs = " __stringify(OHCI_PARAM_DEBUG_IRQS) @@ -2197,6 +2197,11 @@ static irqreturn_t irq_handler(int irq, void *data) if (!event || !~event) return IRQ_NONE; + if (unlikely(param_debug > 0)) { + dev_notice_ratelimited(ohci->card.device, + "The debug parameter is superceded by tracepoints events, and deprecated."); + } + /* * busReset and postedWriteErr events must not be cleared yet * (OHCI 1.1 clauses 7.2.3.2 and 13.2.8.1) From 7d35a0060392af796cc4e08780d7b71d1fc71e5e Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 3 Sep 2024 19:15:23 +0900 Subject: [PATCH 40/55] firewire: ohci: obsolete direct usage of printk_ratelimit() A commit 77006a0a8282 ("ratelimit: add comment warning people off printk_ratelimit()") has already deprecated printk_ratelimit(). This commit uses alternative functions to obsolete its usage. Link: https://lore.kernel.org/r/20240903101523.317110-1-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index e662dc30c21f..3930fdd56155 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -2268,13 +2268,11 @@ static irqreturn_t irq_handler(int irq, void *data) reg_read(ohci, OHCI1394_PostedWriteAddressLo); reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_postedWriteErr); - if (printk_ratelimit()) - ohci_err(ohci, "PCI posted write error\n"); + dev_err_ratelimited(ohci->card.device, "PCI posted write error\n"); } if (unlikely(event & OHCI1394_cycleTooLong)) { - if (printk_ratelimit()) - ohci_notice(ohci, "isochronous cycle too long\n"); + dev_notice_ratelimited(ohci->card.device, "isochronous cycle too long\n"); reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_cycleMaster); } @@ -2286,8 +2284,7 @@ static irqreturn_t irq_handler(int irq, void *data) * stop active cycleMatch iso contexts now and restart * them at least two cycles later. (FIXME?) */ - if (printk_ratelimit()) - ohci_notice(ohci, "isochronous cycle inconsistent\n"); + dev_notice_ratelimited(ohci->card.device, "isochronous cycle inconsistent\n"); } if (unlikely(event & OHCI1394_unrecoverableError)) From c6fb88a5270f4ba24859f5ecb61cfc731ef16300 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 4 Sep 2024 21:51:50 +0900 Subject: [PATCH 41/55] firewire: core: allocate workqueue to handle isochronous contexts in card This commit adds a workqueue dedicated for isochronous context processing. The workqueue is allocated per instance of fw_card structure to satisfy the following characteristics descending from 1394 OHCI specification: In 1394 OHCI specification, memory pages are reserved to each isochronous context dedicated to DMA transmission. It allows to operate these per-context pages concurrently. Software can schedule hardware interrupt for several isochronous context to the same cycle, thus WQ_UNBOUND is specified. Additionally, it is sleepable to operate the content of pages, thus WQ_BH is not used. The isochronous context delivers the packets with time stamp, thus WQ_HIGHPRI is specified for semi real-time data such as IEC 61883-1/6 protocol implemented by ALSA firewire stack. The isochronous context is not used by the implementation of SCSI over IEEE1394 protocol (sbp2), thus WQ_MEM_RECLAIM is not specified. It is useful for users to adjust cpu affinity of the workqueue depending on their work loads, thus WQ_SYS is specified to expose the attributes to user space. Tested-by: Edmund Raile Link: https://lore.kernel.org/r/20240904125155.461886-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-card.c | 33 ++++++++++++++++++++++++++++++--- drivers/firewire/core.h | 4 ++-- drivers/firewire/ohci.c | 2 +- include/linux/firewire.h | 2 ++ 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index e80b762888fa..01354b9de8b2 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -571,11 +571,30 @@ void fw_card_initialize(struct fw_card *card, } EXPORT_SYMBOL(fw_card_initialize); -int fw_card_add(struct fw_card *card, - u32 max_receive, u32 link_speed, u64 guid) +int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid, + unsigned int supported_isoc_contexts) { + struct workqueue_struct *isoc_wq; int ret; + // This workqueue should be: + // * != WQ_BH Sleepable. + // * == WQ_UNBOUND Any core can process data for isoc context. The + // implementation of unit protocol could consumes the core + // longer somehow. + // * != WQ_MEM_RECLAIM Not used for any backend of block device. + // * == WQ_FREEZABLE Isochronous communication is at regular interval in real + // time, thus should be drained if possible at freeze phase. + // * == WQ_HIGHPRI High priority to process semi-realtime timestamped data. + // * == WQ_SYSFS Parameters are available via sysfs. + // * max_active == n_it + n_ir A hardIRQ could notify events for multiple isochronous + // contexts if they are scheduled to the same cycle. + isoc_wq = alloc_workqueue("firewire-isoc-card%u", + WQ_UNBOUND | WQ_FREEZABLE | WQ_HIGHPRI | WQ_SYSFS, + supported_isoc_contexts, card->index); + if (!isoc_wq) + return -ENOMEM; + card->max_receive = max_receive; card->link_speed = link_speed; card->guid = guid; @@ -584,9 +603,12 @@ int fw_card_add(struct fw_card *card, generate_config_rom(card, tmp_config_rom); ret = card->driver->enable(card, tmp_config_rom, config_rom_length); - if (ret < 0) + if (ret < 0) { + destroy_workqueue(isoc_wq); return ret; + } + card->isoc_wq = isoc_wq; list_add_tail(&card->link, &card_list); return 0; @@ -708,6 +730,8 @@ void fw_core_remove_card(struct fw_card *card) { struct fw_card_driver dummy_driver = dummy_driver_template; + might_sleep(); + card->driver->update_phy_reg(card, 4, PHY_LINK_ACTIVE | PHY_CONTENDER, 0); fw_schedule_bus_reset(card, false, true); @@ -719,6 +743,7 @@ void fw_core_remove_card(struct fw_card *card) dummy_driver.free_iso_context = card->driver->free_iso_context; dummy_driver.stop_iso = card->driver->stop_iso; card->driver = &dummy_driver; + drain_workqueue(card->isoc_wq); scoped_guard(spinlock_irqsave, &card->lock) fw_destroy_nodes(card); @@ -727,6 +752,8 @@ void fw_core_remove_card(struct fw_card *card) fw_card_put(card); wait_for_completion(&card->done); + destroy_workqueue(card->isoc_wq); + WARN_ON(!list_empty(&card->transaction_list)); } EXPORT_SYMBOL(fw_core_remove_card); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 57d101c01e36..96ae366889e0 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -115,8 +115,8 @@ struct fw_card_driver { void fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver, struct device *device); -int fw_card_add(struct fw_card *card, - u32 max_receive, u32 link_speed, u64 guid); +int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid, + unsigned int supported_isoc_contexts); void fw_core_remove_card(struct fw_card *card); int fw_compute_block_crc(__be32 *block); void fw_schedule_bm_work(struct fw_card *card, unsigned long delay); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 3930fdd56155..50627b8fc38f 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -3827,7 +3827,7 @@ static int pci_probe(struct pci_dev *dev, goto fail_msi; } - err = fw_card_add(&ohci->card, max_receive, link_speed, guid); + err = fw_card_add(&ohci->card, max_receive, link_speed, guid, ohci->n_it + ohci->n_ir); if (err) goto fail_irq; diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 1cca14cf5652..10e135d60824 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -134,6 +134,8 @@ struct fw_card { __be32 topology_map[(CSR_TOPOLOGY_MAP_END - CSR_TOPOLOGY_MAP) / 4]; __be32 maint_utility_register; + + struct workqueue_struct *isoc_wq; }; static inline struct fw_card *fw_card_get(struct fw_card *card) From 4f55ad754d6b7b6fa4ebcfd8c50f7e53e661a78e Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 4 Sep 2024 21:51:51 +0900 Subject: [PATCH 42/55] firewire: core: add local API to queue work item to workqueue specific to isochronous contexts In the previous commit, the workqueue is added per the instance of fw_card structure for isochronous contexts. The workqueue is designed to be used by the implementation of fw_card_driver structure underlying the fw_card. This commit adds some local APIs to be used by the implementation. Tested-by: Edmund Raile Link: https://lore.kernel.org/r/20240904125155.461886-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-iso.c | 30 ++++++++++++++++++++++++++++-- drivers/firewire/core.h | 10 ++++++++++ include/linux/firewire.h | 1 + 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 101433b8bb51..af76fa1823f1 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -211,21 +211,47 @@ EXPORT_SYMBOL(fw_iso_context_queue_flush); int fw_iso_context_flush_completions(struct fw_iso_context *ctx) { + int err; + trace_isoc_outbound_flush_completions(ctx); trace_isoc_inbound_single_flush_completions(ctx); trace_isoc_inbound_multiple_flush_completions(ctx); - return ctx->card->driver->flush_iso_completions(ctx); + might_sleep(); + + // Avoid dead lock due to programming mistake. + if (WARN_ON(current_work() == &ctx->work)) + return 0; + + disable_work_sync(&ctx->work); + + err = ctx->card->driver->flush_iso_completions(ctx); + + enable_work(&ctx->work); + + return err; } EXPORT_SYMBOL(fw_iso_context_flush_completions); int fw_iso_context_stop(struct fw_iso_context *ctx) { + int err; + trace_isoc_outbound_stop(ctx); trace_isoc_inbound_single_stop(ctx); trace_isoc_inbound_multiple_stop(ctx); - return ctx->card->driver->stop_iso(ctx); + might_sleep(); + + // Avoid dead lock due to programming mistake. + if (WARN_ON(current_work() == &ctx->work)) + return 0; + + err = ctx->card->driver->stop_iso(ctx); + + cancel_work_sync(&ctx->work); + + return err; } EXPORT_SYMBOL(fw_iso_context_stop); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 96ae366889e0..2874f316156a 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -159,6 +159,16 @@ int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count); int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card, enum dma_data_direction direction); +static inline void fw_iso_context_init_work(struct fw_iso_context *ctx, work_func_t func) +{ + INIT_WORK(&ctx->work, func); +} + +static inline void fw_iso_context_queue_work(struct fw_iso_context *ctx) +{ + queue_work(ctx->card->isoc_wq, &ctx->work); +} + /* -topology */ diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 10e135d60824..72f497b61739 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -511,6 +511,7 @@ union fw_iso_callback { struct fw_iso_context { struct fw_card *card; + struct work_struct work; int type; int channel; int speed; From 5390813c34d799bde1ddf7b726f9b8f78bad67f0 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 4 Sep 2024 21:51:52 +0900 Subject: [PATCH 43/55] firewire: ohci: operate IT/IR events in sleepable work process instead of tasklet softIRQ This commit queues work item for IT/IR events at hardIRQ handler to operate the corresponding isochronous context. The work item is queued to any of worker-pools. The callback for either the implementation of unit protocol and user space clients is executed in sleepable work process context. The change could results in any errors of concurrent processing as well as sleep at atomic context. These errors are fixed by the following commits. Tested-by: Edmund Raile Link: https://lore.kernel.org/r/20240904125155.461886-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 55 +++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 50627b8fc38f..d0b1fccc450f 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1182,6 +1182,47 @@ static void context_tasklet(unsigned long data) } } +static void ohci_isoc_context_work(struct work_struct *work) +{ + struct fw_iso_context *base = container_of(work, struct fw_iso_context, work); + struct iso_context *isoc_ctx = container_of(base, struct iso_context, base); + struct context *ctx = &isoc_ctx->context; + struct descriptor *d, *last; + u32 address; + int z; + struct descriptor_buffer *desc; + + desc = list_entry(ctx->buffer_list.next, struct descriptor_buffer, list); + last = ctx->last; + while (last->branch_address != 0) { + struct descriptor_buffer *old_desc = desc; + + address = le32_to_cpu(last->branch_address); + z = address & 0xf; + address &= ~0xf; + ctx->current_bus = address; + + // If the branch address points to a buffer outside of the current buffer, advance + // to the next buffer. + if (address < desc->buffer_bus || address >= desc->buffer_bus + desc->used) + desc = list_entry(desc->list.next, struct descriptor_buffer, list); + d = desc->buffer + (address - desc->buffer_bus) / sizeof(*d); + last = find_branch_descriptor(d, z); + + if (!ctx->callback(ctx, d, last)) + break; + + if (old_desc != desc) { + // If we've advanced to the next buffer, move the previous buffer to the + // free list. + old_desc->used = 0; + guard(spinlock_irqsave)(&ctx->ohci->lock); + list_move_tail(&old_desc->list, &ctx->buffer_list); + } + ctx->last = last; + } +} + /* * Allocate a new buffer and add it to the list of free buffers for this * context. Must be called with ohci->lock held. @@ -2242,8 +2283,7 @@ static irqreturn_t irq_handler(int irq, void *data) while (iso_event) { i = ffs(iso_event) - 1; - tasklet_schedule( - &ohci->ir_context_list[i].context.tasklet); + fw_iso_context_queue_work(&ohci->ir_context_list[i].base); iso_event &= ~(1 << i); } } @@ -2254,8 +2294,7 @@ static irqreturn_t irq_handler(int irq, void *data) while (iso_event) { i = ffs(iso_event) - 1; - tasklet_schedule( - &ohci->it_context_list[i].context.tasklet); + fw_iso_context_queue_work(&ohci->it_context_list[i].base); iso_event &= ~(1 << i); } } @@ -3130,6 +3169,7 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, ret = context_init(&ctx->context, ohci, regs, callback); if (ret < 0) goto out_with_header; + fw_iso_context_init_work(&ctx->base, ohci_isoc_context_work); if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) { set_multichannel_mask(ohci, 0); @@ -3227,7 +3267,6 @@ static int ohci_stop_iso(struct fw_iso_context *base) } flush_writes(ohci); context_stop(&ctx->context); - tasklet_kill(&ctx->context.tasklet); return 0; } @@ -3584,10 +3623,8 @@ static int ohci_flush_iso_completions(struct fw_iso_context *base) struct iso_context *ctx = container_of(base, struct iso_context, base); int ret = 0; - tasklet_disable_in_atomic(&ctx->context.tasklet); - if (!test_and_set_bit_lock(0, &ctx->flushing_completions)) { - context_tasklet((unsigned long)&ctx->context); + ohci_isoc_context_work(&base->work); switch (base->type) { case FW_ISO_CONTEXT_TRANSMIT: @@ -3607,8 +3644,6 @@ static int ohci_flush_iso_completions(struct fw_iso_context *base) smp_mb__after_atomic(); } - tasklet_enable(&ctx->context.tasklet); - return ret; } From f62ec13e8b696c58f360f457f1541056872aee99 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 4 Sep 2024 21:51:53 +0900 Subject: [PATCH 44/55] firewire: core: non-atomic memory allocation for isochronous event to user client In the former commits, the callback of isochronous context runs on work process, thus no need to use atomic memory allocation. This commit replaces GFP_ATOMIC with GCP_KERNEL in the callback for user client. Tested-by: Edmund Raile Link: https://lore.kernel.org/r/20240904125155.461886-5-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 3ea220d96c31..518eaa073b2b 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -982,7 +982,7 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle, struct client *client = data; struct iso_interrupt_event *e; - e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC); + e = kmalloc(sizeof(*e) + header_length, GFP_KERNEL); if (e == NULL) return; @@ -1001,7 +1001,7 @@ static void iso_mc_callback(struct fw_iso_context *context, struct client *client = data; struct iso_interrupt_mc_event *e; - e = kmalloc(sizeof(*e), GFP_ATOMIC); + e = kmalloc(sizeof(*e), GFP_KERNEL); if (e == NULL) return; From 5c49cc0ed405cadb60d8c4484f95ffdaf7c6ec5c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 4 Sep 2024 21:51:54 +0900 Subject: [PATCH 45/55] ALSA: firewire: use nonatomic PCM operation In the former commits, the callback of isochronous context runs on usual work process. In the case, ALSA PCM device has a flag, nonatomic, to acquire mutex lock instead of spin lock for PCM substream group. This commit uses the flag. It has an advantage in the case that ALSA PCM application uses the large size of intermediate buffer, since it takes too long time even in tasklet softIRQ to process many of isochronous packets, then result in the delay of system event due to disabled IRQ so long. It is avertible to switch to nonatomic operation. Reviewed-by: Takashi Iwai Tested-by: Edmund Raile Link: https://lore.kernel.org/r/20240904125155.461886-6-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- sound/firewire/amdtp-stream.c | 34 +++++++++++++++++++----- sound/firewire/bebob/bebob_pcm.c | 1 + sound/firewire/dice/dice-pcm.c | 1 + sound/firewire/digi00x/digi00x-pcm.c | 1 + sound/firewire/fireface/ff-pcm.c | 1 + sound/firewire/fireworks/fireworks_pcm.c | 1 + sound/firewire/isight.c | 1 + sound/firewire/motu/motu-pcm.c | 1 + sound/firewire/oxfw/oxfw-pcm.c | 1 + sound/firewire/tascam/tascam-pcm.c | 1 + 10 files changed, 36 insertions(+), 7 deletions(-) diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index c827d7d8d800..c72b2a754775 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -615,6 +615,22 @@ static void update_pcm_pointers(struct amdtp_stream *s, // The program in user process should periodically check the status of intermediate // buffer associated to PCM substream to process PCM frames in the buffer, instead // of receiving notification of period elapsed by poll wait. + // + // Use another work item for period elapsed event to prevent the following AB/BA + // deadlock: + // + // thread 1 thread 2 + // ================================= ================================= + // A.work item (process) pcm ioctl (process) + // v v + // process_rx_packets() B.PCM stream lock + // process_tx_packets() v + // v callbacks in snd_pcm_ops + // update_pcm_pointers() v + // snd_pcm_elapsed() fw_iso_context_flush_completions() + // snd_pcm_stream_lock_irqsave() disable_work_sync() + // v v + // wait until release of B wait until A exits if (!pcm->runtime->no_period_wakeup) queue_work(system_highpri_wq, &s->period_work); } @@ -1055,8 +1071,15 @@ static void generate_rx_packet_descs(struct amdtp_stream *s, struct pkt_desc *de static inline void cancel_stream(struct amdtp_stream *s) { + struct work_struct *work = current_work(); + s->packet_index = -1; - if (in_softirq()) + + // Detect work items for any isochronous context. The work item for pcm_period_work() + // should be avoided since the call of snd_pcm_period_elapsed() can reach via + // snd_pcm_ops.pointer() under acquiring PCM stream(group) lock and causes dead lock at + // snd_pcm_stop_xrun(). + if (work && work != &s->period_work) amdtp_stream_pcm_abort(s); WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN); } @@ -1856,12 +1879,9 @@ unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d, struct amdtp_stream *irq_target = d->irq_target; if (irq_target && amdtp_stream_running(irq_target)) { - // use wq to prevent AB/BA deadlock competition for - // substream lock: - // fw_iso_context_flush_completions() acquires - // lock by ohci_flush_iso_completions(), - // amdtp-stream process_rx_packets() attempts to - // acquire same lock by snd_pcm_elapsed() + // The work item to call snd_pcm_period_elapsed() can reach here by the call of + // snd_pcm_ops.pointer(), however less packets would be available then. Therefore + // the following call is just for user process contexts. if (current_work() != &s->period_work) fw_iso_context_flush_completions(irq_target->context); } diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index ce49eef0fcba..360ebf3c4ca2 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -367,6 +367,7 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) goto end; pcm->private_data = bebob; + pcm->nonatomic = true; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", bebob->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index d64366217d57..2cf2adb48f2a 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -441,6 +441,7 @@ int snd_dice_create_pcm(struct snd_dice *dice) if (err < 0) return err; pcm->private_data = dice; + pcm->nonatomic = true; strcpy(pcm->name, dice->card->shortname); if (capture > 0) diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c index 3bd1575c9d9c..85e65cbc00c4 100644 --- a/sound/firewire/digi00x/digi00x-pcm.c +++ b/sound/firewire/digi00x/digi00x-pcm.c @@ -350,6 +350,7 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) return err; pcm->private_data = dg00x; + pcm->nonatomic = true; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", dg00x->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c index ec915671a79b..63457d24a288 100644 --- a/sound/firewire/fireface/ff-pcm.c +++ b/sound/firewire/fireface/ff-pcm.c @@ -390,6 +390,7 @@ int snd_ff_create_pcm_devices(struct snd_ff *ff) return err; pcm->private_data = ff; + pcm->nonatomic = true; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", ff->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index c3c21860b245..eaf7778211de 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -397,6 +397,7 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw) goto end; pcm->private_data = efw; + pcm->nonatomic = true; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", efw->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 806f82c9ceee..b1e059f0d473 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -454,6 +454,7 @@ static int isight_create_pcm(struct isight *isight) if (err < 0) return err; pcm->private_data = isight; + pcm->nonatomic = true; strcpy(pcm->name, "iSight"); isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; isight->pcm->ops = &ops; diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c index d410c2efbde5..f3b48495acae 100644 --- a/sound/firewire/motu/motu-pcm.c +++ b/sound/firewire/motu/motu-pcm.c @@ -360,6 +360,7 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu) if (err < 0) return err; pcm->private_data = motu; + pcm->nonatomic = true; strcpy(pcm->name, motu->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index 5f43a0b826d2..8ca9dde54ec6 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -440,6 +440,7 @@ int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) return err; pcm->private_data = oxfw; + pcm->nonatomic = true; strcpy(pcm->name, oxfw->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); if (cap > 0) diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c index f6da571707ac..a73003ac11e6 100644 --- a/sound/firewire/tascam/tascam-pcm.c +++ b/sound/firewire/tascam/tascam-pcm.c @@ -279,6 +279,7 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm) return err; pcm->private_data = tscm; + pcm->nonatomic = true; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", tscm->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); From 7519033f319d4dc8066cb3a37c1276610f4cb0ca Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 5 Sep 2024 22:10:29 +0900 Subject: [PATCH 46/55] firewire: core: use WARN_ON_ONCE() to avoid superfluous dumps It is enough to notify programming mistakes to programmers just once. Suggested-by: Takashi Iwai Link: https://lore.kernel.org/r/20240905131029.6433-1-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-iso.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index af76fa1823f1..a249974a0f87 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -220,7 +220,7 @@ int fw_iso_context_flush_completions(struct fw_iso_context *ctx) might_sleep(); // Avoid dead lock due to programming mistake. - if (WARN_ON(current_work() == &ctx->work)) + if (WARN_ON_ONCE(current_work() == &ctx->work)) return 0; disable_work_sync(&ctx->work); @@ -244,7 +244,7 @@ int fw_iso_context_stop(struct fw_iso_context *ctx) might_sleep(); // Avoid dead lock due to programming mistake. - if (WARN_ON(current_work() == &ctx->work)) + if (WARN_ON_ONCE(current_work() == &ctx->work)) return 0; err = ctx->card->driver->stop_iso(ctx); From 446216bd8e5d4a3ffc9738f6adffc98fbfdcc582 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 8 Sep 2024 13:05:48 +0900 Subject: [PATCH 47/55] firewire: core: expose kernel API to schedule work item to process isochronous context In packet-per-buffer mode for isochronous context of 1394 OHCI, software can schedule hardIRQ to the buffer in which the content of isochronous packet is processed. The actual behaviour is different between isochronous receive (IR) and transmit (IT) contexts in respect to isochronous cycle in which the hardIRQ occurs. In IR context, the hardIRQ occurs when the buffer is filled actually by the content of received packet. If there are any isochronous cycles in which the packet transmission is skipped, it is postponed to generate the hardIRQ in respect to the isochronous cycle. In IT context, software can schedule the content of packet every isochronous cycle including skipping, therefore the hardIRQ occurs in the isochronous cycle to which the software scheduled. ALSA firewire stack uses the packet-per-buffer mode for both IR/IT contexts. To process time stamp per packet (or per sample in some cases) steadily for media clock recovery against unexpected transmission skips, it uses an IT context to operate all of isochronous contexts by calls of fw_iso_context_flush_completions() in the bottom-half of hardIRQ for the IT context. Although it looks well to handle all of isochronous contexts in a single bottom-half context, it relatively takes longer time. In the future code integration (not yet), it is possible to apply parallelism method to process these context. In the case, it is useful to allow unit drivers to schedule work items to process these isochronous contexts. As a preparation, this commit exposes fw_iso_context_schedule_flush_completions() as a kernel API available by unit drivers. It is renamed from fw_iso_context_queue_work() since it is a counter part of fw_iso_context_flush_completions(). Link: https://lore.kernel.org/r/20240908040549.75304-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- Documentation/driver-api/firewire.rst | 2 ++ drivers/firewire/core.h | 5 ----- drivers/firewire/ohci.c | 4 ++-- include/linux/firewire.h | 17 +++++++++++++++++ 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/Documentation/driver-api/firewire.rst b/Documentation/driver-api/firewire.rst index d3cfa73cbb2b..28a32410f7d2 100644 --- a/Documentation/driver-api/firewire.rst +++ b/Documentation/driver-api/firewire.rst @@ -43,6 +43,8 @@ Firewire core transaction interfaces Firewire Isochronous I/O interfaces =================================== +.. kernel-doc:: include/linux/firewire.h + :functions: fw_iso_context_schedule_flush_completions .. kernel-doc:: drivers/firewire/core-iso.c :export: diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 2874f316156a..0ae2c84ecafe 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -164,11 +164,6 @@ static inline void fw_iso_context_init_work(struct fw_iso_context *ctx, work_fun INIT_WORK(&ctx->work, func); } -static inline void fw_iso_context_queue_work(struct fw_iso_context *ctx) -{ - queue_work(ctx->card->isoc_wq, &ctx->work); -} - /* -topology */ diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index d0b1fccc450f..3a911cfb5ff3 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -2283,7 +2283,7 @@ static irqreturn_t irq_handler(int irq, void *data) while (iso_event) { i = ffs(iso_event) - 1; - fw_iso_context_queue_work(&ohci->ir_context_list[i].base); + fw_iso_context_schedule_flush_completions(&ohci->ir_context_list[i].base); iso_event &= ~(1 << i); } } @@ -2294,7 +2294,7 @@ static irqreturn_t irq_handler(int irq, void *data) while (iso_event) { i = ffs(iso_event) - 1; - fw_iso_context_queue_work(&ohci->it_context_list[i].base); + fw_iso_context_schedule_flush_completions(&ohci->it_context_list[i].base); iso_event &= ~(1 << i); } } diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 72f497b61739..f815d12deda0 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -531,6 +531,23 @@ int fw_iso_context_queue(struct fw_iso_context *ctx, unsigned long payload); void fw_iso_context_queue_flush(struct fw_iso_context *ctx); int fw_iso_context_flush_completions(struct fw_iso_context *ctx); + +/** + * fw_iso_context_schedule_flush_completions() - schedule work item to process isochronous context. + * @ctx: the isochronous context + * + * Schedule a work item on workqueue to process the isochronous context. The registered callback + * function is called in the worker if some packets have been already transferred since the last + * time. If it is required to process the context in the current context, + * fw_iso_context_flush_completions() is available instead. + * + * Context: Any context. + */ +static inline void fw_iso_context_schedule_flush_completions(struct fw_iso_context *ctx) +{ + queue_work(ctx->card->isoc_wq, &ctx->work); +} + int fw_iso_context_start(struct fw_iso_context *ctx, int cycle, int sync, int tags); int fw_iso_context_stop(struct fw_iso_context *ctx); From 7b713929bbd80a400ceb90f398bffe58e5cc8fc8 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 8 Sep 2024 13:05:49 +0900 Subject: [PATCH 48/55] firewire: core: fulfill documentation of fw_iso_context_flush_completions() The fw_iso_context_flush_completions() is the counterpart of fw_iso_context_schedule_work() to process isochronous context in current process context. This commit fulfills its documentation. Link: https://lore.kernel.org/r/20240908040549.75304-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-iso.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index a249974a0f87..f2394f3ed194 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -209,6 +209,17 @@ void fw_iso_context_queue_flush(struct fw_iso_context *ctx) } EXPORT_SYMBOL(fw_iso_context_queue_flush); +/** + * fw_iso_context_flush_completions() - process isochronous context in current process context. + * @ctx: the isochronous context + * + * Process the isochronous context in the current process context. The registered callback function + * is called if some packets have been already transferred since the last time. If it is required + * to process the context asynchronously, fw_iso_context_schedule_flush_completions() is available + * instead. + * + * Context: Process context. May sleep due to disable_work_sync(). + */ int fw_iso_context_flush_completions(struct fw_iso_context *ctx) { int err; From e97fb38fa1404abef72bac7de2c5bf2bbab4d93b Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 9 Sep 2024 23:00:17 +0900 Subject: [PATCH 49/55] firewire: core: move workqueue handler from 1394 OHCI driver to core function In current implementation, the work item for isochronous context executes the same procedure of fw_iso_context_flush_completions() internally. There is a space to refactor the implementation. This commit calls fw_iso_context_flush_completions() in the work item. It obsoletes fw_iso_context_init_work(). It also obsoletes a pair of disable_work_sync() and enable_work() since the usage of test_and_set_bit_lock() mediates concurrent call already. Link: https://lore.kernel.org/r/20240909140018.65289-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-iso.c | 26 +++++++++------------ drivers/firewire/core.h | 5 ----- drivers/firewire/ohci.c | 45 ++----------------------------------- 3 files changed, 12 insertions(+), 64 deletions(-) diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index f2394f3ed194..9f41c78878ad 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -131,6 +131,13 @@ size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed) return 0; } +static void flush_completions_work(struct work_struct *work) +{ + struct fw_iso_context *ctx = container_of(work, struct fw_iso_context, work); + + fw_iso_context_flush_completions(ctx); +} + struct fw_iso_context *fw_iso_context_create(struct fw_card *card, int type, int channel, int speed, size_t header_size, fw_iso_callback_t callback, void *callback_data) @@ -149,6 +156,7 @@ struct fw_iso_context *fw_iso_context_create(struct fw_card *card, ctx->header_size = header_size; ctx->callback.sc = callback; ctx->callback_data = callback_data; + INIT_WORK(&ctx->work, flush_completions_work); trace_isoc_outbound_allocate(ctx, channel, speed); trace_isoc_inbound_single_allocate(ctx, channel, header_size); @@ -218,29 +226,15 @@ EXPORT_SYMBOL(fw_iso_context_queue_flush); * to process the context asynchronously, fw_iso_context_schedule_flush_completions() is available * instead. * - * Context: Process context. May sleep due to disable_work_sync(). + * Context: Process context. */ int fw_iso_context_flush_completions(struct fw_iso_context *ctx) { - int err; - trace_isoc_outbound_flush_completions(ctx); trace_isoc_inbound_single_flush_completions(ctx); trace_isoc_inbound_multiple_flush_completions(ctx); - might_sleep(); - - // Avoid dead lock due to programming mistake. - if (WARN_ON_ONCE(current_work() == &ctx->work)) - return 0; - - disable_work_sync(&ctx->work); - - err = ctx->card->driver->flush_iso_completions(ctx); - - enable_work(&ctx->work); - - return err; + return ctx->card->driver->flush_iso_completions(ctx); } EXPORT_SYMBOL(fw_iso_context_flush_completions); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 0ae2c84ecafe..96ae366889e0 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -159,11 +159,6 @@ int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count); int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card, enum dma_data_direction direction); -static inline void fw_iso_context_init_work(struct fw_iso_context *ctx, work_func_t func) -{ - INIT_WORK(&ctx->work, func); -} - /* -topology */ diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 3a911cfb5ff3..02ff0363d3ad 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1182,47 +1182,6 @@ static void context_tasklet(unsigned long data) } } -static void ohci_isoc_context_work(struct work_struct *work) -{ - struct fw_iso_context *base = container_of(work, struct fw_iso_context, work); - struct iso_context *isoc_ctx = container_of(base, struct iso_context, base); - struct context *ctx = &isoc_ctx->context; - struct descriptor *d, *last; - u32 address; - int z; - struct descriptor_buffer *desc; - - desc = list_entry(ctx->buffer_list.next, struct descriptor_buffer, list); - last = ctx->last; - while (last->branch_address != 0) { - struct descriptor_buffer *old_desc = desc; - - address = le32_to_cpu(last->branch_address); - z = address & 0xf; - address &= ~0xf; - ctx->current_bus = address; - - // If the branch address points to a buffer outside of the current buffer, advance - // to the next buffer. - if (address < desc->buffer_bus || address >= desc->buffer_bus + desc->used) - desc = list_entry(desc->list.next, struct descriptor_buffer, list); - d = desc->buffer + (address - desc->buffer_bus) / sizeof(*d); - last = find_branch_descriptor(d, z); - - if (!ctx->callback(ctx, d, last)) - break; - - if (old_desc != desc) { - // If we've advanced to the next buffer, move the previous buffer to the - // free list. - old_desc->used = 0; - guard(spinlock_irqsave)(&ctx->ohci->lock); - list_move_tail(&old_desc->list, &ctx->buffer_list); - } - ctx->last = last; - } -} - /* * Allocate a new buffer and add it to the list of free buffers for this * context. Must be called with ohci->lock held. @@ -3169,7 +3128,6 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, ret = context_init(&ctx->context, ohci, regs, callback); if (ret < 0) goto out_with_header; - fw_iso_context_init_work(&ctx->base, ohci_isoc_context_work); if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) { set_multichannel_mask(ohci, 0); @@ -3624,7 +3582,8 @@ static int ohci_flush_iso_completions(struct fw_iso_context *base) int ret = 0; if (!test_and_set_bit_lock(0, &ctx->flushing_completions)) { - ohci_isoc_context_work(&base->work); + // Note that tasklet softIRQ is not used to process isochronous context anymore. + context_tasklet((unsigned long)&ctx->context); switch (base->type) { case FW_ISO_CONTEXT_TRANSMIT: From f877f1d81b2ffcac341ff62ae076d7ad5ba83f46 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 9 Sep 2024 23:00:18 +0900 Subject: [PATCH 50/55] firewire: core: use mutex to coordinate concurrent calls to flush completions In current implementation, test_and_set_bit_lock() is used to mediate concurrent calls of ohci_flush_iso_completions(). However, the ad-hoc usage of atomic operations is not preferable. This commit uses mutex_trylock() as the similar operations. The core function is responsible for the mediation, instead of 1394 OHCI driver. Link: https://lore.kernel.org/r/20240909140018.65289-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-iso.c | 11 +++++++++-- drivers/firewire/ohci.c | 37 ++++++++++++++----------------------- include/linux/firewire.h | 1 + 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 9f41c78878ad..1405d2e9cb2c 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -157,6 +157,7 @@ struct fw_iso_context *fw_iso_context_create(struct fw_card *card, ctx->callback.sc = callback; ctx->callback_data = callback_data; INIT_WORK(&ctx->work, flush_completions_work); + mutex_init(&ctx->flushing_completions_mutex); trace_isoc_outbound_allocate(ctx, channel, speed); trace_isoc_inbound_single_allocate(ctx, channel, header_size); @@ -173,6 +174,8 @@ void fw_iso_context_destroy(struct fw_iso_context *ctx) trace_isoc_inbound_multiple_destroy(ctx); ctx->card->driver->free_iso_context(ctx); + + mutex_destroy(&ctx->flushing_completions_mutex); } EXPORT_SYMBOL(fw_iso_context_destroy); @@ -226,7 +229,7 @@ EXPORT_SYMBOL(fw_iso_context_queue_flush); * to process the context asynchronously, fw_iso_context_schedule_flush_completions() is available * instead. * - * Context: Process context. + * Context: Process context due to mutex_trylock(). */ int fw_iso_context_flush_completions(struct fw_iso_context *ctx) { @@ -234,7 +237,11 @@ int fw_iso_context_flush_completions(struct fw_iso_context *ctx) trace_isoc_inbound_single_flush_completions(ctx); trace_isoc_inbound_multiple_flush_completions(ctx); - return ctx->card->driver->flush_iso_completions(ctx); + scoped_cond_guard(mutex_try, /* nothing to do */, &ctx->flushing_completions_mutex) { + return ctx->card->driver->flush_iso_completions(ctx); + } + + return 0; } EXPORT_SYMBOL(fw_iso_context_flush_completions); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 02ff0363d3ad..b182998a77f4 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -166,7 +166,6 @@ struct iso_context { struct context context; void *header; size_t header_length; - unsigned long flushing_completions; u32 mc_buffer_bus; u16 mc_completed; u16 last_timestamp; @@ -3579,31 +3578,23 @@ static void ohci_flush_queue_iso(struct fw_iso_context *base) static int ohci_flush_iso_completions(struct fw_iso_context *base) { struct iso_context *ctx = container_of(base, struct iso_context, base); - int ret = 0; - if (!test_and_set_bit_lock(0, &ctx->flushing_completions)) { - // Note that tasklet softIRQ is not used to process isochronous context anymore. - context_tasklet((unsigned long)&ctx->context); + // Note that tasklet softIRQ is not used to process isochronous context anymore. + context_tasklet((unsigned long)&ctx->context); - switch (base->type) { - case FW_ISO_CONTEXT_TRANSMIT: - case FW_ISO_CONTEXT_RECEIVE: - if (ctx->header_length != 0) - flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_FLUSH); - break; - case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: - if (ctx->mc_completed != 0) - flush_ir_buffer_fill(ctx); - break; - default: - ret = -ENOSYS; - } - - clear_bit_unlock(0, &ctx->flushing_completions); - smp_mb__after_atomic(); + switch (base->type) { + case FW_ISO_CONTEXT_TRANSMIT: + case FW_ISO_CONTEXT_RECEIVE: + if (ctx->header_length != 0) + flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_FLUSH); + return 0; + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + if (ctx->mc_completed != 0) + flush_ir_buffer_fill(ctx); + return 0; + default: + return -ENOSYS; } - - return ret; } static const struct fw_card_driver ohci_driver = { diff --git a/include/linux/firewire.h b/include/linux/firewire.h index f815d12deda0..19e8c5f9537c 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -512,6 +512,7 @@ union fw_iso_callback { struct fw_iso_context { struct fw_card *card; struct work_struct work; + struct mutex flushing_completions_mutex; int type; int channel; int speed; From c45b9a07b6392fa224ca76b89f24dae1046eef09 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 12 Sep 2024 22:30:34 +0900 Subject: [PATCH 51/55] Revert "firewire: core: use mutex to coordinate concurrent calls to flush completions" This reverts commit d9605d67562505e27dcc0f71af418118d3db91e5, since this commit is on the following reverted changes. Link: https://lore.kernel.org/r/20240912133038.238786-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-iso.c | 11 ++--------- drivers/firewire/ohci.c | 37 +++++++++++++++++++++++-------------- include/linux/firewire.h | 1 - 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 1405d2e9cb2c..9f41c78878ad 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -157,7 +157,6 @@ struct fw_iso_context *fw_iso_context_create(struct fw_card *card, ctx->callback.sc = callback; ctx->callback_data = callback_data; INIT_WORK(&ctx->work, flush_completions_work); - mutex_init(&ctx->flushing_completions_mutex); trace_isoc_outbound_allocate(ctx, channel, speed); trace_isoc_inbound_single_allocate(ctx, channel, header_size); @@ -174,8 +173,6 @@ void fw_iso_context_destroy(struct fw_iso_context *ctx) trace_isoc_inbound_multiple_destroy(ctx); ctx->card->driver->free_iso_context(ctx); - - mutex_destroy(&ctx->flushing_completions_mutex); } EXPORT_SYMBOL(fw_iso_context_destroy); @@ -229,7 +226,7 @@ EXPORT_SYMBOL(fw_iso_context_queue_flush); * to process the context asynchronously, fw_iso_context_schedule_flush_completions() is available * instead. * - * Context: Process context due to mutex_trylock(). + * Context: Process context. */ int fw_iso_context_flush_completions(struct fw_iso_context *ctx) { @@ -237,11 +234,7 @@ int fw_iso_context_flush_completions(struct fw_iso_context *ctx) trace_isoc_inbound_single_flush_completions(ctx); trace_isoc_inbound_multiple_flush_completions(ctx); - scoped_cond_guard(mutex_try, /* nothing to do */, &ctx->flushing_completions_mutex) { - return ctx->card->driver->flush_iso_completions(ctx); - } - - return 0; + return ctx->card->driver->flush_iso_completions(ctx); } EXPORT_SYMBOL(fw_iso_context_flush_completions); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index b182998a77f4..02ff0363d3ad 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -166,6 +166,7 @@ struct iso_context { struct context context; void *header; size_t header_length; + unsigned long flushing_completions; u32 mc_buffer_bus; u16 mc_completed; u16 last_timestamp; @@ -3578,23 +3579,31 @@ static void ohci_flush_queue_iso(struct fw_iso_context *base) static int ohci_flush_iso_completions(struct fw_iso_context *base) { struct iso_context *ctx = container_of(base, struct iso_context, base); + int ret = 0; - // Note that tasklet softIRQ is not used to process isochronous context anymore. - context_tasklet((unsigned long)&ctx->context); + if (!test_and_set_bit_lock(0, &ctx->flushing_completions)) { + // Note that tasklet softIRQ is not used to process isochronous context anymore. + context_tasklet((unsigned long)&ctx->context); - switch (base->type) { - case FW_ISO_CONTEXT_TRANSMIT: - case FW_ISO_CONTEXT_RECEIVE: - if (ctx->header_length != 0) - flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_FLUSH); - return 0; - case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: - if (ctx->mc_completed != 0) - flush_ir_buffer_fill(ctx); - return 0; - default: - return -ENOSYS; + switch (base->type) { + case FW_ISO_CONTEXT_TRANSMIT: + case FW_ISO_CONTEXT_RECEIVE: + if (ctx->header_length != 0) + flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_FLUSH); + break; + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + if (ctx->mc_completed != 0) + flush_ir_buffer_fill(ctx); + break; + default: + ret = -ENOSYS; + } + + clear_bit_unlock(0, &ctx->flushing_completions); + smp_mb__after_atomic(); } + + return ret; } static const struct fw_card_driver ohci_driver = { diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 19e8c5f9537c..f815d12deda0 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -512,7 +512,6 @@ union fw_iso_callback { struct fw_iso_context { struct fw_card *card; struct work_struct work; - struct mutex flushing_completions_mutex; int type; int channel; int speed; From 6ffa9bd6ebce0626e62358dda59effe5758ebfc5 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 12 Sep 2024 22:30:35 +0900 Subject: [PATCH 52/55] Revert "firewire: core: move workqueue handler from 1394 OHCI driver to core function" This reverts commit 767bfb9ef27ebf760290d9f8bc303828b018c312. It appears that the call of ohci_flush_iso_completions() in the work item scheduled by hardIRQ of 1394 OHCI for any isochronous context changes the timing to queue events in the view of user space application. Link: https://lore.kernel.org/r/20240912133038.238786-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-iso.c | 26 ++++++++++++--------- drivers/firewire/core.h | 5 +++++ drivers/firewire/ohci.c | 45 +++++++++++++++++++++++++++++++++++-- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 9f41c78878ad..f2394f3ed194 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -131,13 +131,6 @@ size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed) return 0; } -static void flush_completions_work(struct work_struct *work) -{ - struct fw_iso_context *ctx = container_of(work, struct fw_iso_context, work); - - fw_iso_context_flush_completions(ctx); -} - struct fw_iso_context *fw_iso_context_create(struct fw_card *card, int type, int channel, int speed, size_t header_size, fw_iso_callback_t callback, void *callback_data) @@ -156,7 +149,6 @@ struct fw_iso_context *fw_iso_context_create(struct fw_card *card, ctx->header_size = header_size; ctx->callback.sc = callback; ctx->callback_data = callback_data; - INIT_WORK(&ctx->work, flush_completions_work); trace_isoc_outbound_allocate(ctx, channel, speed); trace_isoc_inbound_single_allocate(ctx, channel, header_size); @@ -226,15 +218,29 @@ EXPORT_SYMBOL(fw_iso_context_queue_flush); * to process the context asynchronously, fw_iso_context_schedule_flush_completions() is available * instead. * - * Context: Process context. + * Context: Process context. May sleep due to disable_work_sync(). */ int fw_iso_context_flush_completions(struct fw_iso_context *ctx) { + int err; + trace_isoc_outbound_flush_completions(ctx); trace_isoc_inbound_single_flush_completions(ctx); trace_isoc_inbound_multiple_flush_completions(ctx); - return ctx->card->driver->flush_iso_completions(ctx); + might_sleep(); + + // Avoid dead lock due to programming mistake. + if (WARN_ON_ONCE(current_work() == &ctx->work)) + return 0; + + disable_work_sync(&ctx->work); + + err = ctx->card->driver->flush_iso_completions(ctx); + + enable_work(&ctx->work); + + return err; } EXPORT_SYMBOL(fw_iso_context_flush_completions); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 96ae366889e0..0ae2c84ecafe 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -159,6 +159,11 @@ int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count); int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card, enum dma_data_direction direction); +static inline void fw_iso_context_init_work(struct fw_iso_context *ctx, work_func_t func) +{ + INIT_WORK(&ctx->work, func); +} + /* -topology */ diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 02ff0363d3ad..3a911cfb5ff3 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1182,6 +1182,47 @@ static void context_tasklet(unsigned long data) } } +static void ohci_isoc_context_work(struct work_struct *work) +{ + struct fw_iso_context *base = container_of(work, struct fw_iso_context, work); + struct iso_context *isoc_ctx = container_of(base, struct iso_context, base); + struct context *ctx = &isoc_ctx->context; + struct descriptor *d, *last; + u32 address; + int z; + struct descriptor_buffer *desc; + + desc = list_entry(ctx->buffer_list.next, struct descriptor_buffer, list); + last = ctx->last; + while (last->branch_address != 0) { + struct descriptor_buffer *old_desc = desc; + + address = le32_to_cpu(last->branch_address); + z = address & 0xf; + address &= ~0xf; + ctx->current_bus = address; + + // If the branch address points to a buffer outside of the current buffer, advance + // to the next buffer. + if (address < desc->buffer_bus || address >= desc->buffer_bus + desc->used) + desc = list_entry(desc->list.next, struct descriptor_buffer, list); + d = desc->buffer + (address - desc->buffer_bus) / sizeof(*d); + last = find_branch_descriptor(d, z); + + if (!ctx->callback(ctx, d, last)) + break; + + if (old_desc != desc) { + // If we've advanced to the next buffer, move the previous buffer to the + // free list. + old_desc->used = 0; + guard(spinlock_irqsave)(&ctx->ohci->lock); + list_move_tail(&old_desc->list, &ctx->buffer_list); + } + ctx->last = last; + } +} + /* * Allocate a new buffer and add it to the list of free buffers for this * context. Must be called with ohci->lock held. @@ -3128,6 +3169,7 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, ret = context_init(&ctx->context, ohci, regs, callback); if (ret < 0) goto out_with_header; + fw_iso_context_init_work(&ctx->base, ohci_isoc_context_work); if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) { set_multichannel_mask(ohci, 0); @@ -3582,8 +3624,7 @@ static int ohci_flush_iso_completions(struct fw_iso_context *base) int ret = 0; if (!test_and_set_bit_lock(0, &ctx->flushing_completions)) { - // Note that tasklet softIRQ is not used to process isochronous context anymore. - context_tasklet((unsigned long)&ctx->context); + ohci_isoc_context_work(&base->work); switch (base->type) { case FW_ISO_CONTEXT_TRANSMIT: From 5d567654be41ea59cc15a63779209af45615f47e Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 12 Sep 2024 22:30:36 +0900 Subject: [PATCH 53/55] firewire: core: add helper function to retire descriptors Both IR/IT contexts use the same code to retire completed descriptors as AT context uses. This commit adds a helper function to reduce the duplicated codes. Link: https://lore.kernel.org/r/20240912133038.238786-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 45 +++++++++-------------------------------- 1 file changed, 9 insertions(+), 36 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 3a911cfb5ff3..4af4c9af4fe4 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1141,9 +1141,8 @@ static struct descriptor *find_branch_descriptor(struct descriptor *d, int z) return d + z - 1; } -static void context_tasklet(unsigned long data) +static void context_retire_descriptors(struct context *ctx) { - struct context *ctx = (struct context *) data; struct descriptor *d, *last; u32 address; int z; @@ -1182,45 +1181,19 @@ static void context_tasklet(unsigned long data) } } +static void context_tasklet(unsigned long data) +{ + struct context *ctx = (struct context *) data; + + context_retire_descriptors(ctx); +} + static void ohci_isoc_context_work(struct work_struct *work) { struct fw_iso_context *base = container_of(work, struct fw_iso_context, work); struct iso_context *isoc_ctx = container_of(base, struct iso_context, base); - struct context *ctx = &isoc_ctx->context; - struct descriptor *d, *last; - u32 address; - int z; - struct descriptor_buffer *desc; - desc = list_entry(ctx->buffer_list.next, struct descriptor_buffer, list); - last = ctx->last; - while (last->branch_address != 0) { - struct descriptor_buffer *old_desc = desc; - - address = le32_to_cpu(last->branch_address); - z = address & 0xf; - address &= ~0xf; - ctx->current_bus = address; - - // If the branch address points to a buffer outside of the current buffer, advance - // to the next buffer. - if (address < desc->buffer_bus || address >= desc->buffer_bus + desc->used) - desc = list_entry(desc->list.next, struct descriptor_buffer, list); - d = desc->buffer + (address - desc->buffer_bus) / sizeof(*d); - last = find_branch_descriptor(d, z); - - if (!ctx->callback(ctx, d, last)) - break; - - if (old_desc != desc) { - // If we've advanced to the next buffer, move the previous buffer to the - // free list. - old_desc->used = 0; - guard(spinlock_irqsave)(&ctx->ohci->lock); - list_move_tail(&old_desc->list, &ctx->buffer_list); - } - ctx->last = last; - } + context_retire_descriptors(&isoc_ctx->context); } /* From 4010cb1efda08ec6fd02ec5db9da909322ef352e Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 12 Sep 2024 22:30:37 +0900 Subject: [PATCH 54/55] firewire: core: update documentation of kernel APIs for flushing completions There is a slight difference between fw_iso_context_flush_completions() and fw_iso_context_schedule_flush_completions(). This commit updates the documentations for them. Link: https://lore.kernel.org/r/20240912133038.238786-5-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-iso.c | 9 ++++++--- include/linux/firewire.h | 8 +++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index f2394f3ed194..a67493862c85 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -214,9 +214,12 @@ EXPORT_SYMBOL(fw_iso_context_queue_flush); * @ctx: the isochronous context * * Process the isochronous context in the current process context. The registered callback function - * is called if some packets have been already transferred since the last time. If it is required - * to process the context asynchronously, fw_iso_context_schedule_flush_completions() is available - * instead. + * is called when a queued packet buffer with the interrupt flag is completed, either after + * transmission in the IT context or after being filled in the IR context. Additionally, the + * callback function is also called for the packet buffer completed at last. Furthermore, the + * callback function is called as well when the header buffer in the context becomes full. If it is + * required to process the context asynchronously, fw_iso_context_schedule_flush_completions() is + * available instead. * * Context: Process context. May sleep due to disable_work_sync(). */ diff --git a/include/linux/firewire.h b/include/linux/firewire.h index f815d12deda0..b632eec3ab52 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -537,9 +537,11 @@ int fw_iso_context_flush_completions(struct fw_iso_context *ctx); * @ctx: the isochronous context * * Schedule a work item on workqueue to process the isochronous context. The registered callback - * function is called in the worker if some packets have been already transferred since the last - * time. If it is required to process the context in the current context, - * fw_iso_context_flush_completions() is available instead. + * function is called by the worker when a queued packet buffer with the interrupt flag is + * completed, either after transmission in the IT context or after being filled in the IR context. + * The callback function is also called when the header buffer in the context becomes full, If it + * is required to process the context in the current context, fw_iso_context_flush_completions() is + * available instead. * * Context: Any context. */ From f1cba5212e252243a539e079813bc96fbf53e241 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 12 Sep 2024 22:30:38 +0900 Subject: [PATCH 55/55] firewire: core: rename cause flag of tracepoints event The flag of FW_ISO_CONTEXT_COMPLETIONS_CAUSE_IRQ directly causes hardIRQ request by 1394 OHCI hardware when the corresponding isochronous packet is transferred, however it is not so directly associated to hardIRQ processing itself. This commit renames the flag so that it relates to interrupt parameter of internal packet data. Link: https://lore.kernel.org/r/20240912133038.238786-6-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 6 +++--- include/trace/events/firewire.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 4af4c9af4fe4..7ee55c2804de 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -2927,7 +2927,7 @@ static int handle_ir_packet_per_buffer(struct context *context, copy_iso_headers(ctx, (u32 *) (last + 1)); if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) - flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_IRQ); + flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_INTERRUPT); return 1; } @@ -2963,7 +2963,7 @@ static int handle_ir_buffer_fill(struct context *context, if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) { trace_isoc_inbound_multiple_completions(&ctx->base, completed, - FW_ISO_CONTEXT_COMPLETIONS_CAUSE_IRQ); + FW_ISO_CONTEXT_COMPLETIONS_CAUSE_INTERRUPT); ctx->base.callback.mc(&ctx->base, buffer_dma + completed, @@ -3059,7 +3059,7 @@ static int handle_it_packet(struct context *context, ctx->header_length += 4; if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) - flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_IRQ); + flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_INTERRUPT); return 1; } diff --git a/include/trace/events/firewire.h b/include/trace/events/firewire.h index b108176deb22..ad0e0cf82b9c 100644 --- a/include/trace/events/firewire.h +++ b/include/trace/events/firewire.h @@ -830,13 +830,13 @@ TRACE_EVENT_CONDITION(isoc_inbound_multiple_queue, #ifndef show_cause enum fw_iso_context_completions_cause { FW_ISO_CONTEXT_COMPLETIONS_CAUSE_FLUSH = 0, - FW_ISO_CONTEXT_COMPLETIONS_CAUSE_IRQ, + FW_ISO_CONTEXT_COMPLETIONS_CAUSE_INTERRUPT, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_HEADER_OVERFLOW, }; #define show_cause(cause) \ __print_symbolic(cause, \ { FW_ISO_CONTEXT_COMPLETIONS_CAUSE_FLUSH, "FLUSH" }, \ - { FW_ISO_CONTEXT_COMPLETIONS_CAUSE_IRQ, "IRQ" }, \ + { FW_ISO_CONTEXT_COMPLETIONS_CAUSE_INTERRUPT, "INTERRUPT" }, \ { FW_ISO_CONTEXT_COMPLETIONS_CAUSE_HEADER_OVERFLOW, "HEADER_OVERFLOW" } \ ) #endif