cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

iso-resources.c (6626B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * isochronous resources helper functions
      4 *
      5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
      6 */
      7
      8#include <linux/device.h>
      9#include <linux/firewire.h>
     10#include <linux/firewire-constants.h>
     11#include <linux/export.h>
     12#include <linux/jiffies.h>
     13#include <linux/mutex.h>
     14#include <linux/sched.h>
     15#include <linux/spinlock.h>
     16#include "iso-resources.h"
     17
     18/**
     19 * fw_iso_resources_init - initializes a &struct fw_iso_resources
     20 * @r: the resource manager to initialize
     21 * @unit: the device unit for which the resources will be needed
     22 *
     23 * If the device does not support all channel numbers, change @r->channels_mask
     24 * after calling this function.
     25 */
     26int fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit)
     27{
     28	r->channels_mask = ~0uLL;
     29	r->unit = unit;
     30	mutex_init(&r->mutex);
     31	r->allocated = false;
     32
     33	return 0;
     34}
     35EXPORT_SYMBOL(fw_iso_resources_init);
     36
     37/**
     38 * fw_iso_resources_destroy - destroy a resource manager
     39 * @r: the resource manager that is no longer needed
     40 */
     41void fw_iso_resources_destroy(struct fw_iso_resources *r)
     42{
     43	WARN_ON(r->allocated);
     44	mutex_destroy(&r->mutex);
     45}
     46EXPORT_SYMBOL(fw_iso_resources_destroy);
     47
     48static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed)
     49{
     50	unsigned int bytes, s400_bytes;
     51
     52	/* iso packets have three header quadlets and quadlet-aligned payload */
     53	bytes = 3 * 4 + ALIGN(max_payload_bytes, 4);
     54
     55	/* convert to bandwidth units (quadlets at S1600 = bytes at S400) */
     56	if (speed <= SCODE_400)
     57		s400_bytes = bytes * (1 << (SCODE_400 - speed));
     58	else
     59		s400_bytes = DIV_ROUND_UP(bytes, 1 << (speed - SCODE_400));
     60
     61	return s400_bytes;
     62}
     63
     64static int current_bandwidth_overhead(struct fw_card *card)
     65{
     66	/*
     67	 * Under the usual pessimistic assumption (cable length 4.5 m), the
     68	 * isochronous overhead for N cables is 1.797 µs + N * 0.494 µs, or
     69	 * 88.3 + N * 24.3 in bandwidth units.
     70	 *
     71	 * The calculation below tries to deduce N from the current gap count.
     72	 * If the gap count has been optimized by measuring the actual packet
     73	 * transmission time, this derived overhead should be near the actual
     74	 * overhead as well.
     75	 */
     76	return card->gap_count < 63 ? card->gap_count * 97 / 10 + 89 : 512;
     77}
     78
     79static int wait_isoch_resource_delay_after_bus_reset(struct fw_card *card)
     80{
     81	for (;;) {
     82		s64 delay = (card->reset_jiffies + HZ) - get_jiffies_64();
     83		if (delay <= 0)
     84			return 0;
     85		if (schedule_timeout_interruptible(delay) > 0)
     86			return -ERESTARTSYS;
     87	}
     88}
     89
     90/**
     91 * fw_iso_resources_allocate - allocate isochronous channel and bandwidth
     92 * @r: the resource manager
     93 * @max_payload_bytes: the amount of data (including CIP headers) per packet
     94 * @speed: the speed (e.g., SCODE_400) at which the packets will be sent
     95 *
     96 * This function allocates one isochronous channel and enough bandwidth for the
     97 * specified packet size.
     98 *
     99 * Returns the channel number that the caller must use for streaming, or
    100 * a negative error code.  Due to potentionally long delays, this function is
    101 * interruptible and can return -ERESTARTSYS.  On success, the caller is
    102 * responsible for calling fw_iso_resources_update() on bus resets, and
    103 * fw_iso_resources_free() when the resources are not longer needed.
    104 */
    105int fw_iso_resources_allocate(struct fw_iso_resources *r,
    106			      unsigned int max_payload_bytes, int speed)
    107{
    108	struct fw_card *card = fw_parent_device(r->unit)->card;
    109	int bandwidth, channel, err;
    110
    111	if (WARN_ON(r->allocated))
    112		return -EBADFD;
    113
    114	r->bandwidth = packet_bandwidth(max_payload_bytes, speed);
    115
    116retry_after_bus_reset:
    117	spin_lock_irq(&card->lock);
    118	r->generation = card->generation;
    119	r->bandwidth_overhead = current_bandwidth_overhead(card);
    120	spin_unlock_irq(&card->lock);
    121
    122	err = wait_isoch_resource_delay_after_bus_reset(card);
    123	if (err < 0)
    124		return err;
    125
    126	mutex_lock(&r->mutex);
    127
    128	bandwidth = r->bandwidth + r->bandwidth_overhead;
    129	fw_iso_resource_manage(card, r->generation, r->channels_mask,
    130			       &channel, &bandwidth, true);
    131	if (channel == -EAGAIN) {
    132		mutex_unlock(&r->mutex);
    133		goto retry_after_bus_reset;
    134	}
    135	if (channel >= 0) {
    136		r->channel = channel;
    137		r->allocated = true;
    138	} else {
    139		if (channel == -EBUSY)
    140			dev_err(&r->unit->device,
    141				"isochronous resources exhausted\n");
    142		else
    143			dev_err(&r->unit->device,
    144				"isochronous resource allocation failed\n");
    145	}
    146
    147	mutex_unlock(&r->mutex);
    148
    149	return channel;
    150}
    151EXPORT_SYMBOL(fw_iso_resources_allocate);
    152
    153/**
    154 * fw_iso_resources_update - update resource allocations after a bus reset
    155 * @r: the resource manager
    156 *
    157 * This function must be called from the driver's .update handler to reallocate
    158 * any resources that were allocated before the bus reset.  It is safe to call
    159 * this function if no resources are currently allocated.
    160 *
    161 * Returns a negative error code on failure.  If this happens, the caller must
    162 * stop streaming.
    163 */
    164int fw_iso_resources_update(struct fw_iso_resources *r)
    165{
    166	struct fw_card *card = fw_parent_device(r->unit)->card;
    167	int bandwidth, channel;
    168
    169	mutex_lock(&r->mutex);
    170
    171	if (!r->allocated) {
    172		mutex_unlock(&r->mutex);
    173		return 0;
    174	}
    175
    176	spin_lock_irq(&card->lock);
    177	r->generation = card->generation;
    178	r->bandwidth_overhead = current_bandwidth_overhead(card);
    179	spin_unlock_irq(&card->lock);
    180
    181	bandwidth = r->bandwidth + r->bandwidth_overhead;
    182
    183	fw_iso_resource_manage(card, r->generation, 1uLL << r->channel,
    184			       &channel, &bandwidth, true);
    185	/*
    186	 * When another bus reset happens, pretend that the allocation
    187	 * succeeded; we will try again for the new generation later.
    188	 */
    189	if (channel < 0 && channel != -EAGAIN) {
    190		r->allocated = false;
    191		if (channel == -EBUSY)
    192			dev_err(&r->unit->device,
    193				"isochronous resources exhausted\n");
    194		else
    195			dev_err(&r->unit->device,
    196				"isochronous resource allocation failed\n");
    197	}
    198
    199	mutex_unlock(&r->mutex);
    200
    201	return channel;
    202}
    203EXPORT_SYMBOL(fw_iso_resources_update);
    204
    205/**
    206 * fw_iso_resources_free - frees allocated resources
    207 * @r: the resource manager
    208 *
    209 * This function deallocates the channel and bandwidth, if allocated.
    210 */
    211void fw_iso_resources_free(struct fw_iso_resources *r)
    212{
    213	struct fw_card *card;
    214	int bandwidth, channel;
    215
    216	/* Not initialized. */
    217	if (r->unit == NULL)
    218		return;
    219	card = fw_parent_device(r->unit)->card;
    220
    221	mutex_lock(&r->mutex);
    222
    223	if (r->allocated) {
    224		bandwidth = r->bandwidth + r->bandwidth_overhead;
    225		fw_iso_resource_manage(card, r->generation, 1uLL << r->channel,
    226				       &channel, &bandwidth, false);
    227		if (channel < 0)
    228			dev_err(&r->unit->device,
    229				"isochronous resource deallocation failed\n");
    230
    231		r->allocated = false;
    232	}
    233
    234	mutex_unlock(&r->mutex);
    235}
    236EXPORT_SYMBOL(fw_iso_resources_free);