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

qdio_thinint.c (5171B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright IBM Corp. 2000, 2009
      4 * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
      5 *	      Cornelia Huck <cornelia.huck@de.ibm.com>
      6 *	      Jan Glauber <jang@linux.vnet.ibm.com>
      7 */
      8#include <linux/io.h>
      9#include <linux/slab.h>
     10#include <linux/kernel_stat.h>
     11#include <linux/atomic.h>
     12#include <linux/rculist.h>
     13
     14#include <asm/debug.h>
     15#include <asm/qdio.h>
     16#include <asm/airq.h>
     17#include <asm/isc.h>
     18
     19#include "cio.h"
     20#include "ioasm.h"
     21#include "qdio.h"
     22#include "qdio_debug.h"
     23
     24/*
     25 * Restriction: only 63 iqdio subchannels would have its own indicator,
     26 * after that, subsequent subchannels share one indicator
     27 */
     28#define TIQDIO_NR_NONSHARED_IND		63
     29#define TIQDIO_NR_INDICATORS		(TIQDIO_NR_NONSHARED_IND + 1)
     30#define TIQDIO_SHARED_IND		63
     31
     32/* device state change indicators */
     33struct indicator_t {
     34	u32 ind;	/* u32 because of compare-and-swap performance */
     35	atomic_t count; /* use count, 0 or 1 for non-shared indicators */
     36};
     37
     38/* list of thin interrupt input queues */
     39static LIST_HEAD(tiq_list);
     40static DEFINE_MUTEX(tiq_list_lock);
     41
     42static struct indicator_t *q_indicators;
     43
     44u64 last_ai_time;
     45
     46/* returns addr for the device state change indicator */
     47static u32 *get_indicator(void)
     48{
     49	int i;
     50
     51	for (i = 0; i < TIQDIO_NR_NONSHARED_IND; i++)
     52		if (!atomic_cmpxchg(&q_indicators[i].count, 0, 1))
     53			return &q_indicators[i].ind;
     54
     55	/* use the shared indicator */
     56	atomic_inc(&q_indicators[TIQDIO_SHARED_IND].count);
     57	return &q_indicators[TIQDIO_SHARED_IND].ind;
     58}
     59
     60static void put_indicator(u32 *addr)
     61{
     62	struct indicator_t *ind = container_of(addr, struct indicator_t, ind);
     63
     64	if (!addr)
     65		return;
     66	atomic_dec(&ind->count);
     67}
     68
     69static inline int references_shared_dsci(struct qdio_irq *irq_ptr)
     70{
     71	return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
     72}
     73
     74int test_nonshared_ind(struct qdio_irq *irq_ptr)
     75{
     76	if (!is_thinint_irq(irq_ptr))
     77		return 0;
     78	if (references_shared_dsci(irq_ptr))
     79		return 0;
     80	if (*irq_ptr->dsci)
     81		return 1;
     82	else
     83		return 0;
     84}
     85
     86static inline u32 clear_shared_ind(void)
     87{
     88	if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count))
     89		return 0;
     90	return xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
     91}
     92
     93/**
     94 * tiqdio_thinint_handler - thin interrupt handler for qdio
     95 * @airq: pointer to adapter interrupt descriptor
     96 * @floating: flag to recognize floating vs. directed interrupts (unused)
     97 */
     98static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating)
     99{
    100	u64 irq_time = S390_lowcore.int_clock;
    101	u32 si_used = clear_shared_ind();
    102	struct qdio_irq *irq;
    103
    104	last_ai_time = irq_time;
    105	inc_irq_stat(IRQIO_QAI);
    106
    107	/* protect tiq_list entries, only changed in activate or shutdown */
    108	rcu_read_lock();
    109
    110	list_for_each_entry_rcu(irq, &tiq_list, entry) {
    111		/* only process queues from changed sets */
    112		if (unlikely(references_shared_dsci(irq))) {
    113			if (!si_used)
    114				continue;
    115		} else {
    116			if (!*irq->dsci)
    117				continue;
    118
    119			xchg(irq->dsci, 0);
    120		}
    121
    122		qdio_deliver_irq(irq);
    123		irq->last_data_irq_time = irq_time;
    124
    125		QDIO_PERF_STAT_INC(irq, adapter_int);
    126	}
    127	rcu_read_unlock();
    128}
    129
    130static struct airq_struct tiqdio_airq = {
    131	.handler = tiqdio_thinint_handler,
    132	.isc = QDIO_AIRQ_ISC,
    133};
    134
    135static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
    136{
    137	struct chsc_scssc_area *scssc = (void *)irq_ptr->chsc_page;
    138	u64 summary_indicator_addr, subchannel_indicator_addr;
    139	int rc;
    140
    141	if (reset) {
    142		summary_indicator_addr = 0;
    143		subchannel_indicator_addr = 0;
    144	} else {
    145		summary_indicator_addr = virt_to_phys(tiqdio_airq.lsi_ptr);
    146		subchannel_indicator_addr = virt_to_phys(irq_ptr->dsci);
    147	}
    148
    149	rc = chsc_sadc(irq_ptr->schid, scssc, summary_indicator_addr,
    150		       subchannel_indicator_addr, tiqdio_airq.isc);
    151	if (rc) {
    152		DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no,
    153			  scssc->response.code);
    154		goto out;
    155	}
    156
    157	DBF_EVENT("setscind");
    158	DBF_HEX(&summary_indicator_addr, sizeof(summary_indicator_addr));
    159	DBF_HEX(&subchannel_indicator_addr, sizeof(subchannel_indicator_addr));
    160out:
    161	return rc;
    162}
    163
    164int qdio_establish_thinint(struct qdio_irq *irq_ptr)
    165{
    166	int rc;
    167
    168	if (!is_thinint_irq(irq_ptr))
    169		return 0;
    170
    171	irq_ptr->dsci = get_indicator();
    172	DBF_HEX(&irq_ptr->dsci, sizeof(void *));
    173
    174	rc = set_subchannel_ind(irq_ptr, 0);
    175	if (rc) {
    176		put_indicator(irq_ptr->dsci);
    177		return rc;
    178	}
    179
    180	mutex_lock(&tiq_list_lock);
    181	list_add_rcu(&irq_ptr->entry, &tiq_list);
    182	mutex_unlock(&tiq_list_lock);
    183	return 0;
    184}
    185
    186void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
    187{
    188	if (!is_thinint_irq(irq_ptr))
    189		return;
    190
    191	mutex_lock(&tiq_list_lock);
    192	list_del_rcu(&irq_ptr->entry);
    193	mutex_unlock(&tiq_list_lock);
    194	synchronize_rcu();
    195
    196	/* reset adapter interrupt indicators */
    197	set_subchannel_ind(irq_ptr, 1);
    198	put_indicator(irq_ptr->dsci);
    199}
    200
    201int __init qdio_thinint_init(void)
    202{
    203	int rc;
    204
    205	q_indicators = kcalloc(TIQDIO_NR_INDICATORS, sizeof(struct indicator_t),
    206			       GFP_KERNEL);
    207	if (!q_indicators)
    208		return -ENOMEM;
    209
    210	rc = register_adapter_interrupt(&tiqdio_airq);
    211	if (rc) {
    212		DBF_EVENT("RTI:%x", rc);
    213		kfree(q_indicators);
    214		return rc;
    215	}
    216	return 0;
    217}
    218
    219void __exit qdio_thinint_exit(void)
    220{
    221	WARN_ON(!list_empty(&tiq_list));
    222	unregister_adapter_interrupt(&tiqdio_airq);
    223	kfree(q_indicators);
    224}