qdio.c
来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 2,574 行 · 第 1/5 页
C
2,574 行
struct qdio_irq *irq_ptr; char dbf_text[15]; QDIO_DBF_TEXT2(0, trace, "qtoh"); sprintf(dbf_text, "%s", cdev->dev.bus_id); QDIO_DBF_TEXT2(0, trace, dbf_text); irq_ptr = cdev->private->qdio_data; sprintf(dbf_text, "state:%d", irq_ptr->state); QDIO_DBF_TEXT2(0, trace, dbf_text); switch (irq_ptr->state) { case QDIO_IRQ_STATE_INACTIVE: QDIO_PRINT_ERR("establish queues on irq 0.%x.%04x: timed out\n", irq_ptr->schid.ssid, irq_ptr->schid.sch_no); QDIO_DBF_TEXT2(1,setup,"eq:timeo"); qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); break; case QDIO_IRQ_STATE_CLEANUP: QDIO_PRINT_INFO("Did not get interrupt on cleanup, " "irq=0.%x.%x.\n", irq_ptr->schid.ssid, irq_ptr->schid.sch_no); qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); break; case QDIO_IRQ_STATE_ESTABLISHED: case QDIO_IRQ_STATE_ACTIVE: /* I/O has been terminated by common I/O layer. */ QDIO_PRINT_INFO("Queues on irq 0.%x.%04x killed by cio.\n", irq_ptr->schid.ssid, irq_ptr->schid.sch_no); QDIO_DBF_TEXT2(1, trace, "cio:term"); qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED); if (get_device(&cdev->dev)) { /* Can't call shutdown from interrupt context. */ PREPARE_WORK(&cdev->private->kick_work, qdio_call_shutdown, (void *)cdev); queue_work(ccw_device_work, &cdev->private->kick_work); } break; default: BUG(); } ccw_device_set_timeout(cdev, 0); wake_up(&cdev->private->wait_q);}static voidqdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb){ struct qdio_irq *irq_ptr; int cstat,dstat; char dbf_text[15];#ifdef CONFIG_QDIO_DEBUG QDIO_DBF_TEXT4(0, trace, "qint"); sprintf(dbf_text, "%s", cdev->dev.bus_id); QDIO_DBF_TEXT4(0, trace, dbf_text);#endif /* CONFIG_QDIO_DEBUG */ if (!intparm) { QDIO_PRINT_ERR("got unsolicited interrupt in qdio " \ "handler, device %s\n", cdev->dev.bus_id); return; } irq_ptr = cdev->private->qdio_data; if (!irq_ptr) { QDIO_DBF_TEXT2(1, trace, "uint"); sprintf(dbf_text,"%s", cdev->dev.bus_id); QDIO_DBF_TEXT2(1,trace,dbf_text); QDIO_PRINT_ERR("received interrupt on unused device %s!\n", cdev->dev.bus_id); return; } if (IS_ERR(irb)) { /* Currently running i/o is in error. */ switch (PTR_ERR(irb)) { case -EIO: QDIO_PRINT_ERR("i/o error on device %s\n", cdev->dev.bus_id); return; case -ETIMEDOUT: qdio_timeout_handler(cdev); return; default: QDIO_PRINT_ERR("unknown error state %ld on device %s\n", PTR_ERR(irb), cdev->dev.bus_id); return; } } qdio_irq_check_sense(irq_ptr->schid, irb);#ifdef CONFIG_QDIO_DEBUG sprintf(dbf_text, "state:%d", irq_ptr->state); QDIO_DBF_TEXT4(0, trace, dbf_text);#endif /* CONFIG_QDIO_DEBUG */ cstat = irb->scsw.cstat; dstat = irb->scsw.dstat; switch (irq_ptr->state) { case QDIO_IRQ_STATE_INACTIVE: qdio_establish_handle_irq(cdev, cstat, dstat); break; case QDIO_IRQ_STATE_CLEANUP: qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); break; case QDIO_IRQ_STATE_ESTABLISHED: case QDIO_IRQ_STATE_ACTIVE: if (cstat & SCHN_STAT_PCI) { qdio_handle_pci(irq_ptr); break; } if ((cstat&~SCHN_STAT_PCI)||dstat) { qdio_handle_activate_check(cdev, intparm, cstat, dstat); break; } default: QDIO_PRINT_ERR("got interrupt for queues in state %d on " \ "device %s?!\n", irq_ptr->state, cdev->dev.bus_id); } wake_up(&cdev->private->wait_q);}intqdio_synchronize(struct ccw_device *cdev, unsigned int flags, unsigned int queue_number){ int cc = 0; struct qdio_q *q; struct qdio_irq *irq_ptr; void *ptr;#ifdef CONFIG_QDIO_DEBUG char dbf_text[15]="SyncXXXX";#endif irq_ptr = cdev->private->qdio_data; if (!irq_ptr) return -ENODEV;#ifdef CONFIG_QDIO_DEBUG *((int*)(&dbf_text[4])) = irq_ptr->schid.sch_no; QDIO_DBF_HEX4(0,trace,dbf_text,QDIO_DBF_TRACE_LEN); *((int*)(&dbf_text[0]))=flags; *((int*)(&dbf_text[4]))=queue_number; QDIO_DBF_HEX4(0,trace,dbf_text,QDIO_DBF_TRACE_LEN);#endif /* CONFIG_QDIO_DEBUG */ if (flags&QDIO_FLAG_SYNC_INPUT) { q=irq_ptr->input_qs[queue_number]; if (!q) return -EINVAL; if (!(irq_ptr->is_qebsm)) cc = do_siga_sync(q->schid, 0, q->mask); } else if (flags&QDIO_FLAG_SYNC_OUTPUT) { q=irq_ptr->output_qs[queue_number]; if (!q) return -EINVAL; if (!(irq_ptr->is_qebsm)) cc = do_siga_sync(q->schid, q->mask, 0); } else return -EINVAL; ptr=&cc; if (cc) QDIO_DBF_HEX3(0,trace,&ptr,sizeof(int)); return cc;}static inline voidqdio_check_subchannel_qebsm(struct qdio_irq *irq_ptr, unsigned char qdioac, unsigned long token){ struct qdio_q *q; int i; unsigned int count, start_buf; char dbf_text[15]; /*check if QEBSM is disabled */ if (!(irq_ptr->is_qebsm) || !(qdioac & 0x01)) { irq_ptr->is_qebsm = 0; irq_ptr->sch_token = 0; irq_ptr->qib.rflags &= ~QIB_RFLAGS_ENABLE_QEBSM; QDIO_DBF_TEXT0(0,setup,"noV=V"); return; } irq_ptr->sch_token = token; /*input queue*/ for (i = 0; i < irq_ptr->no_input_qs;i++) { q = irq_ptr->input_qs[i]; count = QDIO_MAX_BUFFERS_PER_Q; start_buf = 0; set_slsb(q, &start_buf, SLSB_P_INPUT_NOT_INIT, &count); } sprintf(dbf_text,"V=V:%2x",irq_ptr->is_qebsm); QDIO_DBF_TEXT0(0,setup,dbf_text); sprintf(dbf_text,"%8lx",irq_ptr->sch_token); QDIO_DBF_TEXT0(0,setup,dbf_text); /*output queue*/ for (i = 0; i < irq_ptr->no_output_qs; i++) { q = irq_ptr->output_qs[i]; count = QDIO_MAX_BUFFERS_PER_Q; start_buf = 0; set_slsb(q, &start_buf, SLSB_P_OUTPUT_NOT_INIT, &count); }}static voidqdio_get_ssqd_information(struct qdio_irq *irq_ptr){ int result; unsigned char qdioac; struct { struct chsc_header request; u16 reserved1:10; u16 ssid:2; u16 fmt:4; u16 first_sch; u16 reserved2; u16 last_sch; u32 reserved3; struct chsc_header response; u32 reserved4; u8 flags; u8 reserved5; u16 sch; u8 qfmt; u8 parm; u8 qdioac1; u8 sch_class; u8 reserved7; u8 icnt; u8 reserved8; u8 ocnt; u8 reserved9; u8 mbccnt; u16 qdioac2; u64 sch_token; } *ssqd_area; QDIO_DBF_TEXT0(0,setup,"getssqd"); qdioac = 0; ssqd_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC); if (!ssqd_area) { QDIO_PRINT_WARN("Could not get memory for chsc. Using all " \ "SIGAs for sch x%x.\n", irq_ptr->schid.sch_no); irq_ptr->qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY || CHSC_FLAG_SIGA_OUTPUT_NECESSARY || CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */ irq_ptr->is_qebsm = 0; irq_ptr->sch_token = 0; irq_ptr->qib.rflags &= ~QIB_RFLAGS_ENABLE_QEBSM; return; } ssqd_area->request = (struct chsc_header) { .length = 0x0010, .code = 0x0024, }; ssqd_area->first_sch = irq_ptr->schid.sch_no; ssqd_area->last_sch = irq_ptr->schid.sch_no; ssqd_area->ssid = irq_ptr->schid.ssid; result = chsc(ssqd_area); if (result) { QDIO_PRINT_WARN("CHSC returned cc %i. Using all " \ "SIGAs for sch 0.%x.%x.\n", result, irq_ptr->schid.ssid, irq_ptr->schid.sch_no); qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY || CHSC_FLAG_SIGA_OUTPUT_NECESSARY || CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */ irq_ptr->is_qebsm = 0; goto out; } if (ssqd_area->response.code != QDIO_CHSC_RESPONSE_CODE_OK) { QDIO_PRINT_WARN("response upon checking SIGA needs " \ "is 0x%x. Using all SIGAs for sch 0.%x.%x.\n", ssqd_area->response.code, irq_ptr->schid.ssid, irq_ptr->schid.sch_no); qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY || CHSC_FLAG_SIGA_OUTPUT_NECESSARY || CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */ irq_ptr->is_qebsm = 0; goto out; } if (!(ssqd_area->flags & CHSC_FLAG_QDIO_CAPABILITY) || !(ssqd_area->flags & CHSC_FLAG_VALIDITY) || (ssqd_area->sch != irq_ptr->schid.sch_no)) { QDIO_PRINT_WARN("huh? problems checking out sch 0.%x.%x... " \ "using all SIGAs.\n", irq_ptr->schid.ssid, irq_ptr->schid.sch_no); qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY | CHSC_FLAG_SIGA_OUTPUT_NECESSARY | CHSC_FLAG_SIGA_SYNC_NECESSARY; /* worst case */ irq_ptr->is_qebsm = 0; goto out; } qdioac = ssqd_area->qdioac1;out: qdio_check_subchannel_qebsm(irq_ptr, qdioac, ssqd_area->sch_token); mempool_free(ssqd_area, qdio_mempool_scssc); irq_ptr->qdioac = qdioac;}static unsigned inttiqdio_check_chsc_availability(void){ char dbf_text[15]; if (!css_characteristics_avail) return -EIO; /* Check for bit 41. */ if (!css_general_characteristics.aif) { QDIO_PRINT_WARN("Adapter interruption facility not " \ "installed.\n"); return -ENOENT; } /* Check for bits 107 and 108. */ if (!css_chsc_characteristics.scssc || !css_chsc_characteristics.scsscf) { QDIO_PRINT_WARN("Set Chan Subsys. Char. & Fast-CHSCs " \ "not available.\n"); return -ENOENT; } /* Check for OSA/FCP thin interrupts (bit 67). */ hydra_thinints = css_general_characteristics.aif_osa; sprintf(dbf_text,"hydrati%1x", hydra_thinints); QDIO_DBF_TEXT0(0,setup,dbf_text);#ifdef CONFIG_64BIT /* Check for QEBSM support in general (bit 58). */ is_passthrough = css_general_characteristics.qebsm;#endif sprintf(dbf_text,"cssQBS:%1x", is_passthrough); QDIO_DBF_TEXT0(0,setup,dbf_text); /* Check for aif time delay disablement fac (bit 56). If installed, * omit svs even under lpar (good point by rick again) */ omit_svs = css_general_characteristics.aif_tdd; sprintf(dbf_text,"omitsvs%1x", omit_svs); QDIO_DBF_TEXT0(0,setup,dbf_text); return 0;}static unsigned inttiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero){ unsigned long real_addr_local_summary_bit; unsigned long real_addr_dev_st_chg_ind; void *ptr; char dbf_text[15]; unsigned int resp_code; int result; struct { struct chsc_header request; u16 operation_code; u16 reserved1; u32 reserved2; u32 reserved3; u64 summary_indicator_addr; u64 subchannel_indicator_addr; u32 ks:4; u32 kc:4; u32 reserved4:21; u32 isc:3; u32 word_with_d_bit; /* set to 0x10000000 to enable * time delay disablement facility */ u32 reserved5; struct subchannel_id schid; u32 reserved6[1004]; struct chsc_header response; u32 reserved7; } *scssc_area; if (!irq_ptr->is_thinint_irq) return -ENODEV; if (reset_to_zero) { real_addr_local_summary_bit=0; real_addr_dev_st_chg_ind=0; } else { real_addr_local_summary_bit= virt_to_phys((volatile void *)indicators); real_addr_dev_st_chg_ind= virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind); } scssc_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC); if (!scssc_area) { QDIO_PRINT_WARN("No memory for setting indicators on " \ "subchannel 0.%x.%x.\n", irq_ptr->schid.ssid, irq_ptr->schid.sch_no); return -ENOMEM; } scssc_area->request = (struct chsc_header) { .length = 0x0fe0, .code = 0x0021, }; scssc_area->operation_code = 0; scssc_area->summary_indicator_addr = real_addr_local_summary_bit; scssc_area->subchannel_indicator_addr = real_addr_dev_st_chg_ind; scssc_area->ks = QDIO_STORAGE_KEY; scssc_area->kc = QDIO_STORAGE_KEY; scssc_area->isc = TIQDIO_THININT_ISC; scssc_area->schid = irq_ptr->schid; /* enables the time delay disablement facility. Don't care * whether it is really there (i.e. we haven't checked for * it) */ if (css_general_characteristics.aif_tdd) scssc_area->word_with_d_bit = 0x10000000; else QDIO_PRINT_WARN("Time delay disablement facility " \ "not available\n"); result = chsc(scssc_area); if (result) { QDIO_PRINT_WARN("could not set indicators on irq 0.%x.%x, " \ "cc=%i.\n", irq_ptr->schid.ssid, irq_ptr->schid.sch_no,result); result = -EIO; goto out; } resp_code = scssc_area->response.code; if (resp_code!=QDIO_CHSC_RESPONSE_CODE_OK) { QDIO_PRINT_WARN("response upon setting indicators " \ "is 0x%x.\n",resp_code); sprintf(dbf_text,"sidR%4x",resp_code); QDIO_DBF_TEXT1(0,trace,dbf_text); QDIO_DBF_TEXT1(0,setup,dbf_text); ptr=&scssc_area->response; QDIO_DBF_HEX2(1,setup,&ptr,QDIO_DBF_SETUP_LEN); result = -EIO; goto out; } QDIO_DBF_TEXT2(0,setup,"setscind"); QDIO_DBF_HEX2(0,setup,&real_addr_local_summary_bit, sizeof(unsigned long)); QDIO_DBF_HEX2(0,setup,&real_addr_dev_st_chg_ind,sizeof(unsigned long)); result = 0;out: mempool_free(scssc_area, qdio_mempool_scssc); return result;}static unsigned inttiqdio_set_delay_target(struct qdio_irq *irq_ptr, unsigned long delay_target){ unsigned int resp_code; int result; void *ptr; char dbf_text[15]; struct { struct chsc_header request; u16 operation_code; u16 reserved1; u32 reserved2; u32 reserved3; u32 reserved4[2]; u32 delay_target; u32 reserved5[1009]; struct chsc_header response; u32 reserved6; } *scsscf_area; if (!irq_ptr->is_thinint_irq) return -ENODEV; scsscf_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC); if (!scsscf_area) { QDIO_PRINT_WARN("No memory for setting delay target on " \ "subchannel 0.%x.%x.\n", irq_ptr->schid.ssid, irq_ptr->schid.sch_no); return -ENOMEM; } scsscf_area->request = (struct chsc_header) { .length = 0x0fe0, .code = 0x1027, }; scsscf_area->delay_target = delay_target<<16; result=chsc(scsscf_area); if (result) { QDIO_PRINT_WARN("could not set delay target on irq 0.%x.%x, " \ "cc=%i. Continuing.\n", irq_ptr->schid.ssid, irq_ptr->schid.sch_no, result); result = -EIO; goto out; } resp_code = scsscf_area->response.code; if (resp_code!=QDIO_CHSC_RESPONSE_C
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?