Commit 70618359 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'firewire-updates-6.17' of...

Merge tag 'firewire-updates-6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394

Pull firewire updates from Takashi Sakamoto:
 "This update replaces the remaining tasklet usage in the FireWire
  subsystem with workqueue for asynchronous packet transmission. With
  this change, tasklets are now fully eliminated from the subsystem.

  Asynchronous packet transmission is used for serial bus topology
  management as well as for the operation of the SBP-2 protocol driver
  (firewire-sbp2). To ensure reliability during low-memory conditions,
  the associated workqueue is created with the WQ_MEM_RECLAIM flag,
  allowing it to participate in memory reclaim paths. Other attributes
  are aligned with those used for isochronous packet handling, which was
  migrated to workqueues in v6.12.

  The workqueues are sleepable and support preemptible work items,
  making them more suitable for real-time workloads that benefit from
  timely task preemption at the system level.

  There remains an issue where 'schedule()' may be called within an RCU
  read-side critical section, due to a direct replacement of
  'tasklet_disable_in_atomic()' with 'disable_work_sync()'. A proposed
  fix for this has been posted[1], and is currently under review and
  testing. It is expected to be sent upstream later"

Link: https://lore.kernel.org/lkml/20250728015125.17825-1-o-takashi@sakamocchi.jp/ [1]

* tag 'firewire-updates-6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394:
  firewire: ohci: reduce the size of common context structure by extracting members into AT structure
  firewire: core: minor code refactoring to localize table of gap count
  firewire: ohci: use workqueue to handle events of AT request/response contexts
  firewire: ohci: use workqueue to handle events of AR request/response contexts
  firewire: core: allocate workqueue for AR/AT request/response contexts
  firewire: core: use from_work() macro to expand parent structure of work_struct
  firewire: ohci: use from_work() macro to expand parent structure of work_struct
  firewire: ohci: correct code comments about bus_reset tasklet
parents a6923c06 95a042a0
Loading
Loading
Loading
Loading
+39 −20
Original line number Diff line number Diff line
@@ -237,7 +237,7 @@ EXPORT_SYMBOL(fw_schedule_bus_reset);

static void br_work(struct work_struct *work)
{
	struct fw_card *card = container_of(work, struct fw_card, br_work.work);
	struct fw_card *card = from_work(card, work, br_work.work);

	/* Delay for 2s after last reset per IEEE 1394 clause 8.2.1. */
	if (card->reset_jiffies != 0 &&
@@ -273,10 +273,6 @@ static void allocate_broadcast_channel(struct fw_card *card, int generation)
			      fw_device_set_broadcast_channel);
}

static const char gap_count_table[] = {
	63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40
};

void fw_schedule_bm_work(struct fw_card *card, unsigned long delay)
{
	fw_card_get(card);
@@ -286,7 +282,10 @@ void fw_schedule_bm_work(struct fw_card *card, unsigned long delay)

static void bm_work(struct work_struct *work)
{
	struct fw_card *card = container_of(work, struct fw_card, bm_work.work);
	static const char gap_count_table[] = {
		63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40
	};
	struct fw_card *card = from_work(card, work, bm_work.work);
	struct fw_device *root_device, *irm_device;
	struct fw_node *root_node;
	int root_id, new_root_id, irm_id, bm_id, local_id;
@@ -574,7 +573,6 @@ EXPORT_SYMBOL(fw_card_initialize);
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:
@@ -589,29 +587,48 @@ int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid,
	//  * == 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",
	card->isoc_wq = alloc_workqueue("firewire-isoc-card%u",
					WQ_UNBOUND | WQ_FREEZABLE | WQ_HIGHPRI | WQ_SYSFS,
					supported_isoc_contexts, card->index);
	if (!isoc_wq)
	if (!card->isoc_wq)
		return -ENOMEM;

	// This workqueue should be:
	//  * != WQ_BH			Sleepable.
	//  * == WQ_UNBOUND		Any core can process data for asynchronous context.
	//  * == WQ_MEM_RECLAIM		Used for any backend of block device.
	//  * == WQ_FREEZABLE		The target device would not be available when being freezed.
	//  * == WQ_HIGHPRI		High priority to process semi-realtime timestamped data.
	//  * == WQ_SYSFS		Parameters are available via sysfs.
	//  * max_active == 4		A hardIRQ could notify events for a pair of requests and
	//				response AR/AT contexts.
	card->async_wq = alloc_workqueue("firewire-async-card%u",
					 WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE | WQ_HIGHPRI | WQ_SYSFS,
					 4, card->index);
	if (!card->async_wq) {
		ret = -ENOMEM;
		goto err_isoc;
	}

	card->max_receive = max_receive;
	card->link_speed = link_speed;
	card->guid = guid;

	guard(mutex)(&card_mutex);

	scoped_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) {
		destroy_workqueue(isoc_wq);
		return ret;
	}
		if (ret < 0)
			goto err_async;

	card->isoc_wq = isoc_wq;
		list_add_tail(&card->link, &card_list);
	}

	return 0;
