1 // SPDX-License-Identifier: LGPL-2.1+ 2 3 #include <linux/cleanup.h> 4 #include <linux/cpu.h> 5 #include <linux/cpumask.h> 6 #include <linux/interrupt.h> 7 #include <linux/irq.h> 8 #include <linux/irqdesc.h> 9 #include <linux/irqdomain.h> 10 #include <linux/nodemask.h> 11 #include <kunit/test.h> 12 13 #include "internals.h" 14 15 static irqreturn_t noop_handler(int irq, void *data) 16 { 17 return IRQ_HANDLED; 18 } 19 20 static void noop(struct irq_data *data) { } 21 static unsigned int noop_ret(struct irq_data *data) { return 0; } 22 23 static int noop_affinity(struct irq_data *data, const struct cpumask *dest, 24 bool force) 25 { 26 irq_data_update_effective_affinity(data, dest); 27 28 return 0; 29 } 30 31 static struct irq_chip fake_irq_chip = { 32 .name = "fake", 33 .irq_startup = noop_ret, 34 .irq_shutdown = noop, 35 .irq_enable = noop, 36 .irq_disable = noop, 37 .irq_ack = noop, 38 .irq_mask = noop, 39 .irq_unmask = noop, 40 .irq_set_affinity = noop_affinity, 41 .flags = IRQCHIP_SKIP_SET_WAKE, 42 }; 43 44 static int irq_test_setup_fake_irq(struct kunit *test, struct irq_affinity_desc *affd) 45 { 46 struct irq_desc *desc; 47 int virq; 48 49 virq = irq_domain_alloc_descs(-1, 1, 0, NUMA_NO_NODE, affd); 50 KUNIT_ASSERT_GE(test, virq, 0); 51 52 irq_set_chip_and_handler(virq, &fake_irq_chip, handle_simple_irq); 53 54 desc = irq_to_desc(virq); 55 KUNIT_ASSERT_PTR_NE(test, desc, NULL); 56 57 /* On some architectures, IRQs are NOREQUEST | NOPROBE by default. */ 58 irq_settings_clr_norequest(desc); 59 60 return virq; 61 } 62 63 static void irq_disable_depth_test(struct kunit *test) 64 { 65 struct irq_desc *desc; 66 int virq, ret; 67 68 virq = irq_test_setup_fake_irq(test, NULL); 69 70 desc = irq_to_desc(virq); 71 KUNIT_ASSERT_PTR_NE(test, desc, NULL); 72 73 ret = request_irq(virq, noop_handler, 0, "test_irq", NULL); 74 KUNIT_ASSERT_EQ(test, ret, 0); 75 76 KUNIT_EXPECT_EQ(test, desc->depth, 0); 77 78 disable_irq(virq); 79 KUNIT_EXPECT_EQ(test, desc->depth, 1); 80 81 enable_irq(virq); 82 KUNIT_EXPECT_EQ(test, desc->depth, 0); 83 84 free_irq(virq, NULL); 85 } 86 87 static void irq_free_disabled_test(struct kunit *test) 88 { 89 struct irq_desc *desc; 90 int virq, ret; 91 92 virq = irq_test_setup_fake_irq(test, NULL); 93 94 desc = irq_to_desc(virq); 95 KUNIT_ASSERT_PTR_NE(test, desc, NULL); 96 97 ret = request_irq(virq, noop_handler, 0, "test_irq", NULL); 98 KUNIT_ASSERT_EQ(test, ret, 0); 99 100 KUNIT_EXPECT_EQ(test, desc->depth, 0); 101 102 disable_irq(virq); 103 KUNIT_EXPECT_EQ(test, desc->depth, 1); 104 105 free_irq(virq, NULL); 106 KUNIT_EXPECT_GE(test, desc->depth, 1); 107 108 ret = request_irq(virq, noop_handler, 0, "test_irq", NULL); 109 KUNIT_ASSERT_EQ(test, ret, 0); 110 KUNIT_EXPECT_EQ(test, desc->depth, 0); 111 112 free_irq(virq, NULL); 113 } 114 115 static void irq_shutdown_depth_test(struct kunit *test) 116 { 117 struct irq_desc *desc; 118 struct irq_data *data; 119 int virq, ret; 120 struct irq_affinity_desc affinity = { 121 .is_managed = 1, 122 .mask = CPU_MASK_ALL, 123 }; 124 125 if (!IS_ENABLED(CONFIG_SMP)) 126 kunit_skip(test, "requires CONFIG_SMP for managed shutdown"); 127 128 virq = irq_test_setup_fake_irq(test, &affinity); 129 130 desc = irq_to_desc(virq); 131 KUNIT_ASSERT_PTR_NE(test, desc, NULL); 132 133 data = irq_desc_get_irq_data(desc); 134 KUNIT_ASSERT_PTR_NE(test, data, NULL); 135 136 ret = request_irq(virq, noop_handler, 0, "test_irq", NULL); 137 KUNIT_ASSERT_EQ(test, ret, 0); 138 139 KUNIT_EXPECT_TRUE(test, irqd_is_activated(data)); 140 KUNIT_EXPECT_TRUE(test, irqd_is_started(data)); 141 KUNIT_EXPECT_TRUE(test, irqd_affinity_is_managed(data)); 142 143 KUNIT_EXPECT_EQ(test, desc->depth, 0); 144 145 disable_irq(virq); 146 KUNIT_EXPECT_EQ(test, desc->depth, 1); 147 148 scoped_guard(raw_spinlock_irqsave, &desc->lock) 149 irq_shutdown_and_deactivate(desc); 150 151 KUNIT_EXPECT_FALSE(test, irqd_is_activated(data)); 152 KUNIT_EXPECT_FALSE(test, irqd_is_started(data)); 153 154 KUNIT_EXPECT_EQ(test, irq_activate(desc), 0); 155 #ifdef CONFIG_SMP 156 irq_startup_managed(desc); 157 #endif 158 159 KUNIT_EXPECT_EQ(test, desc->depth, 1); 160 161 enable_irq(virq); 162 KUNIT_EXPECT_EQ(test, desc->depth, 0); 163 164 free_irq(virq, NULL); 165 } 166 167 static void irq_cpuhotplug_test(struct kunit *test) 168 { 169 struct irq_desc *desc; 170 struct irq_data *data; 171 int virq, ret; 172 struct irq_affinity_desc affinity = { 173 .is_managed = 1, 174 }; 175 176 if (!IS_ENABLED(CONFIG_SMP)) 177 kunit_skip(test, "requires CONFIG_SMP for CPU hotplug"); 178 if (!get_cpu_device(1)) 179 kunit_skip(test, "requires more than 1 CPU for CPU hotplug"); 180 if (!cpu_is_hotpluggable(1)) 181 kunit_skip(test, "CPU 1 must be hotpluggable"); 182 if (!cpu_online(1)) 183 kunit_skip(test, "CPU 1 must be online"); 184 185 cpumask_copy(&affinity.mask, cpumask_of(1)); 186 187 virq = irq_test_setup_fake_irq(test, &affinity); 188 189 desc = irq_to_desc(virq); 190 KUNIT_ASSERT_PTR_NE(test, desc, NULL); 191 192 data = irq_desc_get_irq_data(desc); 193 KUNIT_ASSERT_PTR_NE(test, data, NULL); 194 195 ret = request_irq(virq, noop_handler, 0, "test_irq", NULL); 196 KUNIT_ASSERT_EQ(test, ret, 0); 197 198 KUNIT_EXPECT_TRUE(test, irqd_is_activated(data)); 199 KUNIT_EXPECT_TRUE(test, irqd_is_started(data)); 200 KUNIT_EXPECT_TRUE(test, irqd_affinity_is_managed(data)); 201 202 KUNIT_EXPECT_EQ(test, desc->depth, 0); 203 204 disable_irq(virq); 205 KUNIT_EXPECT_EQ(test, desc->depth, 1); 206 207 KUNIT_EXPECT_EQ(test, remove_cpu(1), 0); 208 KUNIT_EXPECT_GE(test, desc->depth, 1); 209 KUNIT_EXPECT_EQ(test, add_cpu(1), 0); 210 211 KUNIT_EXPECT_EQ(test, desc->depth, 1); 212 213 enable_irq(virq); 214 KUNIT_EXPECT_TRUE(test, irqd_is_activated(data)); 215 KUNIT_EXPECT_TRUE(test, irqd_is_started(data)); 216 KUNIT_EXPECT_EQ(test, desc->depth, 0); 217 218 free_irq(virq, NULL); 219 } 220 221 static struct kunit_case irq_test_cases[] = { 222 KUNIT_CASE(irq_disable_depth_test), 223 KUNIT_CASE(irq_free_disabled_test), 224 KUNIT_CASE(irq_shutdown_depth_test), 225 KUNIT_CASE(irq_cpuhotplug_test), 226 {} 227 }; 228 229 static struct kunit_suite irq_test_suite = { 230 .name = "irq_test_cases", 231 .test_cases = irq_test_cases, 232 }; 233 234 kunit_test_suite(irq_test_suite); 235 MODULE_DESCRIPTION("IRQ unit test suite"); 236 MODULE_LICENSE("GPL"); 237