mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-29 06:55:45 -04:00
The upcoming cpu idle time accounting rework involves comparing and subtracting cross cpu time stamps. Time stamps created with the stckf instruction monotonic with respect to the local cpu. For cross cpu monotonic time stamps the slightly slower stcke instruction has to be used [1]. Convert the idle time accounting relevant usages of stckf to stcke. [1] Principles of Operation - Setting and Inspecting the Clock Signed-off-by: Heiko Carstens <hca@linux.ibm.com> Acked-by: Frederic Weisbecker <frederic@kernel.org> Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
226 lines
5.1 KiB
C
226 lines
5.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright IBM Corp. 2000, 2009
|
|
* 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>
|
|
#include <linux/slab.h>
|
|
#include <linux/kernel_stat.h>
|
|
#include <linux/atomic.h>
|
|
#include <linux/rculist.h>
|
|
|
|
#include <asm/debug.h>
|
|
#include <asm/qdio.h>
|
|
#include <asm/airq.h>
|
|
#include <asm/isc.h>
|
|
#include <asm/tpi.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)
|
|
#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 */
|
|
};
|
|
|
|
/* list of thin interrupt input queues */
|
|
static LIST_HEAD(tiq_list);
|
|
static DEFINE_MUTEX(tiq_list_lock);
|
|
|
|
static struct indicator_t *q_indicators;
|
|
|
|
u64 last_ai_time;
|
|
|
|
/* 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_cmpxchg(&q_indicators[i].count, 0, 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)
|
|
{
|
|
struct indicator_t *ind = container_of(addr, struct indicator_t, ind);
|
|
|
|
if (!addr)
|
|
return;
|
|
atomic_dec(&ind->count);
|
|
}
|
|
|
|
static inline int references_shared_dsci(struct qdio_irq *irq_ptr)
|
|
{
|
|
return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
|
|
}
|
|
|
|
int test_nonshared_ind(struct qdio_irq *irq_ptr)
|
|
{
|
|
if (!is_thinint_irq(irq_ptr))
|
|
return 0;
|
|
if (references_shared_dsci(irq_ptr))
|
|
return 0;
|
|
if (*irq_ptr->dsci)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static inline u32 clear_shared_ind(void)
|
|
{
|
|
if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count))
|
|
return 0;
|
|
return xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
|
|
}
|
|
|
|
/**
|
|
* tiqdio_thinint_handler - thin interrupt handler for qdio
|
|
* @airq: pointer to adapter interrupt descriptor
|
|
* @tpi_info: interrupt information (e.g. floating vs directed -- unused)
|
|
*/
|
|
static void tiqdio_thinint_handler(struct airq_struct *airq,
|
|
struct tpi_info *tpi_info)
|
|
{
|
|
u64 irq_time = get_lowcore()->int_clock.tod;
|
|
u32 si_used = clear_shared_ind();
|
|
struct qdio_irq *irq;
|
|
|
|
last_ai_time = irq_time;
|
|
inc_irq_stat(IRQIO_QAI);
|
|
|
|
/* protect tiq_list entries, only changed in activate or shutdown */
|
|
rcu_read_lock();
|
|
|
|
list_for_each_entry_rcu(irq, &tiq_list, entry) {
|
|
/* only process queues from changed sets */
|
|
if (unlikely(references_shared_dsci(irq))) {
|
|
if (!si_used)
|
|
continue;
|
|
} else {
|
|
if (!*irq->dsci)
|
|
continue;
|
|
|
|
xchg(irq->dsci, 0);
|
|
}
|
|
|
|
qdio_deliver_irq(irq);
|
|
irq->last_data_irq_time = irq_time;
|
|
|
|
QDIO_PERF_STAT_INC(irq, adapter_int);
|
|
}
|
|
rcu_read_unlock();
|
|
}
|
|
|
|
static struct airq_struct tiqdio_airq = {
|
|
.handler = tiqdio_thinint_handler,
|
|
.isc = QDIO_AIRQ_ISC,
|
|
};
|
|
|
|
static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
|
|
{
|
|
struct chsc_scssc_area *scssc = (void *)irq_ptr->chsc_page;
|
|
dma64_t summary_indicator_addr, subchannel_indicator_addr;
|
|
int rc;
|
|
|
|
if (reset) {
|
|
summary_indicator_addr = 0;
|
|
subchannel_indicator_addr = 0;
|
|
} else {
|
|
summary_indicator_addr = virt_to_dma64(tiqdio_airq.lsi_ptr);
|
|
subchannel_indicator_addr = virt_to_dma64(irq_ptr->dsci);
|
|
}
|
|
|
|
rc = chsc_sadc(irq_ptr->schid, scssc, summary_indicator_addr,
|
|
subchannel_indicator_addr, tiqdio_airq.isc);
|
|
if (rc) {
|
|
DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no,
|
|
scssc->response.code);
|
|
goto out;
|
|
}
|
|
|
|
DBF_EVENT("setscind");
|
|
DBF_HEX(&summary_indicator_addr, sizeof(summary_indicator_addr));
|
|
DBF_HEX(&subchannel_indicator_addr, sizeof(subchannel_indicator_addr));
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
int qdio_establish_thinint(struct qdio_irq *irq_ptr)
|
|
{
|
|
int rc;
|
|
|
|
if (!is_thinint_irq(irq_ptr))
|
|
return 0;
|
|
|
|
irq_ptr->dsci = get_indicator();
|
|
DBF_HEX(&irq_ptr->dsci, sizeof(void *));
|
|
|
|
rc = set_subchannel_ind(irq_ptr, 0);
|
|
if (rc) {
|
|
put_indicator(irq_ptr->dsci);
|
|
return rc;
|
|
}
|
|
|
|
mutex_lock(&tiq_list_lock);
|
|
list_add_rcu(&irq_ptr->entry, &tiq_list);
|
|
mutex_unlock(&tiq_list_lock);
|
|
return 0;
|
|
}
|
|
|
|
void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
|
|
{
|
|
if (!is_thinint_irq(irq_ptr))
|
|
return;
|
|
|
|
mutex_lock(&tiq_list_lock);
|
|
list_del_rcu(&irq_ptr->entry);
|
|
mutex_unlock(&tiq_list_lock);
|
|
synchronize_rcu();
|
|
|
|
/* reset adapter interrupt indicators */
|
|
set_subchannel_ind(irq_ptr, 1);
|
|
put_indicator(irq_ptr->dsci);
|
|
}
|
|
|
|
int __init qdio_thinint_init(void)
|
|
{
|
|
int rc;
|
|
|
|
q_indicators = kzalloc_objs(struct indicator_t, TIQDIO_NR_INDICATORS);
|
|
if (!q_indicators)
|
|
return -ENOMEM;
|
|
|
|
rc = register_adapter_interrupt(&tiqdio_airq);
|
|
if (rc) {
|
|
DBF_EVENT("RTI:%x", rc);
|
|
kfree(q_indicators);
|
|
return rc;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void __exit qdio_thinint_exit(void)
|
|
{
|
|
WARN_ON(!list_empty(&tiq_list));
|
|
unregister_adapter_interrupt(&tiqdio_airq);
|
|
kfree(q_indicators);
|
|
}
|