qdio_thinint.c 7.4 KB
Newer Older
J
Jan Glauber 已提交
1
/*
2
 * Copyright IBM Corp. 2000, 2009
J
Jan Glauber 已提交
3 4 5 6 7
 * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
 *	      Cornelia Huck <cornelia.huck@de.ibm.com>
 *	      Jan Glauber <jang@linux.vnet.ibm.com>
 */
#include <linux/io.h>
8
#include <linux/slab.h>
9
#include <linux/kernel_stat.h>
A
Arun Sharma 已提交
10
#include <linux/atomic.h>
J
Jan Glauber 已提交
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
#include <asm/debug.h>
#include <asm/qdio.h>
#include <asm/airq.h>
#include <asm/isc.h>

#include "cio.h"
#include "ioasm.h"
#include "qdio.h"
#include "qdio_debug.h"

/*
 * Restriction: only 63 iqdio subchannels would have its own indicator,
 * after that, subsequent subchannels share one indicator
 */
#define TIQDIO_NR_NONSHARED_IND		63
#define TIQDIO_NR_INDICATORS		(TIQDIO_NR_NONSHARED_IND + 1)
27 28 29 30 31 32 33
#define TIQDIO_SHARED_IND		63

/* device state change indicators */
struct indicator_t {
	u32 ind;	/* u32 because of compare-and-swap performance */
	atomic_t count; /* use count, 0 or 1 for non-shared indicators */
};
J
Jan Glauber 已提交
34 35 36

/* list of thin interrupt input queues */
static LIST_HEAD(tiq_list);
37
static DEFINE_MUTEX(tiq_list_lock);
J
Jan Glauber 已提交
38 39

/* adapter local summary indicator */
J
Jan Glauber 已提交
40
static u8 *tiqdio_alsi;
J
Jan Glauber 已提交
41

42
static struct indicator_t *q_indicators;
J
Jan Glauber 已提交
43

44
u64 last_ai_time;
J
Jan Glauber 已提交
45

J
Jan Glauber 已提交
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
/* returns addr for the device state change indicator */
static u32 *get_indicator(void)
{
	int i;

	for (i = 0; i < TIQDIO_NR_NONSHARED_IND; i++)
		if (!atomic_read(&q_indicators[i].count)) {
			atomic_set(&q_indicators[i].count, 1);
			return &q_indicators[i].ind;
		}

	/* use the shared indicator */
	atomic_inc(&q_indicators[TIQDIO_SHARED_IND].count);
	return &q_indicators[TIQDIO_SHARED_IND].ind;
}

static void put_indicator(u32 *addr)
{
	int i;

	if (!addr)
		return;
	i = ((unsigned long)addr - (unsigned long)q_indicators) /
		sizeof(struct indicator_t);
	atomic_dec(&q_indicators[i].count);
}

void tiqdio_add_input_queues(struct qdio_irq *irq_ptr)
{
75
	mutex_lock(&tiq_list_lock);
76 77
	BUG_ON(irq_ptr->nr_input_qs < 1);
	list_add_rcu(&irq_ptr->input_qs[0]->entry, &tiq_list);
78
	mutex_unlock(&tiq_list_lock);
79
	xchg(irq_ptr->dsci, 1 << 7);
J
Jan Glauber 已提交
80 81 82 83 84 85
}

void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
{
	struct qdio_q *q;

86 87 88 89 90
	BUG_ON(irq_ptr->nr_input_qs < 1);
	q = irq_ptr->input_qs[0];
	/* if establish triggered an error */
	if (!q || !q->entry.prev || !q->entry.next)
		return;
91

92 93 94 95
	mutex_lock(&tiq_list_lock);
	list_del_rcu(&q->entry);
	mutex_unlock(&tiq_list_lock);
	synchronize_rcu();
J
Jan Glauber 已提交
96 97
}

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
static inline int has_multiple_inq_on_dsci(struct qdio_irq *irq_ptr)
{
	return irq_ptr->nr_input_qs > 1;
}

