diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/kernel/apic/io_apic.c | 165 |
1 files changed, 133 insertions, 32 deletions
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index b739d398bb29..2016f9dabd72 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c | |||
@@ -300,9 +300,9 @@ static struct irq_cfg *alloc_irq_and_cfg_at(unsigned int at, int node) | |||
300 | return cfg; | 300 | return cfg; |
301 | } | 301 | } |
302 | 302 | ||
303 | static int alloc_irq_from(unsigned int from, int node) | 303 | static int alloc_irqs_from(unsigned int from, unsigned int count, int node) |
304 | { | 304 | { |
305 | return irq_alloc_desc_from(from, node); | 305 | return irq_alloc_descs_from(from, count, node); |
306 | } | 306 | } |
307 | 307 | ||
308 | static void free_irq_at(unsigned int at, struct irq_cfg *cfg) | 308 | static void free_irq_at(unsigned int at, struct irq_cfg *cfg) |
@@ -2982,37 +2982,58 @@ device_initcall(ioapic_init_ops); | |||
2982 | /* | 2982 | /* |
2983 | * Dynamic irq allocate and deallocation | 2983 | * Dynamic irq allocate and deallocation |
2984 | */ | 2984 | */ |
2985 | unsigned int create_irq_nr(unsigned int from, int node) | 2985 | unsigned int __create_irqs(unsigned int from, unsigned int count, int node) |
2986 | { | 2986 | { |
2987 | struct irq_cfg *cfg; | 2987 | struct irq_cfg **cfg; |
2988 | unsigned long flags; | 2988 | unsigned long flags; |
2989 | unsigned int ret = 0; | 2989 | int irq, i; |
2990 | int irq; | ||
2991 | 2990 | ||
2992 | if (from < nr_irqs_gsi) | 2991 | if (from < nr_irqs_gsi) |
2993 | from = nr_irqs_gsi; | 2992 | from = nr_irqs_gsi; |
2994 | 2993 | ||
2995 | irq = alloc_irq_from(from, node); | 2994 | cfg = kzalloc_node(count * sizeof(cfg[0]), GFP_KERNEL, node); |
2996 | if (irq < 0) | 2995 | if (!cfg) |
2997 | return 0; | ||
2998 | cfg = alloc_irq_cfg(irq, node); | ||
2999 | if (!cfg) { | ||
3000 | free_irq_at(irq, NULL); | ||
3001 | return 0; | 2996 | return 0; |
2997 | |||
2998 | irq = alloc_irqs_from(from, count, node); | ||
2999 | if (irq < 0) | ||
3000 | goto out_cfgs; | ||
3001 | |||
3002 | for (i = 0; i < count; i++) { | ||
3003 | cfg[i] = alloc_irq_cfg(irq + i, node); | ||
3004 | if (!cfg[i]) | ||
3005 | goto out_irqs; | ||
3002 | } | 3006 | } |
3003 | 3007 | ||
3004 | raw_spin_lock_irqsave(&vector_lock, flags); | 3008 | raw_spin_lock_irqsave(&vector_lock, flags); |
3005 | if (!__assign_irq_vector(irq, cfg, apic->target_cpus())) | 3009 | for (i = 0; i < count; i++) |
3006 | ret = irq; | 3010 | if (__assign_irq_vector(irq + i, cfg[i], apic->target_cpus())) |
3011 | goto out_vecs; | ||
3007 | raw_spin_unlock_irqrestore(&vector_lock, flags); | 3012 | raw_spin_unlock_irqrestore(&vector_lock, flags); |
3008 | 3013 | ||
3009 | if (ret) { | 3014 | for (i = 0; i < count; i++) { |
3010 | irq_set_chip_data(irq, cfg); | 3015 | irq_set_chip_data(irq + i, cfg[i]); |
3011 | irq_clear_status_flags(irq, IRQ_NOREQUEST); | 3016 | irq_clear_status_flags(irq + i, IRQ_NOREQUEST); |
3012 | } else { | ||
3013 | free_irq_at(irq, cfg); | ||
3014 | } | 3017 | } |
3015 | return ret; | 3018 | |
3019 | kfree(cfg); | ||
3020 | return irq; | ||
3021 | |||
3022 | out_vecs: | ||
3023 | for (i--; i >= 0; i--) | ||
3024 | __clear_irq_vector(irq + i, cfg[i]); | ||
3025 | raw_spin_unlock_irqrestore(&vector_lock, flags); | ||
3026 | out_irqs: | ||
3027 | for (i = 0; i < count; i++) | ||
3028 | free_irq_at(irq + i, cfg[i]); | ||
3029 | out_cfgs: | ||
3030 | kfree(cfg); | ||
3031 | return 0; | ||
3032 | } | ||
3033 | |||
3034 | unsigned int create_irq_nr(unsigned int from, int node) | ||
3035 | { | ||
3036 | return __create_irqs(from, 1, node); | ||
3016 | } | 3037 | } |
3017 | 3038 | ||
3018 | int create_irq(void) | 3039 | int create_irq(void) |
@@ -3045,6 +3066,14 @@ void destroy_irq(unsigned int irq) | |||
3045 | free_irq_at(irq, cfg); | 3066 | free_irq_at(irq, cfg); |
3046 | } | 3067 | } |
3047 | 3068 | ||
3069 | static inline void destroy_irqs(unsigned int irq, unsigned int count) | ||
3070 | { | ||
3071 | unsigned int i; | ||
3072 | |||
3073 | for (i = 0; i < count; i++) | ||
3074 | destroy_irq(irq + i); | ||
3075 | } | ||
3076 | |||
3048 | /* | 3077 | /* |
3049 | * MSI message composition | 3078 | * MSI message composition |
3050 | */ | 3079 | */ |
@@ -3071,7 +3100,7 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, | |||
3071 | 3100 | ||
3072 | if (irq_remapped(cfg)) { | 3101 | if (irq_remapped(cfg)) { |
3073 | compose_remapped_msi_msg(pdev, irq, dest, msg, hpet_id); | 3102 | compose_remapped_msi_msg(pdev, irq, dest, msg, hpet_id); |
3074 | return err; | 3103 | return 0; |
3075 | } | 3104 | } |
3076 | 3105 | ||
3077 | if (x2apic_enabled()) | 3106 | if (x2apic_enabled()) |
@@ -3098,7 +3127,7 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, | |||
3098 | MSI_DATA_DELIVERY_LOWPRI) | | 3127 | MSI_DATA_DELIVERY_LOWPRI) | |
3099 | MSI_DATA_VECTOR(cfg->vector); | 3128 | MSI_DATA_VECTOR(cfg->vector); |
3100 | 3129 | ||
3101 | return err; | 3130 | return 0; |
3102 | } | 3131 | } |
3103 | 3132 | ||
3104 | static int | 3133 | static int |
@@ -3136,18 +3165,26 @@ static struct irq_chip msi_chip = { | |||
3136 | .irq_retrigger = ioapic_retrigger_irq, | 3165 | .irq_retrigger = ioapic_retrigger_irq, |
3137 | }; | 3166 | }; |
3138 | 3167 | ||
3139 | static int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, int irq) | 3168 | static int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, |
3169 | unsigned int irq_base, unsigned int irq_offset) | ||
3140 | { | 3170 | { |
3141 | struct irq_chip *chip = &msi_chip; | 3171 | struct irq_chip *chip = &msi_chip; |
3142 | struct msi_msg msg; | 3172 | struct msi_msg msg; |
3173 | unsigned int irq = irq_base + irq_offset; | ||
3143 | int ret; | 3174 | int ret; |
3144 | 3175 | ||
3145 | ret = msi_compose_msg(dev, irq, &msg, -1); | 3176 | ret = msi_compose_msg(dev, irq, &msg, -1); |
3146 | if (ret < 0) | 3177 | if (ret < 0) |
3147 | return ret; | 3178 | return ret; |
3148 | 3179 | ||
3149 | irq_set_msi_desc(irq, msidesc); | 3180 | irq_set_msi_desc_off(irq_base, irq_offset, msidesc); |
3150 | write_msi_msg(irq, &msg); | 3181 | |
3182 | /* | ||
3183 | * MSI-X message is written per-IRQ, the offset is always 0. | ||
3184 | * MSI message denotes a contiguous group of IRQs, written for 0th IRQ. | ||
3185 | */ | ||
3186 | if (!irq_offset) | ||
3187 | write_msi_msg(irq, &msg); | ||
3151 | 3188 | ||
3152 | if (irq_remapped(irq_get_chip_data(irq))) { | 3189 | if (irq_remapped(irq_get_chip_data(irq))) { |
3153 | irq_set_status_flags(irq, IRQ_MOVE_PCNTXT); | 3190 | irq_set_status_flags(irq, IRQ_MOVE_PCNTXT); |
@@ -3161,23 +3198,19 @@ static int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, int irq) | |||
3161 | return 0; | 3198 | return 0; |
3162 | } | 3199 | } |
3163 | 3200 | ||
3164 | int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | 3201 | int setup_msix_irqs(struct pci_dev *dev, int nvec) |
3165 | { | 3202 | { |
3166 | int node, ret, sub_handle, index = 0; | 3203 | int node, ret, sub_handle, index = 0; |
3167 | unsigned int irq, irq_want; | 3204 | unsigned int irq, irq_want; |
3168 | struct msi_desc *msidesc; | 3205 | struct msi_desc *msidesc; |
3169 | 3206 | ||
3170 | /* x86 doesn't support multiple MSI yet */ | ||
3171 | if (type == PCI_CAP_ID_MSI && nvec > 1) | ||
3172 | return 1; | ||
3173 | |||
3174 | node = dev_to_node(&dev->dev); | 3207 | node = dev_to_node(&dev->dev); |
3175 | irq_want = nr_irqs_gsi; | 3208 | irq_want = nr_irqs_gsi; |
3176 | sub_handle = 0; | 3209 | sub_handle = 0; |
3177 | list_for_each_entry(msidesc, &dev->msi_list, list) { | 3210 | list_for_each_entry(msidesc, &dev->msi_list, list) { |
3178 | irq = create_irq_nr(irq_want, node); | 3211 | irq = create_irq_nr(irq_want, node); |
3179 | if (irq == 0) | 3212 | if (irq == 0) |
3180 | return -1; | 3213 | return -ENOSPC; |
3181 | irq_want = irq + 1; | 3214 | irq_want = irq + 1; |
3182 | if (!irq_remapping_enabled) | 3215 | if (!irq_remapping_enabled) |
3183 | goto no_ir; | 3216 | goto no_ir; |
@@ -3199,7 +3232,7 @@ int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | |||
3199 | goto error; | 3232 | goto error; |
3200 | } | 3233 | } |
3201 | no_ir: | 3234 | no_ir: |
3202 | ret = setup_msi_irq(dev, msidesc, irq); | 3235 | ret = setup_msi_irq(dev, msidesc, irq, 0); |
3203 | if (ret < 0) | 3236 | if (ret < 0) |
3204 | goto error; | 3237 | goto error; |
3205 | sub_handle++; | 3238 | sub_handle++; |
@@ -3211,6 +3244,74 @@ error: | |||
3211 | return ret; | 3244 | return ret; |
3212 | } | 3245 | } |
3213 | 3246 | ||
3247 | int setup_msi_irqs(struct pci_dev *dev, int nvec) | ||
3248 | { | ||
3249 | int node, ret, sub_handle, index = 0; | ||
3250 | unsigned int irq; | ||
3251 | struct msi_desc *msidesc; | ||
3252 | |||
3253 | if (nvec > 1 && !irq_remapping_enabled) | ||
3254 | return 1; | ||
3255 | |||
3256 | nvec = __roundup_pow_of_two(nvec); | ||
3257 | |||
3258 | WARN_ON(!list_is_singular(&dev->msi_list)); | ||
3259 | msidesc = list_entry(dev->msi_list.next, struct msi_desc, list); | ||
3260 | WARN_ON(msidesc->irq); | ||
3261 | WARN_ON(msidesc->msi_attrib.multiple); | ||
3262 | |||
3263 | node = dev_to_node(&dev->dev); | ||
3264 | irq = __create_irqs(nr_irqs_gsi, nvec, node); | ||
3265 | if (irq == 0) | ||
3266 | return -ENOSPC; | ||
3267 | |||
3268 | if (!irq_remapping_enabled) { | ||
3269 | ret = setup_msi_irq(dev, msidesc, irq, 0); | ||
3270 | if (ret < 0) | ||
3271 | goto error; | ||
3272 | return 0; | ||
3273 | } | ||
3274 | |||
3275 | msidesc->msi_attrib.multiple = ilog2(nvec); | ||
3276 | for (sub_handle = 0; sub_handle < nvec; sub_handle++) { | ||
3277 | if (!sub_handle) { | ||
3278 | index = msi_alloc_remapped_irq(dev, irq, nvec); | ||
3279 | if (index < 0) { | ||
3280 | ret = index; | ||
3281 | goto error; | ||
3282 | } | ||
3283 | } else { | ||
3284 | ret = msi_setup_remapped_irq(dev, irq + sub_handle, | ||
3285 | index, sub_handle); | ||
3286 | if (ret < 0) | ||
3287 | goto error; | ||
3288 | } | ||
3289 | ret = setup_msi_irq(dev, msidesc, irq, sub_handle); | ||
3290 | if (ret < 0) | ||
3291 | goto error; | ||
3292 | } | ||
3293 | return 0; | ||
3294 | |||
3295 | error: | ||
3296 | destroy_irqs(irq, nvec); | ||
3297 | |||
3298 | /* | ||
3299 | * Restore altered MSI descriptor fields and prevent just destroyed | ||
3300 | * IRQs from tearing down again in default_teardown_msi_irqs() | ||
3301 | */ | ||
3302 | msidesc->irq = 0; | ||
3303 | msidesc->msi_attrib.multiple = 0; | ||
3304 | |||
3305 | return ret; | ||
3306 | } | ||
3307 | |||
3308 | int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | ||
3309 | { | ||
3310 | if (type == PCI_CAP_ID_MSI) | ||
3311 | return setup_msi_irqs(dev, nvec); | ||
3312 | return setup_msix_irqs(dev, nvec); | ||
3313 | } | ||
3314 | |||
3214 | void native_teardown_msi_irq(unsigned int irq) | 3315 | void native_teardown_msi_irq(unsigned int irq) |
3215 | { | 3316 | { |
3216 | destroy_irq(irq); | 3317 | destroy_irq(irq); |