Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/s390/cio/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 5 kB image not shown  

Quelle  qdio_thinint.c   Sprache: 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;
 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 = kcalloc(TIQDIO_NR_INDICATORS, sizeof(struct indicator_t),
          GFP_KERNEL);
 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);
}

Messung V0.5
C=96 H=92 G=93

¤ Dauer der Verarbeitung: 0.3 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.