static inline int references_shared_dsci(struct qdio_irq *irq_ptr)
{
	return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
}

static inline int shared_ind(struct qdio_irq *irq_ptr)
{
	return references_shared_dsci(irq_ptr) ||
		has_multiple_inq_on_dsci(irq_ptr);
}

void clear_nonshared_ind(struct qdio_irq *irq_ptr)
{
	if (!is_thinint_irq(irq_ptr))
		return;
	if (shared_ind(irq_ptr))
		return;
	xchg(irq_ptr->dsci, 0);
}

int test_nonshared_ind(struct qdio_irq *irq_ptr)
{
	if (!is_thinint_irq(irq_ptr))
		return 0;
	if (shared_ind(irq_ptr))
		return 0;
	if (*irq_ptr->dsci)
		return 1;
	else
		return 0;
}

135
static inline u32 clear_shared_ind(void)
J
Jan Glauber 已提交
136
{
137 138 139
	if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count))
		return 0;
	return xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
J
Jan Glauber 已提交
140 141
}

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq)
{
	struct qdio_q *q;
	int i;

	for_each_input_queue(irq, q, i) {
		if (!references_shared_dsci(irq) &&
		    has_multiple_inq_on_dsci(irq))
			xchg(q->irq_ptr->dsci, 0);

		if (q->u.in.queue_start_poll) {
			/* skip if polling is enabled or already in work */
			if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED,
					     &q->u.in.queue_irq_state)) {
				qperf_inc(q, int_discarded);
				continue;
			}

			/* avoid dsci clear here, done after processing */
			q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr,
						 q->irq_ptr->int_parm);
		} else {
164
			if (!shared_ind(q->irq_ptr))
165 166 167 168 169 170 171 172 173 174 175
				xchg(q->irq_ptr->dsci, 0);

			/*
			 * Call inbound processing but not directly
			 * since that could starve other thinint queues.
			 */
			tasklet_schedule(&q->tasklet);
		}
	}
}

176 177
/**
 * tiqdio_thinint_handler - thin interrupt handler for qdio
J
Jan Glauber 已提交
178 179
 * @alsi: pointer to adapter local summary indicator
 * @data: NULL
180
 */
J
Jan Glauber 已提交
181
static void tiqdio_thinint_handler(void *alsi, void *data)
J
Jan Glauber 已提交
182
{
183
	u32 si_used = clear_shared_ind();
J
Jan Glauber 已提交
184 185
	struct qdio_q *q;

J
Jan Glauber 已提交
186
	last_ai_time = S390_lowcore.int_clock;
187
	kstat_cpu(smp_processor_id()).irqs[IOINT_QAI]++;
J
Jan Glauber 已提交
188

J
Jan Glauber 已提交
189 190 191
	/* protect tiq_list entries, only changed in activate or shutdown */
	rcu_read_lock();

192
	/* check for work on all inbound thinint queues */
J
Jan Glauber 已提交
193
	list_for_each_entry_rcu(q, &tiq_list, entry) {
194
		struct qdio_irq *irq;
J
Jan Glauber 已提交
195

J
Jan Glauber 已提交
196
		/* only process queues from changed sets */
197 198
		irq = q->irq_ptr;
		if (unlikely(references_shared_dsci(irq))) {
199 200
			if (!si_used)
				continue;
201
		} else if (!*irq->dsci)
J
Jan Glauber 已提交
202
			continue;
J
Jan Glauber 已提交
203

204
		tiqdio_call_inq_handlers(irq);
J
Jan Glauber 已提交
205 206 207

		qperf_inc(q, adapter_int);
	}
J
Jan Glauber 已提交
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
	rcu_read_unlock();
}

