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

tsnep_tc.c (10931B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* Copyright (C) 2021 Gerhard Engleder <gerhard@engleder-embedded.com> */
      3
      4#include "tsnep.h"
      5
      6#include <net/pkt_sched.h>
      7
      8/* save one operation at the end for additional operation at list change */
      9#define TSNEP_MAX_GCL_NUM (TSNEP_GCL_COUNT - 1)
     10
     11static int tsnep_validate_gcl(struct tc_taprio_qopt_offload *qopt)
     12{
     13	int i;
     14	u64 cycle_time;
     15
     16	if (!qopt->cycle_time)
     17		return -ERANGE;
     18	if (qopt->num_entries > TSNEP_MAX_GCL_NUM)
     19		return -EINVAL;
     20	cycle_time = 0;
     21	for (i = 0; i < qopt->num_entries; i++) {
     22		if (qopt->entries[i].command != TC_TAPRIO_CMD_SET_GATES)
     23			return -EINVAL;
     24		if (qopt->entries[i].gate_mask & ~TSNEP_GCL_MASK)
     25			return -EINVAL;
     26		if (qopt->entries[i].interval < TSNEP_GCL_MIN_INTERVAL)
     27			return -EINVAL;
     28		cycle_time += qopt->entries[i].interval;
     29	}
     30	if (qopt->cycle_time != cycle_time)
     31		return -EINVAL;
     32	if (qopt->cycle_time_extension >= qopt->cycle_time)
     33		return -EINVAL;
     34
     35	return 0;
     36}
     37
     38static void tsnep_write_gcl_operation(struct tsnep_gcl *gcl, int index,
     39				      u32 properties, u32 interval, bool flush)
     40{
     41	void __iomem *addr = gcl->addr +
     42			     sizeof(struct tsnep_gcl_operation) * index;
     43
     44	gcl->operation[index].properties = properties;
     45	gcl->operation[index].interval = interval;
     46
     47	iowrite32(properties, addr);
     48	iowrite32(interval, addr + sizeof(u32));
     49
     50	if (flush) {
     51		/* flush write with read access */
     52		ioread32(addr);
     53	}
     54}
     55
     56static u64 tsnep_change_duration(struct tsnep_gcl *gcl, int index)
     57{
     58	u64 duration;
     59	int count;
     60
     61	/* change needs to be triggered one or two operations before start of
     62	 * new gate control list
     63	 * - change is triggered at start of operation (minimum one operation)
     64	 * - operation with adjusted interval is inserted on demand to exactly
     65	 *   meet the start of the new gate control list (optional)
     66	 *
     67	 * additionally properties are read directly after start of previous
     68	 * operation
     69	 *
     70	 * therefore, three operations needs to be considered for the limit
     71	 */
     72	duration = 0;
     73	count = 3;
     74	while (count) {
     75		duration += gcl->operation[index].interval;
     76
     77		index--;
     78		if (index < 0)
     79			index = gcl->count - 1;
     80
     81		count--;
     82	}
     83
     84	return duration;
     85}
     86
     87static void tsnep_write_gcl(struct tsnep_gcl *gcl,
     88			    struct tc_taprio_qopt_offload *qopt)
     89{
     90	int i;
     91	u32 properties;
     92	u64 extend;
     93	u64 cut;
     94
     95	gcl->base_time = ktime_to_ns(qopt->base_time);
     96	gcl->cycle_time = qopt->cycle_time;
     97	gcl->cycle_time_extension = qopt->cycle_time_extension;
     98
     99	for (i = 0; i < qopt->num_entries; i++) {
    100		properties = qopt->entries[i].gate_mask;
    101		if (i == (qopt->num_entries - 1))
    102			properties |= TSNEP_GCL_LAST;
    103
    104		tsnep_write_gcl_operation(gcl, i, properties,
    105					  qopt->entries[i].interval, true);
    106	}
    107	gcl->count = qopt->num_entries;
    108
    109	/* calculate change limit; i.e., the time needed between enable and
    110	 * start of new gate control list
    111	 */
    112
    113	/* case 1: extend cycle time for change
    114	 * - change duration of last operation
    115	 * - cycle time extension
    116	 */
    117	extend = tsnep_change_duration(gcl, gcl->count - 1);
    118	extend += gcl->cycle_time_extension;
    119
    120	/* case 2: cut cycle time for change
    121	 * - maximum change duration
    122	 */
    123	cut = 0;
    124	for (i = 0; i < gcl->count; i++)
    125		cut = max(cut, tsnep_change_duration(gcl, i));
    126
    127	/* use maximum, because the actual case (extend or cut) can be
    128	 * determined only after limit is known (chicken-and-egg problem)
    129	 */
    130	gcl->change_limit = max(extend, cut);
    131}
    132
    133static u64 tsnep_gcl_start_after(struct tsnep_gcl *gcl, u64 limit)
    134{
    135	u64 start = gcl->base_time;
    136	u64 n;
    137
    138	if (start <= limit) {
    139		n = div64_u64(limit - start, gcl->cycle_time);
    140		start += (n + 1) * gcl->cycle_time;
    141	}
    142
    143	return start;
    144}
    145
    146static u64 tsnep_gcl_start_before(struct tsnep_gcl *gcl, u64 limit)
    147{
    148	u64 start = gcl->base_time;
    149	u64 n;
    150
    151	n = div64_u64(limit - start, gcl->cycle_time);
    152	start += n * gcl->cycle_time;
    153	if (start == limit)
    154		start -= gcl->cycle_time;
    155
    156	return start;
    157}
    158
    159static u64 tsnep_set_gcl_change(struct tsnep_gcl *gcl, int index, u64 change,
    160				bool insert)
    161{
    162	/* previous operation triggers change and properties are evaluated at
    163	 * start of operation
    164	 */
    165	if (index == 0)
    166		index = gcl->count - 1;
    167	else
    168		index = index - 1;
    169	change -= gcl->operation[index].interval;
    170
    171	/* optionally change to new list with additional operation in between */
    172	if (insert) {
    173		void __iomem *addr = gcl->addr +
    174				     sizeof(struct tsnep_gcl_operation) * index;
    175
    176		gcl->operation[index].properties |= TSNEP_GCL_INSERT;
    177		iowrite32(gcl->operation[index].properties, addr);
    178	}
    179
    180	return change;
    181}
    182
    183static void tsnep_clean_gcl(struct tsnep_gcl *gcl)
    184{
    185	int i;
    186	u32 mask = TSNEP_GCL_LAST | TSNEP_GCL_MASK;
    187	void __iomem *addr;
    188
    189	/* search for insert operation and reset properties */
    190	for (i = 0; i < gcl->count; i++) {
    191		if (gcl->operation[i].properties & ~mask) {
    192			addr = gcl->addr +
    193			       sizeof(struct tsnep_gcl_operation) * i;
    194
    195			gcl->operation[i].properties &= mask;
    196			iowrite32(gcl->operation[i].properties, addr);
    197
    198			break;
    199		}
    200	}
    201}
    202
    203static u64 tsnep_insert_gcl_operation(struct tsnep_gcl *gcl, int ref,
    204				      u64 change, u32 interval)
    205{
    206	u32 properties;
    207
    208	properties = gcl->operation[ref].properties & TSNEP_GCL_MASK;
    209	/* change to new list directly after inserted operation */
    210	properties |= TSNEP_GCL_CHANGE;
    211
    212	/* last operation of list is reserved to insert operation */
    213	tsnep_write_gcl_operation(gcl, TSNEP_GCL_COUNT - 1, properties,
    214				  interval, false);
    215
    216	return tsnep_set_gcl_change(gcl, ref, change, true);
    217}
    218
    219static u64 tsnep_extend_gcl(struct tsnep_gcl *gcl, u64 start, u32 extension)
    220{
    221	int ref = gcl->count - 1;
    222	u32 interval = gcl->operation[ref].interval + extension;
    223
    224	start -= gcl->operation[ref].interval;
    225
    226	return tsnep_insert_gcl_operation(gcl, ref, start, interval);
    227}
    228
    229static u64 tsnep_cut_gcl(struct tsnep_gcl *gcl, u64 start, u64 cycle_time)
    230{
    231	u64 sum = 0;
    232	int i;
    233
    234	/* find operation which shall be cutted */
    235	for (i = 0; i < gcl->count; i++) {
    236		u64 sum_tmp = sum + gcl->operation[i].interval;
    237		u64 interval;
    238
    239		/* sum up operations as long as cycle time is not exceeded */
    240		if (sum_tmp > cycle_time)
    241			break;
    242
    243		/* remaining interval must be big enough for hardware */
    244		interval = cycle_time - sum_tmp;
    245		if (interval > 0 && interval < TSNEP_GCL_MIN_INTERVAL)
    246			break;
    247
    248		sum = sum_tmp;
    249	}
    250	if (sum == cycle_time) {
    251		/* no need to cut operation itself or whole cycle
    252		 * => change exactly at operation
    253		 */
    254		return tsnep_set_gcl_change(gcl, i, start + sum, false);
    255	}
    256	return tsnep_insert_gcl_operation(gcl, i, start + sum,
    257					  cycle_time - sum);
    258}
    259
    260static int tsnep_enable_gcl(struct tsnep_adapter *adapter,
    261			    struct tsnep_gcl *gcl, struct tsnep_gcl *curr)
    262{
    263	u64 system_time;
    264	u64 timeout;
    265	u64 limit;
    266
    267	/* estimate timeout limit after timeout enable, actually timeout limit
    268	 * in hardware will be earlier than estimate so we are on the safe side
    269	 */
    270	tsnep_get_system_time(adapter, &system_time);
    271	timeout = system_time + TSNEP_GC_TIMEOUT;
    272
    273	if (curr)
    274		limit = timeout + curr->change_limit;
    275	else
    276		limit = timeout;
    277
    278	gcl->start_time = tsnep_gcl_start_after(gcl, limit);
    279
    280	/* gate control time register is only 32bit => time shall be in the near
    281	 * future (no driver support for far future implemented)
    282	 */
    283	if ((gcl->start_time - system_time) >= U32_MAX)
    284		return -EAGAIN;
    285
    286	if (curr) {
    287		/* change gate control list */
    288		u64 last;
    289		u64 change;
    290
    291		last = tsnep_gcl_start_before(curr, gcl->start_time);
    292		if ((last + curr->cycle_time) == gcl->start_time)
    293			change = tsnep_cut_gcl(curr, last,
    294					       gcl->start_time - last);
    295		else if (((gcl->start_time - last) <=
    296			  curr->cycle_time_extension) ||
    297			 ((gcl->start_time - last) <= TSNEP_GCL_MIN_INTERVAL))
    298			change = tsnep_extend_gcl(curr, last,
    299						  gcl->start_time - last);
    300		else
    301			change = tsnep_cut_gcl(curr, last,
    302					       gcl->start_time - last);
    303
    304		WARN_ON(change <= timeout);
    305		gcl->change = true;
    306		iowrite32(change & 0xFFFFFFFF, adapter->addr + TSNEP_GC_CHANGE);
    307	} else {
    308		/* start gate control list */
    309		WARN_ON(gcl->start_time <= timeout);
    310		gcl->change = false;
    311		iowrite32(gcl->start_time & 0xFFFFFFFF,
    312			  adapter->addr + TSNEP_GC_TIME);
    313	}
    314
    315	return 0;
    316}
    317
    318static int tsnep_taprio(struct tsnep_adapter *adapter,
    319			struct tc_taprio_qopt_offload *qopt)
    320{
    321	struct tsnep_gcl *gcl;
    322	struct tsnep_gcl *curr;
    323	int retval;
    324
    325	if (!adapter->gate_control)
    326		return -EOPNOTSUPP;
    327
    328	if (!qopt->enable) {
    329		/* disable gate control if active */
    330		mutex_lock(&adapter->gate_control_lock);
    331
    332		if (adapter->gate_control_active) {
    333			iowrite8(TSNEP_GC_DISABLE, adapter->addr + TSNEP_GC);
    334			adapter->gate_control_active = false;
    335		}
    336
    337		mutex_unlock(&adapter->gate_control_lock);
    338
    339		return 0;
    340	}
    341
    342	retval = tsnep_validate_gcl(qopt);
    343	if (retval)
    344		return retval;
    345
    346	mutex_lock(&adapter->gate_control_lock);
    347
    348	gcl = &adapter->gcl[adapter->next_gcl];
    349	tsnep_write_gcl(gcl, qopt);
    350
    351	/* select current gate control list if active */
    352	if (adapter->gate_control_active) {
    353		if (adapter->next_gcl == 0)
    354			curr = &adapter->gcl[1];
    355		else
    356			curr = &adapter->gcl[0];
    357	} else {
    358		curr = NULL;
    359	}
    360
    361	for (;;) {
    362		/* start timeout which discards late enable, this helps ensuring
    363		 * that start/change time are in the future at enable
    364		 */
    365		iowrite8(TSNEP_GC_ENABLE_TIMEOUT, adapter->addr + TSNEP_GC);
    366
    367		retval = tsnep_enable_gcl(adapter, gcl, curr);
    368		if (retval) {
    369			mutex_unlock(&adapter->gate_control_lock);
    370
    371			return retval;
    372		}
    373
    374		/* enable gate control list */
    375		if (adapter->next_gcl == 0)
    376			iowrite8(TSNEP_GC_ENABLE_A, adapter->addr + TSNEP_GC);
    377		else
    378			iowrite8(TSNEP_GC_ENABLE_B, adapter->addr + TSNEP_GC);
    379
    380		/* done if timeout did not happen */
    381		if (!(ioread32(adapter->addr + TSNEP_GC) &
    382		      TSNEP_GC_TIMEOUT_SIGNAL))
    383			break;
    384
    385		/* timeout is acknowledged with any enable */
    386		iowrite8(TSNEP_GC_ENABLE_A, adapter->addr + TSNEP_GC);
    387
    388		if (curr)
    389			tsnep_clean_gcl(curr);
    390
    391		/* retry because of timeout */
    392	}
    393
    394	adapter->gate_control_active = true;
    395
    396	if (adapter->next_gcl == 0)
    397		adapter->next_gcl = 1;
    398	else
    399		adapter->next_gcl = 0;
    400
    401	mutex_unlock(&adapter->gate_control_lock);
    402
    403	return 0;
    404}
    405
    406int tsnep_tc_setup(struct net_device *netdev, enum tc_setup_type type,
    407		   void *type_data)
    408{
    409	struct tsnep_adapter *adapter = netdev_priv(netdev);
    410
    411	switch (type) {
    412	case TC_SETUP_QDISC_TAPRIO:
    413		return tsnep_taprio(adapter, type_data);
    414	default:
    415		return -EOPNOTSUPP;
    416	}
    417}
    418
    419int tsnep_tc_init(struct tsnep_adapter *adapter)
    420{
    421	if (!adapter->gate_control)
    422		return 0;
    423
    424	/* open all gates */
    425	iowrite8(TSNEP_GC_DISABLE, adapter->addr + TSNEP_GC);
    426	iowrite32(TSNEP_GC_OPEN | TSNEP_GC_NEXT_OPEN, adapter->addr + TSNEP_GC);
    427
    428	adapter->gcl[0].addr = adapter->addr + TSNEP_GCL_A;
    429	adapter->gcl[1].addr = adapter->addr + TSNEP_GCL_B;
    430
    431	return 0;
    432}
    433
    434void tsnep_tc_cleanup(struct tsnep_adapter *adapter)
    435{
    436	if (!adapter->gate_control)
    437		return;
    438
    439	if (adapter->gate_control_active) {
    440		iowrite8(TSNEP_GC_DISABLE, adapter->addr + TSNEP_GC);
    441		adapter->gate_control_active = false;
    442	}
    443}