err_async:
	destroy_workqueue(card->async_wq);
err_isoc:
	destroy_workqueue(card->isoc_wq);
	return ret;
}
EXPORT_SYMBOL(fw_card_add);

@@ -744,6 +761,7 @@ void fw_core_remove_card(struct fw_card *card)
	dummy_driver.stop_iso		= card->driver->stop_iso;
	card->driver = &dummy_driver;
	drain_workqueue(card->isoc_wq);
	drain_workqueue(card->async_wq);

	scoped_guard(spinlock_irqsave, &card->lock)
		fw_destroy_nodes(card);
@@ -753,6 +771,7 @@ void fw_core_remove_card(struct fw_card *card)
	wait_for_completion(&card->done);

	destroy_workqueue(card->isoc_wq);
	destroy_workqueue(card->async_wq);

	WARN_ON(!list_empty(&card->transaction_list));
}
+1 −2
Original line number Diff line number Diff line
@@ -1313,8 +1313,7 @@ static int ioctl_get_cycle_timer(struct client *client, union ioctl_arg *arg)
static void iso_resource_work(struct work_struct *work)
{
	struct iso_resource_event *e;
	struct iso_resource *r =
			container_of(work, struct iso_resource, work.work);
	struct iso_resource *r = from_work(r, work, work.work);
	struct client *client = r->client;
	unsigned long index = r->resource.handle;
	int generation, channel, bandwidth, todo;
+5 −10
Original line number Diff line number Diff line
@@ -853,8 +853,7 @@ static void fw_schedule_device_work(struct fw_device *device,

static void fw_device_shutdown(struct work_struct *work)
{
	struct fw_device *device =
		container_of(work, struct fw_device, work.work);
	struct fw_device *device = from_work(device, work, work.work);

	if (time_before64(get_jiffies_64(),
			  device->card->reset_jiffies + SHUTDOWN_DELAY)
@@ -921,8 +920,7 @@ static int update_unit(struct device *dev, void *data)

static void fw_device_update(struct work_struct *work)
{
	struct fw_device *device =
		container_of(work, struct fw_device, work.work);
	struct fw_device *device = from_work(device, work, work.work);

	fw_device_cdev_update(device);
	device_for_each_child(&device->device, NULL, update_unit);
@@ -1002,8 +1000,7 @@ static int compare_configuration_rom(struct device *dev, const void *data)

static void fw_device_init(struct work_struct *work)
{
	struct fw_device *device =
		container_of(work, struct fw_device, work.work);
	struct fw_device *device = from_work(device, work, work.work);
	struct fw_card *card = device->card;
	struct device *found;
	u32 minor;
@@ -1184,8 +1181,7 @@ static int reread_config_rom(struct fw_device *device, int generation,

static void fw_device_refresh(struct work_struct *work)
{
	struct fw_device *device =
		container_of(work, struct fw_device, work.work);
	struct fw_device *device = from_work(device, work, work.work);
	struct fw_card *card = device->card;
	int ret, node_id = device->node_id;
	bool changed;
@@ -1251,8 +1247,7 @@ static void fw_device_refresh(struct work_struct *work)

static void fw_device_workfn(struct work_struct *work)
{
	struct fw_device *device = container_of(to_delayed_work(work),
						struct fw_device, work);
	struct fw_device *device = from_work(device, to_delayed_work(work), work);
	device->workfn(work);
}

+4 −3
Original line number Diff line number Diff line
@@ -557,9 +557,10 @@ const struct fw_address_region fw_unit_space_region =
 *
 * region->start, ->end, and handler->length have to be quadlet-aligned.
 *
 * When a request is received that falls within the specified address range,
 * the specified callback is invoked.  The parameters passed to the callback
 * give the details of the particular request.
 * When a request is received that falls within the specified address range, the specified callback
 * is invoked.  The parameters passed to the callback give the details of the particular request.
 * The callback is invoked in the workqueue context in most cases. However, if the request is
 * initiated by the local node, the callback is invoked in the initiator's context.
 *
 * To be called in process context.
 * Return value:  0 on success, non-zero otherwise.
+2 −2
Original line number Diff line number Diff line
@@ -1007,7 +1007,7 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)

		spin_lock_irqsave(&dev->lock, flags);

		/* If the AT tasklet already ran, we may be last user. */
		/* If the AT work item already ran, we may be last user. */
		free = (ptask->outstanding_pkts == 0 && !ptask->enqueued);
		if (!free)
			ptask->enqueued = true;
@@ -1026,7 +1026,7 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)

	spin_lock_irqsave(&dev->lock, flags);

	/* If the AT tasklet already ran, we may be last user. */
	/* If the AT work item already ran, we may be last user. */
	free = (ptask->outstanding_pkts == 0 && !ptask->enqueued);
	if (!free)
		ptask->enqueued = true;
Loading