static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
{
	struct scssc_area *scssc_area;
	int rc;

	scssc_area = (struct scssc_area *)irq_ptr->chsc_page;
	memset(scssc_area, 0, PAGE_SIZE);

	if (reset) {
		scssc_area->summary_indicator_addr = 0;
		scssc_area->subchannel_indicator_addr = 0;
	} else {
		scssc_area->summary_indicator_addr = virt_to_phys(tiqdio_alsi);
		scssc_area->subchannel_indicator_addr =
			virt_to_phys(irq_ptr->dsci);
	}

	scssc_area->request = (struct chsc_header) {
		.length = 0x0fe0,
		.code	= 0x0021,
	};
	scssc_area->operation_code = 0;
233 234
	scssc_area->ks = PAGE_DEFAULT_KEY >> 4;
	scssc_area->kc = PAGE_DEFAULT_KEY >> 4;
J
Jan Glauber 已提交
235 236 237 238 239 240 241 242 243 244 245 246 247
	scssc_area->isc = QDIO_AIRQ_ISC;
	scssc_area->schid = irq_ptr->schid;

	/* enable the time delay disablement facility */
	if (css_general_characteristics.aif_tdd)
		scssc_area->word_with_d_bit = 0x10000000;

	rc = chsc(scssc_area);
	if (rc)
		return -EIO;

	rc = chsc_error_from_response(scssc_area->response.code);
	if (rc) {
248 249 250
		DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no,
			  scssc_area->response.code);
		DBF_ERROR_HEX(&scssc_area->response, sizeof(void *));
J
Jan Glauber 已提交
251 252 253
		return rc;
	}

254 255 256
	DBF_EVENT("setscind");
	DBF_HEX(&scssc_area->summary_indicator_addr, sizeof(unsigned long));
	DBF_HEX(&scssc_area->subchannel_indicator_addr,	sizeof(unsigned long));
J
Jan Glauber 已提交
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
	return 0;
}

/* allocate non-shared indicators and shared indicator */
int __init tiqdio_allocate_memory(void)
{
	q_indicators = kzalloc(sizeof(struct indicator_t) * TIQDIO_NR_INDICATORS,
			     GFP_KERNEL);
	if (!q_indicators)
		return -ENOMEM;
	return 0;
}

void tiqdio_free_memory(void)
{
	kfree(q_indicators);
}

int __init tiqdio_register_thinints(void)
{
	isc_register(QDIO_AIRQ_ISC);
	tiqdio_alsi = s390_register_adapter_interrupt(&tiqdio_thinint_handler,
						      NULL, QDIO_AIRQ_ISC);
	if (IS_ERR(tiqdio_alsi)) {
281
		DBF_EVENT("RTI:%lx", PTR_ERR(tiqdio_alsi));
J
Jan Glauber 已提交
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
		tiqdio_alsi = NULL;
		isc_unregister(QDIO_AIRQ_ISC);
		return -ENOMEM;
	}
	return 0;
}

int qdio_establish_thinint(struct qdio_irq *irq_ptr)
{
	if (!is_thinint_irq(irq_ptr))
		return 0;
	return set_subchannel_ind(irq_ptr, 0);
}

void qdio_setup_thinint(struct qdio_irq *irq_ptr)
{
	if (!is_thinint_irq(irq_ptr))
		return;
	irq_ptr->dsci = get_indicator();
301
	DBF_HEX(&irq_ptr->dsci, sizeof(void *));
J
Jan Glauber 已提交
302 303 304 305 306 307 308 309 310
}

void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
{
	if (!is_thinint_irq(irq_ptr))
		return;

	/* reset adapter interrupt indicators */
	set_subchannel_ind(irq_ptr, 1);
311
	put_indicator(irq_ptr->dsci);
J
Jan Glauber 已提交
312 313 314 315
}

void __exit tiqdio_unregister_thinints(void)
{
316
	WARN_ON(!list_empty(&tiq_list));
J
Jan Glauber 已提交
317 318 319 320 321 322

	if (tiqdio_alsi) {
		s390_unregister_adapter_interrupt(tiqdio_alsi, QDIO_AIRQ_ISC);
		isc_unregister(QDIO_AIRQ_ISC);
	}
}