sclp.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 854 行 · 第 1/2 页

C
854
字号
	sccb_mask_t	sclp_send_mask;	u32		read_data_function_mask;} __attribute__((packed));static inline void__sclp_notify_state_change(void){	struct list_head *l;	struct sclp_register *t;	sccb_mask_t receive_mask, send_mask;	list_for_each(l, &sclp_reg_list) {		t = list_entry(l, struct sclp_register, list);		receive_mask = t->receive_mask & sclp_receive_mask;		send_mask = t->send_mask & sclp_send_mask;		if (t->sclp_receive_mask != receive_mask ||		    t->sclp_send_mask != send_mask) {			t->sclp_receive_mask = receive_mask;			t->sclp_send_mask = send_mask;			if (t->state_change_fn != NULL)				t->state_change_fn(t);		}	}}static voidsclp_state_change(struct evbuf_header *evbuf){	unsigned long flags;	struct sclp_statechangebuf *scbuf;	spin_lock_irqsave(&sclp_lock, flags);	scbuf = (struct sclp_statechangebuf *) evbuf;	if (scbuf->validity_sclp_receive_mask) {		if (scbuf->mask_length != sizeof(sccb_mask_t))			printk(KERN_WARNING SCLP_CORE_PRINT_HEADER			       "state change event with mask length %i\n",			       scbuf->mask_length);		else			/* set new receive mask */			sclp_receive_mask = scbuf->sclp_receive_mask;	}	if (scbuf->validity_sclp_send_mask) {		if (scbuf->mask_length != sizeof(sccb_mask_t))			printk(KERN_WARNING SCLP_CORE_PRINT_HEADER			       "state change event with mask length %i\n",			       scbuf->mask_length);		else			/* set new send mask */			sclp_send_mask = scbuf->sclp_send_mask;	}	__sclp_notify_state_change();	spin_unlock_irqrestore(&sclp_lock, flags);}static struct sclp_register sclp_state_change_event = {	.receive_mask = EvTyp_StateChange_Mask,	.receiver_fn = sclp_state_change};/* * SCLP quiesce event handler */#ifdef CONFIG_SMPstatic voiddo_load_quiesce_psw(void * __unused){	static atomic_t cpuid = ATOMIC_INIT(-1);	psw_t quiesce_psw;	__u32 status;	int i;	if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid))		signal_processor(smp_processor_id(), sigp_stop);	/* Wait for all other cpus to enter stopped state */	i = 1;	while (i < NR_CPUS) {		if (!cpu_online(i)) {			i++;			continue;		}		switch (signal_processor_ps(&status, 0, i, sigp_sense)) {		case sigp_order_code_accepted:		case sigp_status_stored:			/* Check for stopped and check stop state */			if (status & 0x50)				i++;			break;		case sigp_busy:			break;		case sigp_not_operational:			i++;			break;		}	}	/* Quiesce the last cpu with the special psw */	quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;	quiesce_psw.addr = 0xfff;	__load_psw(quiesce_psw);}static voiddo_machine_quiesce(void){	on_each_cpu(do_load_quiesce_psw, NULL, 0, 0);}#elsestatic voiddo_machine_quiesce(void){	psw_t quiesce_psw;	quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;	quiesce_psw.addr = 0xfff;	__load_psw(quiesce_psw);}#endifextern void ctrl_alt_del(void);static voidsclp_quiesce(struct evbuf_header *evbuf){	/*	 * We got a "shutdown" request.	 * Add a call to an appropriate "shutdown" routine here. This	 * routine should set all PSWs to 'disabled-wait', 'stopped'	 * or 'check-stopped' - except 1 PSW which needs to carry a	 * special bit pattern called 'quiesce PSW'.	 */	_machine_restart = (void *) do_machine_quiesce;	_machine_halt = do_machine_quiesce;	_machine_power_off = do_machine_quiesce;	ctrl_alt_del();}static struct sclp_register sclp_quiesce_event = {	.receive_mask = EvTyp_SigQuiesce_Mask,	.receiver_fn = sclp_quiesce};/* initialisation of SCLP */struct init_sccb {	struct sccb_header header;	u16 _reserved;	u16 mask_length;	sccb_mask_t receive_mask;	sccb_mask_t send_mask;	sccb_mask_t sclp_send_mask;	sccb_mask_t sclp_receive_mask;} __attribute__((packed));static void sclp_init_mask_retry(unsigned long);static intsclp_init_mask(void){	unsigned long flags;	struct init_sccb *sccb;	struct sclp_req *req;	struct list_head *l;	struct sclp_register *t;	int rc;	sccb = (struct init_sccb *) sclp_init_sccb;	/* stick the request structure to the end of the init sccb page */	req = (struct sclp_req *) ((addr_t) sccb + PAGE_SIZE) - 1;	/* SCLP setup concerning receiving and sending Event Buffers */	req->command = SCLP_CMDW_WRITEMASK;	req->status = SCLP_REQ_QUEUED;	req->callback = NULL;	req->sccb = sccb;	/* setup sccb for writemask command */	memset(sccb, 0, sizeof(struct init_sccb));	sccb->header.length = sizeof(struct init_sccb);	sccb->mask_length = sizeof(sccb_mask_t);	/* copy in the sccb mask of the registered event types */	spin_lock_irqsave(&sclp_lock, flags);	if (!test_bit(SCLP_SHUTDOWN, &sclp_status)) {		list_for_each(l, &sclp_reg_list) {			t = list_entry(l, struct sclp_register, list);			sccb->receive_mask |= t->receive_mask;			sccb->send_mask |= t->send_mask;		}	}	sccb->sclp_receive_mask = 0;	sccb->sclp_send_mask = 0;	if (test_bit(SCLP_INIT, &sclp_status)) {		/* add request to sclp queue */		list_add_tail(&req->list, &sclp_req_queue);		spin_unlock_irqrestore(&sclp_lock, flags);		/* and start if SCLP is idle */		sclp_start_request();		/* now wait for completion */		while (req->status != SCLP_REQ_DONE &&		       req->status != SCLP_REQ_FAILED)			sclp_sync_wait();		spin_lock_irqsave(&sclp_lock, flags);	} else {		/*		 * Special case for the very first write mask command.		 * The interrupt handler is not removing request from		 * the request queue and doesn't call callbacks yet		 * because there might be an pending old interrupt		 * after a Re-IPL. We have to receive and ignore it.		 */		do {			rc = __service_call(req->command, req->sccb);			if (rc == 0)				set_bit(SCLP_RUNNING, &sclp_status);			spin_unlock_irqrestore(&sclp_lock, flags);			if (rc == -EIO)				return -ENOSYS;			sclp_sync_wait();			spin_lock_irqsave(&sclp_lock, flags);		} while (rc == -EBUSY);	}	if (sccb->header.response_code != 0x0020) {		/* WRITEMASK failed - we cannot rely on receiving a state		   change event, so initially, polling is the only alternative		   for us to ever become operational. */		if (!test_bit(SCLP_SHUTDOWN, &sclp_status) &&		    (!timer_pending(&retry_timer) ||		     !mod_timer(&retry_timer,			       jiffies + SCLP_INIT_POLL_INTERVAL*HZ))) {			retry_timer.function = sclp_init_mask_retry;			retry_timer.data = 0;			retry_timer.expires = jiffies +				SCLP_INIT_POLL_INTERVAL*HZ;			add_timer(&retry_timer);		}	} else {		sclp_receive_mask = sccb->sclp_receive_mask;		sclp_send_mask = sccb->sclp_send_mask;		__sclp_notify_state_change();	}	spin_unlock_irqrestore(&sclp_lock, flags);	return 0;}static voidsclp_init_mask_retry(unsigned long data) {	sclp_init_mask();}/* Reboot event handler - reset send and receive mask to prevent pending SCLP * events from interfering with rebooted system. */static intsclp_reboot_event(struct notifier_block *this, unsigned long event, void *ptr){	unsigned long flags;	/* Note: need spinlock to maintain atomicity when accessing global         * variables. */	spin_lock_irqsave(&sclp_lock, flags);	set_bit(SCLP_SHUTDOWN, &sclp_status);	spin_unlock_irqrestore(&sclp_lock, flags);	sclp_init_mask();	return NOTIFY_DONE;}static struct notifier_block sclp_reboot_notifier = {	.notifier_call = sclp_reboot_event};/* * sclp setup function. Called early (no kmalloc!) from sclp_console_init(). */static intsclp_init(void){	int rc;	if (test_bit(SCLP_INIT, &sclp_status))		/* Already initialized. */		return 0;	spin_lock_init(&sclp_lock);	INIT_LIST_HEAD(&sclp_req_queue);	/* init event list */	INIT_LIST_HEAD(&sclp_reg_list);	list_add(&sclp_state_change_event.list, &sclp_reg_list);	list_add(&sclp_quiesce_event.list, &sclp_reg_list);	rc = register_reboot_notifier(&sclp_reboot_notifier);	if (rc)		return rc;	/*	 * request the 0x2401 external interrupt	 * The sclp driver is initialized early (before kmalloc works). We	 * need to use register_early_external_interrupt.	 */	if (register_early_external_interrupt(0x2401, sclp_interrupt_handler,					      &ext_int_info_hwc) != 0)		return -EBUSY;	/* enable service-signal external interruptions,	 * Control Register 0 bit 22 := 1	 * (besides PSW bit 7 must be set to 1 sometimes for external	 * interruptions)	 */	ctl_set_bit(0, 9);	init_timer(&retry_timer);	init_timer(&sclp_busy_timer);	/* do the initial write event mask */	rc = sclp_init_mask();	if (rc == 0) {		/* Ok, now everything is setup right. */		set_bit(SCLP_INIT, &sclp_status);		return 0;	}	/* The sclp_init_mask failed. SCLP is broken, unregister and exit. */	ctl_clear_bit(0,9);	unregister_early_external_interrupt(0x2401, sclp_interrupt_handler,					    &ext_int_info_hwc);	return rc;}/* * Register the SCLP event listener identified by REG. Return 0 on success. * Some error codes and their meaning: * *  -ENODEV = SCLP interface is not supported on this machine *   -EBUSY = there is already a listener registered for the requested *            event type *     -EIO = SCLP interface is currently not operational */intsclp_register(struct sclp_register *reg){	unsigned long flags;	struct list_head *l;	struct sclp_register *t;	if (!MACHINE_HAS_SCLP)		return -ENODEV;	if (!test_bit(SCLP_INIT, &sclp_status))		sclp_init();	spin_lock_irqsave(&sclp_lock, flags);	/* check already registered event masks for collisions */	list_for_each(l, &sclp_reg_list) {		t = list_entry(l, struct sclp_register, list);		if (t->receive_mask & reg->receive_mask ||		    t->send_mask & reg->send_mask) {			spin_unlock_irqrestore(&sclp_lock, flags);			return -EBUSY;		}	}	/*	 * set present mask to 0 to trigger state change	 * callback in sclp_init_mask	 */	reg->sclp_receive_mask = 0;	reg->sclp_send_mask = 0;	list_add(&reg->list, &sclp_reg_list);	spin_unlock_irqrestore(&sclp_lock, flags);	sclp_init_mask();	return 0;}/* * Unregister the SCLP event listener identified by REG. */voidsclp_unregister(struct sclp_register *reg){	unsigned long flags;	spin_lock_irqsave(&sclp_lock, flags);	list_del(&reg->list);	spin_unlock_irqrestore(&sclp_lock, flags);	sclp_init_mask();}#define	SCLP_EVBUF_PROCESSED	0x80/* * Traverse array of event buffers contained in SCCB and remove all buffers * with a set "processed" flag. Return the number of unprocessed buffers. */intsclp_remove_processed(struct sccb_header *sccb){	struct evbuf_header *evbuf;	int unprocessed;	u16 remaining;	evbuf = (struct evbuf_header *) (sccb + 1);	unprocessed = 0;	remaining = sccb->length - sizeof(struct sccb_header);	while (remaining > 0) {		remaining -= evbuf->length;		if (evbuf->flags & SCLP_EVBUF_PROCESSED) {			sccb->length -= evbuf->length;			memcpy((void *) evbuf,			       (void *) ((addr_t) evbuf + evbuf->length),			       remaining);		} else {			unprocessed++;			evbuf = (struct evbuf_header *)					((addr_t) evbuf + evbuf->length);		}	}	return unprocessed;}module_init(sclp_init);EXPORT_SYMBOL(sclp_add_request);EXPORT_SYMBOL(sclp_sync_wait);EXPORT_SYMBOL(sclp_register);EXPORT_SYMBOL(sclp_unregister);EXPORT_SYMBOL(sclp_error_message);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?