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 allocation functions

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

- msi_domain_alloc_irqs_range():

Handles a caller defined precise range

- msi_domain_alloc_irqs_all():

Allocates all interrupts associated to a domain by scanning the
allocated MSI descriptors

The latter is useful for the existing PCI/MSI support which does not have
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.396497163@linutronix.de

+147 -48
+10 -8
include/linux/msi.h
··· 383 383 * @get_hwirq, @msi_init and @msi_free are callbacks used by the underlying 384 384 * irqdomain. 385 385 * 386 - * @msi_check, @msi_prepare and @set_desc are callbacks used by 387 - * msi_domain_alloc/free_irqs(). 386 + * @msi_check, @msi_prepare and @set_desc are callbacks used by the 387 + * msi_domain_alloc/free_irqs*() variants. 388 388 * 389 389 * @domain_alloc_irqs, @domain_free_irqs can be used to override the 390 390 * default allocation/free functions (__msi_domain_alloc/free_irqs). This ··· 392 392 * be wrapped into the regular irq domains concepts by mere mortals. This 393 393 * allows to universally use msi_domain_alloc/free_irqs without having to 394 394 * special case XEN all over the place. 395 - * 396 - * Contrary to other operations @domain_alloc_irqs and @domain_free_irqs 397 - * are set to the default implementation if NULL and even when 398 - * MSI_FLAG_USE_DEF_DOM_OPS is not set to avoid breaking existing users and 399 - * because these callbacks are obviously mandatory. 400 395 */ 401 396 struct msi_domain_ops { 402 397 irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info, ··· 491 496 int nvec); 492 497 int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, 493 498 int nvec); 499 + 494 500 void msi_domain_free_irqs_descs_locked(struct irq_domain *domain, struct device *dev); 495 501 void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev); 502 + 503 + int msi_domain_alloc_irqs_range_locked(struct device *dev, unsigned int domid, 504 + unsigned int first, unsigned int last); 505 + int msi_domain_alloc_irqs_range(struct device *dev, unsigned int domid, 506 + unsigned int first, unsigned int last); 507 + int msi_domain_alloc_irqs_all_locked(struct device *dev, unsigned int domid, int nirqs); 508 + 496 509 497 510 void msi_domain_free_irqs_range_locked(struct device *dev, unsigned int domid, 498 511 unsigned int first, unsigned int last); 499 512 void msi_domain_free_irqs_range(struct device *dev, unsigned int domid, 500 513 unsigned int first, unsigned int last); 501 - 502 514 void msi_domain_free_irqs_all_locked(struct device *dev, unsigned int domid); 503 515 void msi_domain_free_irqs_all(struct device *dev, unsigned int domid); 504 516
+137 -40
kernel/irq/msi.c
··· 24 24 * @domid: ID of the domain on which management operations should be done 25 25 * @first: First (hardware) slot index to operate on 26 26 * @last: Last (hardware) slot index to operate on 27 + * @nirqs: The number of Linux interrupts to allocate. Can be larger 28 + * than the range due to PCI/multi-MSI. 27 29 */ 28 30 struct msi_ctrl { 29 31 unsigned int domid; 30 32 unsigned int first; 31 33 unsigned int last; 34 + unsigned int nirqs; 32 35 }; 33 36 34 37 /* Invalid Xarray index which is outside of any searchable range */ ··· 39 36 /* The maximum domain size */ 40 37 #define MSI_XA_DOMAIN_SIZE (MSI_MAX_INDEX + 1) 41 38 39 + static void msi_domain_free_locked(struct device *dev, struct msi_ctrl *ctrl); 42 40 static inline int msi_sysfs_create_group(struct device *dev); 43 41 44 42 ··· 548 544 static inline void msi_sysfs_remove_desc(struct device *dev, struct msi_desc *desc) { } 549 545 #endif /* !CONFIG_SYSFS */ 550 546 551 - static int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, int nvec); 552 - 553 547 static struct irq_domain *msi_get_device_domain(struct device *dev, unsigned int domid) 554 548 { 555 549 struct irq_domain *domain; ··· 725 723 .msi_init = msi_domain_ops_init, 726 724 .msi_prepare = msi_domain_ops_prepare, 727 725 .set_desc = msi_domain_ops_set_desc, 728 - .domain_alloc_irqs = __msi_domain_alloc_irqs, 729 726 }; 730 727 731 728 static void msi_domain_update_dom_ops(struct msi_domain_info *info) ··· 735 734 info->ops = &msi_domain_ops_default; 736 735 return; 737 736 } 738 - 739 - if (ops->domain_alloc_irqs == NULL) 740 - ops->domain_alloc_irqs = msi_domain_ops_default.domain_alloc_irqs; 741 737 742 738 if (!(info->flags & MSI_FLAG_USE_DEF_DOM_OPS)) 743 739 return; ··· 949 951 return 0; 950 952 } 951 953 952 - static int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, 953 - int nvec) 954 + static int __msi_domain_alloc_irqs(struct device *dev, struct irq_domain *domain, 955 + struct msi_ctrl *ctrl) 954 956 { 957 + struct xarray *xa = &dev->msi.data->__domains[ctrl->domid].store; 955 958 struct msi_domain_info *info = domain->host_data; 956 959 struct msi_domain_ops *ops = info->ops; 960 + unsigned int vflags = 0, allocated = 0; 957 961 msi_alloc_info_t arg = { }; 958 - unsigned int vflags = 0; 959 962 struct msi_desc *desc; 960 - int allocated = 0; 963 + unsigned long idx; 961 964 int i, ret, virq; 962 965 963 - ret = msi_domain_prepare_irqs(domain, dev, nvec, &arg); 966 + ret = msi_domain_prepare_irqs(domain, dev, ctrl->nirqs, &arg); 964 967 if (ret) 965 968 return ret; 966 969 ··· 987 988 vflags |= VIRQ_NOMASK_QUIRK; 988 989 } 989 990 990 - msi_for_each_desc(desc, dev, MSI_DESC_NOTASSOCIATED) { 991 + xa_for_each_range(xa, idx, desc, ctrl->first, ctrl->last) { 992 + if (!msi_desc_match(desc, MSI_DESC_NOTASSOCIATED)) 993 + continue; 994 + 995 + /* This should return -ECONFUSED... */ 996 + if (WARN_ON_ONCE(allocated >= ctrl->nirqs)) 997 + return -EINVAL; 998 + 991 999 ops->set_desc(&arg, desc); 992 1000 993 1001 virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used, ··· 1022 1016 1023 1017 static int msi_domain_alloc_simple_msi_descs(struct device *dev, 1024 1018 struct msi_domain_info *info, 1025 - unsigned int num_descs) 1019 + struct msi_ctrl *ctrl) 1026 1020 { 1027 - struct msi_ctrl ctrl = { 1028 - .domid = MSI_DEFAULT_DOMAIN, 1029 - .last = num_descs - 1, 1030 - }; 1031 - 1032 1021 if (!(info->flags & MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS)) 1033 1022 return 0; 1034 1023 1035 - return msi_domain_add_simple_msi_descs(dev, &ctrl); 1024 + return msi_domain_add_simple_msi_descs(dev, ctrl); 1025 + } 1026 + 1027 + static int __msi_domain_alloc_locked(struct device *dev, struct msi_ctrl *ctrl) 1028 + { 1029 + struct msi_domain_info *info; 1030 + struct msi_domain_ops *ops; 1031 + struct irq_domain *domain; 1032 + int ret; 1033 + 1034 + if (!msi_ctrl_valid(dev, ctrl)) 1035 + return -EINVAL; 1036 + 1037 + domain = msi_get_device_domain(dev, ctrl->domid); 1038 + if (!domain) 1039 + return -ENODEV; 1040 + 1041 + info = domain->host_data; 1042 + 1043 + ret = msi_domain_alloc_simple_msi_descs(dev, info, ctrl); 1044 + if (ret) 1045 + return ret; 1046 + 1047 + ops = info->ops; 1048 + if (ops->domain_alloc_irqs) 1049 + return ops->domain_alloc_irqs(domain, dev, ctrl->nirqs); 1050 + 1051 + return __msi_domain_alloc_irqs(dev, domain, ctrl); 1052 + } 1053 + 1054 + static int msi_domain_alloc_locked(struct device *dev, struct msi_ctrl *ctrl) 1055 + { 1056 + int ret = __msi_domain_alloc_locked(dev, ctrl); 1057 + 1058 + if (ret) 1059 + msi_domain_free_locked(dev, ctrl); 1060 + return ret; 1061 + } 1062 + 1063 + /** 1064 + * msi_domain_alloc_irqs_range_locked - Allocate interrupts from a MSI interrupt domain 1065 + * @dev: Pointer to device struct of the device for which the interrupts 1066 + * are allocated 1067 + * @domid: Id of the interrupt domain to operate on 1068 + * @first: First index to allocate (inclusive) 1069 + * @last: Last index to allocate (inclusive) 1070 + * 1071 + * Must be invoked from within a msi_lock_descs() / msi_unlock_descs() 1072 + * pair. Use this for MSI irqdomains which implement their own descriptor 1073 + * allocation/free. 1074 + * 1075 + * Return: %0 on success or an error code. 1076 + */ 1077 + int msi_domain_alloc_irqs_range_locked(struct device *dev, unsigned int domid, 1078 + unsigned int first, unsigned int last) 1079 + { 1080 + struct msi_ctrl ctrl = { 1081 + .domid = domid, 1082 + .first = first, 1083 + .last = last, 1084 + .nirqs = last + 1 - first, 1085 + }; 1086 + 1087 + return msi_domain_alloc_locked(dev, &ctrl); 1088 + } 1089 + 1090 + /** 1091 + * msi_domain_alloc_irqs_range - Allocate interrupts from a MSI interrupt domain 1092 + * @dev: Pointer to device struct of the device for which the interrupts 1093 + * are allocated 1094 + * @domid: Id of the interrupt domain to operate on 1095 + * @first: First index to allocate (inclusive) 1096 + * @last: Last index to allocate (inclusive) 1097 + * 1098 + * Return: %0 on success or an error code. 1099 + */ 1100 + int msi_domain_alloc_irqs_range(struct device *dev, unsigned int domid, 1101 + unsigned int first, unsigned int last) 1102 + { 1103 + int ret; 1104 + 1105 + msi_lock_descs(dev); 1106 + ret = msi_domain_alloc_irqs_range_locked(dev, domid, first, last); 1107 + msi_unlock_descs(dev); 1108 + return ret; 1109 + } 1110 + 1111 + /** 1112 + * msi_domain_alloc_irqs_all_locked - Allocate all interrupts from a MSI interrupt domain 1113 + * 1114 + * @dev: Pointer to device struct of the device for which the interrupts 1115 + * are allocated 1116 + * @domid: Id of the interrupt domain to operate on 1117 + * @nirqs: The number of interrupts to allocate 1118 + * 1119 + * This function scans all MSI descriptors of the MSI domain and allocates interrupts 1120 + * for all unassigned ones. That function is to be used for MSI domain usage where 1121 + * the descriptor allocation is handled at the call site, e.g. PCI/MSI[X]. 1122 + * 1123 + * Return: %0 on success or an error code. 1124 + */ 1125 + int msi_domain_alloc_irqs_all_locked(struct device *dev, unsigned int domid, int nirqs) 1126 + { 1127 + struct msi_ctrl ctrl = { 1128 + .domid = domid, 1129 + .first = 0, 1130 + .last = MSI_MAX_INDEX, 1131 + .nirqs = nirqs, 1132 + }; 1133 + 1134 + return msi_domain_alloc_locked(dev, &ctrl); 1036 1135 } 1037 1136 1038 1137 /** ··· 1156 1045 int msi_domain_alloc_irqs_descs_locked(struct irq_domain *domain, struct device *dev, 1157 1046 int nvec) 1158 1047 { 1159 - struct msi_domain_info *info = domain->host_data; 1160 - struct msi_domain_ops *ops = info->ops; 1161 - int ret; 1048 + struct msi_ctrl ctrl = { 1049 + .domid = MSI_DEFAULT_DOMAIN, 1050 + .first = 0, 1051 + .last = MSI_MAX_INDEX, 1052 + .nirqs = nvec, 1053 + }; 1162 1054 1163 - lockdep_assert_held(&dev->msi.data->mutex); 1164 - 1165 - if (WARN_ON_ONCE(irq_domain_is_msi_parent(domain))) { 1166 - ret = -EINVAL; 1167 - goto free; 1168 - } 1169 - 1170 - /* Frees allocated descriptors in case of failure. */ 1171 - ret = msi_domain_alloc_simple_msi_descs(dev, info, nvec); 1172 - if (ret) 1173 - goto free; 1174 - 1175 - ret = ops->domain_alloc_irqs(domain, dev, nvec); 1176 - if (!ret) 1177 - return 0; 1178 - free: 1179 - msi_domain_free_irqs_descs_locked(domain, dev); 1180 - return ret; 1055 + return msi_domain_alloc_locked(dev, &ctrl); 1181 1056 } 1182 1057 1183 1058 /**