Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

genirq/msi: Provide new domain id based interfaces for freeing interrupts

Provide two sorts of interfaces to handle the different use cases:

- msi_domain_free_irqs_range():

Handles a caller defined precise range

- msi_domain_free_irqs_all():

Frees all interrupts associated to a domain

The latter is useful for device teardown and to handle the legacy MSI support
which does not have any range information available.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Acked-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20221124230314.337844751@linutronix.de

+129 -22
+9
include/linux/msi.h
··· 498 498 int nvec); 499 499 void msi_domain_free_irqs_descs_locked(struct irq_domain *domain, struct device *dev); 500 500 void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev); 501 + 502 + void msi_domain_free_irqs_range_locked(struct device *dev, unsigned int domid, 503 + unsigned int first, unsigned int last); 504 + void msi_domain_free_irqs_range(struct device *dev, unsigned int domid, 505 + unsigned int first, unsigned int last); 506 + 507 + void msi_domain_free_irqs_all_locked(struct device *dev, unsigned int domid); 508 + void msi_domain_free_irqs_all(struct device *dev, unsigned int domid); 509 + 501 510 struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain); 502 511 503 512 struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
+120 -22
kernel/irq/msi.c
··· 545 545 #endif /* !CONFIG_SYSFS */ 546 546 547 547 static int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, int nvec); 548 - static void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev); 548 + 549 + static struct irq_domain *msi_get_device_domain(struct device *dev, unsigned int domid) 550 + { 551 + struct irq_domain *domain; 552 + 553 + lockdep_assert_held(&dev->msi.data->mutex); 554 + 555 + if (WARN_ON_ONCE(domid >= MSI_MAX_DEVICE_IRQDOMAINS)) 556 + return NULL; 557 + 558 + domain = dev->msi.data->__domains[domid].domain; 559 + if (!domain) 560 + return NULL; 561 + 562 + if (WARN_ON_ONCE(irq_domain_is_msi_parent(domain))) 563 + return NULL; 564 + 565 + return domain; 566 + } 549 567 550 568 static inline void irq_chip_write_msi_msg(struct irq_data *data, 551 569 struct msi_msg *msg) ··· 724 706 .msi_prepare = msi_domain_ops_prepare, 725 707 .set_desc = msi_domain_ops_set_desc, 726 708 .domain_alloc_irqs = __msi_domain_alloc_irqs, 727 - .domain_free_irqs = __msi_domain_free_irqs, 728 709 }; 729 710 730 711 static void msi_domain_update_dom_ops(struct msi_domain_info *info) ··· 737 720 738 721 if (ops->domain_alloc_irqs == NULL) 739 722 ops->domain_alloc_irqs = msi_domain_ops_default.domain_alloc_irqs; 740 - if (ops->domain_free_irqs == NULL) 741 - ops->domain_free_irqs = msi_domain_ops_default.domain_free_irqs; 742 723 743 724 if (!(info->flags & MSI_FLAG_USE_DEF_DOM_OPS)) 744 725 return; ··· 1088 1073 return ret; 1089 1074 } 1090 1075 1091 - static void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev) 1076 + static void __msi_domain_free_irqs(struct device *dev, struct irq_domain *domain, 1077 + struct msi_ctrl *ctrl) 1092 1078 { 1079 + struct xarray *xa = &dev->msi.data->__domains[ctrl->domid].store; 1093 1080 struct msi_domain_info *info = domain->host_data; 1094 1081 struct irq_data *irqd; 1095 1082 struct msi_desc *desc; 1083 + unsigned long idx; 1096 1084 int i; 1097 1085 1098 - /* Only handle MSI entries which have an interrupt associated */ 1099 - msi_for_each_desc(desc, dev, MSI_DESC_ASSOCIATED) { 1086 + xa_for_each_range(xa, idx, desc, ctrl->first, ctrl->last) { 1087 + /* Only handle MSI entries which have an interrupt associated */ 1088 + if (!msi_desc_match(desc, MSI_DESC_ASSOCIATED)) 1089 + continue; 1090 + 1100 1091 /* Make sure all interrupts are deactivated */ 1101 1092 for (i = 0; i < desc->nvec_used; i++) { 1102 1093 irqd = irq_domain_get_irq_data(domain, desc->irq + i); ··· 1117 1096 } 1118 1097 } 1119 1098 1120 - static void msi_domain_free_msi_descs(struct msi_domain_info *info, 1121 - struct device *dev) 1099 + static void msi_domain_free_locked(struct device *dev, struct msi_ctrl *ctrl) 1122 1100 { 1101 + struct msi_domain_info *info; 1102 + struct msi_domain_ops *ops; 1103 + struct irq_domain *domain; 1104 + 1105 + if (!msi_ctrl_valid(dev, ctrl)) 1106 + return; 1107 + 1108 + domain = msi_get_device_domain(dev, ctrl->domid); 1109 + if (!domain) 1110 + return; 1111 + 1112 + info = domain->host_data; 1113 + ops = info->ops; 1114 + 1115 + if (ops->domain_free_irqs) 1116 + ops->domain_free_irqs(domain, dev); 1117 + else 1118 + __msi_domain_free_irqs(dev, domain, ctrl); 1119 + 1120 + if (ops->msi_post_free) 1121 + ops->msi_post_free(domain, dev); 1122 + 1123 1123 if (info->flags & MSI_FLAG_FREE_MSI_DESCS) 1124 - msi_free_msi_descs(dev); 1124 + msi_domain_free_descs(dev, ctrl); 1125 + } 1126 + 1127 + /** 1128 + * msi_domain_free_irqs_range_locked - Free a range of interrupts from a MSI interrupt domain 1129 + * associated to @dev with msi_lock held 1130 + * @dev: Pointer to device struct of the device for which the interrupts 1131 + * are freed 1132 + * @domid: Id of the interrupt domain to operate on 1133 + * @first: First index to free (inclusive) 1134 + * @last: Last index to free (inclusive) 1135 + */ 1136 + void msi_domain_free_irqs_range_locked(struct device *dev, unsigned int domid, 1137 + unsigned int first, unsigned int last) 1138 + { 1139 + struct msi_ctrl ctrl = { 1140 + .domid = domid, 1141 + .first = first, 1142 + .last = last, 1143 + }; 1144 + msi_domain_free_locked(dev, &ctrl); 1145 + } 1146 + 1147 + /** 1148 + * msi_domain_free_irqs_range - Free a range of interrupts from a MSI interrupt domain 1149 + * associated to @dev 1150 + * @dev: Pointer to device struct of the device for which the interrupts 1151 + * are freed 1152 + * @domid: Id of the interrupt domain to operate on 1153 + * @first: First index to free (inclusive) 1154 + * @last: Last index to free (inclusive) 1155 + */ 1156 + void msi_domain_free_irqs_range(struct device *dev, unsigned int domid, 1157 + unsigned int first, unsigned int last) 1158 + { 1159 + msi_lock_descs(dev); 1160 + msi_domain_free_irqs_range_locked(dev, domid, first, last); 1161 + msi_unlock_descs(dev); 1162 + } 1163 + 1164 + /** 1165 + * msi_domain_free_irqs_all_locked - Free all interrupts from a MSI interrupt domain 1166 + * associated to a device 1167 + * @dev: Pointer to device struct of the device for which the interrupts 1168 + * are freed 1169 + * @domid: The id of the domain to operate on 1170 + * 1171 + * Must be invoked from within a msi_lock_descs() / msi_unlock_descs() 1172 + * pair. Use this for MSI irqdomains which implement their own vector 1173 + * allocation. 1174 + */ 1175 + void msi_domain_free_irqs_all_locked(struct device *dev, unsigned int domid) 1176 + { 1177 + msi_domain_free_irqs_range_locked(dev, domid, 0, MSI_MAX_INDEX); 1178 + } 1179 + 1180 + /** 1181 + * msi_domain_free_irqs_all - Free all interrupts from a MSI interrupt domain 1182 + * associated to a device 1183 + * @dev: Pointer to device struct of the device for which the interrupts 1184 + * are freed 1185 + * @domid: The id of the domain to operate on 1186 + */ 1187 + void msi_domain_free_irqs_all(struct device *dev, unsigned int domid) 1188 + { 1189 + msi_lock_descs(dev); 1190 + msi_domain_free_irqs_all_locked(dev, domid); 1191 + msi_unlock_descs(dev); 1125 1192 } 1126 1193 1127 1194 /** ··· 1224 1115 */ 1225 1116 void msi_domain_free_irqs_descs_locked(struct irq_domain *domain, struct device *dev) 1226 1117 { 1227 - struct msi_domain_info *info = domain->host_data; 1228 - struct msi_domain_ops *ops = info->ops; 1229 - 1230 - lockdep_assert_held(&dev->msi.data->mutex); 1231 - 1232 - if (WARN_ON_ONCE(irq_domain_is_msi_parent(domain))) 1233 - return; 1234 - 1235 - ops->domain_free_irqs(domain, dev); 1236 - if (ops->msi_post_free) 1237 - ops->msi_post_free(domain, dev); 1238 - msi_domain_free_msi_descs(info, dev); 1118 + msi_domain_free_irqs_range_locked(dev, MSI_DEFAULT_DOMAIN, 0, MSI_MAX_INDEX); 1239 1119 } 1240 1120 1241 1121 /**