css.c
来自「linux 内核源代码」· C语言 代码 · 共 841 行 · 第 1/2 页
C
841 行
/* * drivers/s390/cio/css.c * driver for channel subsystem * * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation * Author(s): Arnd Bergmann (arndb@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) */#include <linux/module.h>#include <linux/init.h>#include <linux/device.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/list.h>#include <linux/reboot.h>#include "css.h"#include "cio.h"#include "cio_debug.h"#include "ioasm.h"#include "chsc.h"#include "device.h"#include "idset.h"#include "chp.h"int css_init_done = 0;static int need_reprobe = 0;static int max_ssid = 0;struct channel_subsystem *channel_subsystems[__MAX_CSSID + 1];int css_characteristics_avail = 0;intfor_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data){ struct subchannel_id schid; int ret; init_subchannel_id(&schid); ret = -ENODEV; do { do { ret = fn(schid, data); if (ret) break; } while (schid.sch_no++ < __MAX_SUBCHANNEL); schid.sch_no = 0; } while (schid.ssid++ < max_ssid); return ret;}static struct subchannel *css_alloc_subchannel(struct subchannel_id schid){ struct subchannel *sch; int ret; sch = kmalloc (sizeof (*sch), GFP_KERNEL | GFP_DMA); if (sch == NULL) return ERR_PTR(-ENOMEM); ret = cio_validate_subchannel (sch, schid); if (ret < 0) { kfree(sch); return ERR_PTR(ret); } if (sch->st != SUBCHANNEL_TYPE_IO) { /* For now we ignore all non-io subchannels. */ kfree(sch); return ERR_PTR(-EINVAL); } /* * Set intparm to subchannel address. * This is fine even on 64bit since the subchannel is always located * under 2G. */ sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; ret = cio_modify(sch); if (ret) { kfree(sch->lock); kfree(sch); return ERR_PTR(ret); } return sch;}static voidcss_free_subchannel(struct subchannel *sch){ if (sch) { /* Reset intparm to zeroes. */ sch->schib.pmcw.intparm = 0; cio_modify(sch); kfree(sch->lock); kfree(sch); }}static voidcss_subchannel_release(struct device *dev){ struct subchannel *sch; sch = to_subchannel(dev); if (!cio_is_console(sch->schid)) { kfree(sch->lock); kfree(sch); }}static int css_sch_device_register(struct subchannel *sch){ int ret; mutex_lock(&sch->reg_mutex); ret = device_register(&sch->dev); mutex_unlock(&sch->reg_mutex); return ret;}void css_sch_device_unregister(struct subchannel *sch){ mutex_lock(&sch->reg_mutex); device_unregister(&sch->dev); mutex_unlock(&sch->reg_mutex);}static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw){ int i; int mask; memset(ssd, 0, sizeof(struct chsc_ssd_info)); ssd->path_mask = pmcw->pim; for (i = 0; i < 8; i++) { mask = 0x80 >> i; if (pmcw->pim & mask) { chp_id_init(&ssd->chpid[i]); ssd->chpid[i].id = pmcw->chpid[i]; } }}static void ssd_register_chpids(struct chsc_ssd_info *ssd){ int i; int mask; for (i = 0; i < 8; i++) { mask = 0x80 >> i; if (ssd->path_mask & mask) if (!chp_is_registered(ssd->chpid[i])) chp_new(ssd->chpid[i]); }}void css_update_ssd_info(struct subchannel *sch){ int ret; if (cio_is_console(sch->schid)) { /* Console is initialized too early for functions requiring * memory allocation. */ ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw); } else { ret = chsc_get_ssd_info(sch->schid, &sch->ssd_info); if (ret) ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw); ssd_register_chpids(&sch->ssd_info); }}static int css_register_subchannel(struct subchannel *sch){ int ret; /* Initialize the subchannel structure */ sch->dev.parent = &channel_subsystems[0]->device; sch->dev.bus = &css_bus_type; sch->dev.release = &css_subchannel_release; sch->dev.groups = subch_attr_groups; /* * We don't want to generate uevents for I/O subchannels that don't * have a working ccw device behind them since they will be * unregistered before they can be used anyway, so we delay the add * uevent until after device recognition was successful. */ if (!cio_is_console(sch->schid)) /* Console is special, no need to suppress. */ sch->dev.uevent_suppress = 1; css_update_ssd_info(sch); /* make it known to the system */ ret = css_sch_device_register(sch); if (ret) { CIO_MSG_EVENT(0, "Could not register sch 0.%x.%04x: %d\n", sch->schid.ssid, sch->schid.sch_no, ret); return ret; } return ret;}static int css_probe_device(struct subchannel_id schid){ int ret; struct subchannel *sch; sch = css_alloc_subchannel(schid); if (IS_ERR(sch)) return PTR_ERR(sch); ret = css_register_subchannel(sch); if (ret) css_free_subchannel(sch); return ret;}static intcheck_subchannel(struct device * dev, void * data){ struct subchannel *sch; struct subchannel_id *schid = data; sch = to_subchannel(dev); return schid_equal(&sch->schid, schid);}struct subchannel *get_subchannel_by_schid(struct subchannel_id schid){ struct device *dev; dev = bus_find_device(&css_bus_type, NULL, &schid, check_subchannel); return dev ? to_subchannel(dev) : NULL;}static int css_get_subchannel_status(struct subchannel *sch){ struct schib schib; if (stsch(sch->schid, &schib) || !schib.pmcw.dnv) return CIO_GONE; if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev)) return CIO_REVALIDATE; if (!sch->lpm) return CIO_NO_PATH; return CIO_OPER;}static int css_evaluate_known_subchannel(struct subchannel *sch, int slow){ int event, ret, disc; unsigned long flags; enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action; spin_lock_irqsave(sch->lock, flags); disc = device_is_disconnected(sch); if (disc && slow) { /* Disconnected devices are evaluated directly only.*/ spin_unlock_irqrestore(sch->lock, flags); return 0; } /* No interrupt after machine check - kill pending timers. */ device_kill_pending_timer(sch); if (!disc && !slow) { /* Non-disconnected devices are evaluated on the slow path. */ spin_unlock_irqrestore(sch->lock, flags); return -EAGAIN; } event = css_get_subchannel_status(sch); CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n", sch->schid.ssid, sch->schid.sch_no, event, disc ? "disconnected" : "normal", slow ? "slow" : "fast"); /* Analyze subchannel status. */ action = NONE; switch (event) { case CIO_NO_PATH: if (disc) { /* Check if paths have become available. */ action = REPROBE; break; } /* fall through */ case CIO_GONE: /* Prevent unwanted effects when opening lock. */ cio_disable_subchannel(sch); device_set_disconnected(sch); /* Ask driver what to do with device. */ action = UNREGISTER; if (sch->driver && sch->driver->notify) { spin_unlock_irqrestore(sch->lock, flags); ret = sch->driver->notify(&sch->dev, event); spin_lock_irqsave(sch->lock, flags); if (ret) action = NONE; } break; case CIO_REVALIDATE: /* Device will be removed, so no notify necessary. */ if (disc) /* Reprobe because immediate unregister might block. */ action = REPROBE; else action = UNREGISTER_PROBE; break; case CIO_OPER: if (disc) /* Get device operational again. */ action = REPROBE; break; } /* Perform action. */ ret = 0; switch (action) { case UNREGISTER: case UNREGISTER_PROBE: /* Unregister device (will use subchannel lock). */ spin_unlock_irqrestore(sch->lock, flags); css_sch_device_unregister(sch); spin_lock_irqsave(sch->lock, flags); /* Reset intparm to zeroes. */ sch->schib.pmcw.intparm = 0; cio_modify(sch); break; case REPROBE: device_trigger_reprobe(sch); break; default: break; } spin_unlock_irqrestore(sch->lock, flags); /* Probe if necessary. */ if (action == UNREGISTER_PROBE) ret = css_probe_device(sch->schid); return ret;}static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow){ struct schib schib; if (!slow) { /* Will be done on the slow path. */ return -EAGAIN; } if (stsch_err(schid, &schib) || !schib.pmcw.dnv) { /* Unusable - ignore. */ return 0; } CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, unknown, " "slow path.\n", schid.ssid, schid.sch_no, CIO_OPER); return css_probe_device(schid);}static void css_evaluate_subchannel(struct subchannel_id schid, int slow){ struct subchannel *sch; int ret; sch = get_subchannel_by_schid(schid); if (sch) { ret = css_evaluate_known_subchannel(sch, slow); put_device(&sch->dev); } else ret = css_evaluate_new_subchannel(schid, slow); if (ret == -EAGAIN) css_schedule_eval(schid);}static struct idset *slow_subchannel_set;static spinlock_t slow_subchannel_lock;static int __init slow_subchannel_init(void){ spin_lock_init(&slow_subchannel_lock); slow_subchannel_set = idset_sch_new(); if (!slow_subchannel_set) { CIO_MSG_EVENT(0, "could not allocate slow subchannel set\n"); return -ENOMEM; } return 0;}static void css_slow_path_func(struct work_struct *unused){ struct subchannel_id schid; CIO_TRACE_EVENT(4, "slowpath"); spin_lock_irq(&slow_subchannel_lock); init_subchannel_id(&schid); while (idset_sch_get_first(slow_subchannel_set, &schid)) { idset_sch_del(slow_subchannel_set, schid); spin_unlock_irq(&slow_subchannel_lock); css_evaluate_subchannel(schid, 1); spin_lock_irq(&slow_subchannel_lock); } spin_unlock_irq(&slow_subchannel_lock);}static DECLARE_WORK(slow_path_work, css_slow_path_func);struct workqueue_struct *slow_path_wq;void css_schedule_eval(struct subchannel_id schid){ unsigned long flags; spin_lock_irqsave(&slow_subchannel_lock, flags); idset_sch_add(slow_subchannel_set, schid); queue_work(slow_path_wq, &slow_path_work); spin_unlock_irqrestore(&slow_subchannel_lock, flags);}void css_schedule_eval_all(void){
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?