1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Export the iSCSI boot info to userland via sysfs. |
4 | * |
5 | * Copyright (C) 2010 Red Hat, Inc. All rights reserved. |
6 | * Copyright (C) 2010 Mike Christie |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/string.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/sysfs.h> |
13 | #include <linux/capability.h> |
14 | #include <linux/iscsi_boot_sysfs.h> |
15 | |
16 | |
17 | MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>" ); |
18 | MODULE_DESCRIPTION("sysfs interface and helpers to export iSCSI boot information" ); |
19 | MODULE_LICENSE("GPL" ); |
20 | /* |
21 | * The kobject and attribute structures. |
22 | */ |
23 | struct iscsi_boot_attr { |
24 | struct attribute attr; |
25 | int type; |
26 | ssize_t (*show) (void *data, int type, char *buf); |
27 | }; |
28 | |
29 | /* |
30 | * The routine called for all sysfs attributes. |
31 | */ |
32 | static ssize_t iscsi_boot_show_attribute(struct kobject *kobj, |
33 | struct attribute *attr, char *buf) |
34 | { |
35 | struct iscsi_boot_kobj *boot_kobj = |
36 | container_of(kobj, struct iscsi_boot_kobj, kobj); |
37 | struct iscsi_boot_attr *boot_attr = |
38 | container_of(attr, struct iscsi_boot_attr, attr); |
39 | ssize_t ret = -EIO; |
40 | char *str = buf; |
41 | |
42 | if (!capable(CAP_SYS_ADMIN)) |
43 | return -EACCES; |
44 | |
45 | if (boot_kobj->show) |
46 | ret = boot_kobj->show(boot_kobj->data, boot_attr->type, str); |
47 | return ret; |
48 | } |
49 | |
50 | static const struct sysfs_ops iscsi_boot_attr_ops = { |
51 | .show = iscsi_boot_show_attribute, |
52 | }; |
53 | |
54 | static void iscsi_boot_kobj_release(struct kobject *kobj) |
55 | { |
56 | struct iscsi_boot_kobj *boot_kobj = |
57 | container_of(kobj, struct iscsi_boot_kobj, kobj); |
58 | |
59 | if (boot_kobj->release) |
60 | boot_kobj->release(boot_kobj->data); |
61 | kfree(objp: boot_kobj); |
62 | } |
63 | |
64 | static struct kobj_type iscsi_boot_ktype = { |
65 | .release = iscsi_boot_kobj_release, |
66 | .sysfs_ops = &iscsi_boot_attr_ops, |
67 | }; |
68 | |
69 | #define iscsi_boot_rd_attr(fnname, sysfs_name, attr_type) \ |
70 | static struct iscsi_boot_attr iscsi_boot_attr_##fnname = { \ |
71 | .attr = { .name = __stringify(sysfs_name), .mode = 0444 }, \ |
72 | .type = attr_type, \ |
73 | } |
74 | |
75 | /* Target attrs */ |
76 | iscsi_boot_rd_attr(tgt_index, index, ISCSI_BOOT_TGT_INDEX); |
77 | iscsi_boot_rd_attr(tgt_flags, flags, ISCSI_BOOT_TGT_FLAGS); |
78 | iscsi_boot_rd_attr(tgt_ip, ip-addr, ISCSI_BOOT_TGT_IP_ADDR); |
79 | iscsi_boot_rd_attr(tgt_port, port, ISCSI_BOOT_TGT_PORT); |
80 | iscsi_boot_rd_attr(tgt_lun, lun, ISCSI_BOOT_TGT_LUN); |
81 | iscsi_boot_rd_attr(tgt_chap, chap-type, ISCSI_BOOT_TGT_CHAP_TYPE); |
82 | iscsi_boot_rd_attr(tgt_nic, nic-assoc, ISCSI_BOOT_TGT_NIC_ASSOC); |
83 | iscsi_boot_rd_attr(tgt_name, target-name, ISCSI_BOOT_TGT_NAME); |
84 | iscsi_boot_rd_attr(tgt_chap_name, chap-name, ISCSI_BOOT_TGT_CHAP_NAME); |
85 | iscsi_boot_rd_attr(tgt_chap_secret, chap-secret, ISCSI_BOOT_TGT_CHAP_SECRET); |
86 | iscsi_boot_rd_attr(tgt_chap_rev_name, rev-chap-name, |
87 | ISCSI_BOOT_TGT_REV_CHAP_NAME); |
88 | iscsi_boot_rd_attr(tgt_chap_rev_secret, rev-chap-name-secret, |
89 | ISCSI_BOOT_TGT_REV_CHAP_SECRET); |
90 | |
91 | static struct attribute *target_attrs[] = { |
92 | &iscsi_boot_attr_tgt_index.attr, |
93 | &iscsi_boot_attr_tgt_flags.attr, |
94 | &iscsi_boot_attr_tgt_ip.attr, |
95 | &iscsi_boot_attr_tgt_port.attr, |
96 | &iscsi_boot_attr_tgt_lun.attr, |
97 | &iscsi_boot_attr_tgt_chap.attr, |
98 | &iscsi_boot_attr_tgt_nic.attr, |
99 | &iscsi_boot_attr_tgt_name.attr, |
100 | &iscsi_boot_attr_tgt_chap_name.attr, |
101 | &iscsi_boot_attr_tgt_chap_secret.attr, |
102 | &iscsi_boot_attr_tgt_chap_rev_name.attr, |
103 | &iscsi_boot_attr_tgt_chap_rev_secret.attr, |
104 | NULL |
105 | }; |
106 | |
107 | static umode_t iscsi_boot_tgt_attr_is_visible(struct kobject *kobj, |
108 | struct attribute *attr, int i) |
109 | { |
110 | struct iscsi_boot_kobj *boot_kobj = |
111 | container_of(kobj, struct iscsi_boot_kobj, kobj); |
112 | |
113 | if (attr == &iscsi_boot_attr_tgt_index.attr) |
114 | return boot_kobj->is_visible(boot_kobj->data, |
115 | ISCSI_BOOT_TGT_INDEX); |
116 | else if (attr == &iscsi_boot_attr_tgt_flags.attr) |
117 | return boot_kobj->is_visible(boot_kobj->data, |
118 | ISCSI_BOOT_TGT_FLAGS); |
119 | else if (attr == &iscsi_boot_attr_tgt_ip.attr) |
120 | return boot_kobj->is_visible(boot_kobj->data, |
121 | ISCSI_BOOT_TGT_IP_ADDR); |
122 | else if (attr == &iscsi_boot_attr_tgt_port.attr) |
123 | return boot_kobj->is_visible(boot_kobj->data, |
124 | ISCSI_BOOT_TGT_PORT); |
125 | else if (attr == &iscsi_boot_attr_tgt_lun.attr) |
126 | return boot_kobj->is_visible(boot_kobj->data, |
127 | ISCSI_BOOT_TGT_LUN); |
128 | else if (attr == &iscsi_boot_attr_tgt_chap.attr) |
129 | return boot_kobj->is_visible(boot_kobj->data, |
130 | ISCSI_BOOT_TGT_CHAP_TYPE); |
131 | else if (attr == &iscsi_boot_attr_tgt_nic.attr) |
132 | return boot_kobj->is_visible(boot_kobj->data, |
133 | ISCSI_BOOT_TGT_NIC_ASSOC); |
134 | else if (attr == &iscsi_boot_attr_tgt_name.attr) |
135 | return boot_kobj->is_visible(boot_kobj->data, |
136 | ISCSI_BOOT_TGT_NAME); |
137 | else if (attr == &iscsi_boot_attr_tgt_chap_name.attr) |
138 | return boot_kobj->is_visible(boot_kobj->data, |
139 | ISCSI_BOOT_TGT_CHAP_NAME); |
140 | else if (attr == &iscsi_boot_attr_tgt_chap_secret.attr) |
141 | return boot_kobj->is_visible(boot_kobj->data, |
142 | ISCSI_BOOT_TGT_CHAP_SECRET); |
143 | else if (attr == &iscsi_boot_attr_tgt_chap_rev_name.attr) |
144 | return boot_kobj->is_visible(boot_kobj->data, |
145 | ISCSI_BOOT_TGT_REV_CHAP_NAME); |
146 | else if (attr == &iscsi_boot_attr_tgt_chap_rev_secret.attr) |
147 | return boot_kobj->is_visible(boot_kobj->data, |
148 | ISCSI_BOOT_TGT_REV_CHAP_SECRET); |
149 | return 0; |
150 | } |
151 | |
152 | static struct attribute_group iscsi_boot_target_attr_group = { |
153 | .attrs = target_attrs, |
154 | .is_visible = iscsi_boot_tgt_attr_is_visible, |
155 | }; |
156 | |
157 | /* Ethernet attrs */ |
158 | iscsi_boot_rd_attr(eth_index, index, ISCSI_BOOT_ETH_INDEX); |
159 | iscsi_boot_rd_attr(eth_flags, flags, ISCSI_BOOT_ETH_FLAGS); |
160 | iscsi_boot_rd_attr(eth_ip, ip-addr, ISCSI_BOOT_ETH_IP_ADDR); |
161 | iscsi_boot_rd_attr(eth_prefix, prefix-len, ISCSI_BOOT_ETH_PREFIX_LEN); |
162 | iscsi_boot_rd_attr(eth_subnet, subnet-mask, ISCSI_BOOT_ETH_SUBNET_MASK); |
163 | iscsi_boot_rd_attr(eth_origin, origin, ISCSI_BOOT_ETH_ORIGIN); |
164 | iscsi_boot_rd_attr(eth_gateway, gateway, ISCSI_BOOT_ETH_GATEWAY); |
165 | iscsi_boot_rd_attr(eth_primary_dns, primary-dns, ISCSI_BOOT_ETH_PRIMARY_DNS); |
166 | iscsi_boot_rd_attr(eth_secondary_dns, secondary-dns, |
167 | ISCSI_BOOT_ETH_SECONDARY_DNS); |
168 | iscsi_boot_rd_attr(eth_dhcp, dhcp, ISCSI_BOOT_ETH_DHCP); |
169 | iscsi_boot_rd_attr(eth_vlan, vlan, ISCSI_BOOT_ETH_VLAN); |
170 | iscsi_boot_rd_attr(eth_mac, mac, ISCSI_BOOT_ETH_MAC); |
171 | iscsi_boot_rd_attr(eth_hostname, hostname, ISCSI_BOOT_ETH_HOSTNAME); |
172 | |
173 | static struct attribute *ethernet_attrs[] = { |
174 | &iscsi_boot_attr_eth_index.attr, |
175 | &iscsi_boot_attr_eth_flags.attr, |
176 | &iscsi_boot_attr_eth_ip.attr, |
177 | &iscsi_boot_attr_eth_prefix.attr, |
178 | &iscsi_boot_attr_eth_subnet.attr, |
179 | &iscsi_boot_attr_eth_origin.attr, |
180 | &iscsi_boot_attr_eth_gateway.attr, |
181 | &iscsi_boot_attr_eth_primary_dns.attr, |
182 | &iscsi_boot_attr_eth_secondary_dns.attr, |
183 | &iscsi_boot_attr_eth_dhcp.attr, |
184 | &iscsi_boot_attr_eth_vlan.attr, |
185 | &iscsi_boot_attr_eth_mac.attr, |
186 | &iscsi_boot_attr_eth_hostname.attr, |
187 | NULL |
188 | }; |
189 | |
190 | static umode_t iscsi_boot_eth_attr_is_visible(struct kobject *kobj, |
191 | struct attribute *attr, int i) |
192 | { |
193 | struct iscsi_boot_kobj *boot_kobj = |
194 | container_of(kobj, struct iscsi_boot_kobj, kobj); |
195 | |
196 | if (attr == &iscsi_boot_attr_eth_index.attr) |
197 | return boot_kobj->is_visible(boot_kobj->data, |
198 | ISCSI_BOOT_ETH_INDEX); |
199 | else if (attr == &iscsi_boot_attr_eth_flags.attr) |
200 | return boot_kobj->is_visible(boot_kobj->data, |
201 | ISCSI_BOOT_ETH_FLAGS); |
202 | else if (attr == &iscsi_boot_attr_eth_ip.attr) |
203 | return boot_kobj->is_visible(boot_kobj->data, |
204 | ISCSI_BOOT_ETH_IP_ADDR); |
205 | else if (attr == &iscsi_boot_attr_eth_prefix.attr) |
206 | return boot_kobj->is_visible(boot_kobj->data, |
207 | ISCSI_BOOT_ETH_PREFIX_LEN); |
208 | else if (attr == &iscsi_boot_attr_eth_subnet.attr) |
209 | return boot_kobj->is_visible(boot_kobj->data, |
210 | ISCSI_BOOT_ETH_SUBNET_MASK); |
211 | else if (attr == &iscsi_boot_attr_eth_origin.attr) |
212 | return boot_kobj->is_visible(boot_kobj->data, |
213 | ISCSI_BOOT_ETH_ORIGIN); |
214 | else if (attr == &iscsi_boot_attr_eth_gateway.attr) |
215 | return boot_kobj->is_visible(boot_kobj->data, |
216 | ISCSI_BOOT_ETH_GATEWAY); |
217 | else if (attr == &iscsi_boot_attr_eth_primary_dns.attr) |
218 | return boot_kobj->is_visible(boot_kobj->data, |
219 | ISCSI_BOOT_ETH_PRIMARY_DNS); |
220 | else if (attr == &iscsi_boot_attr_eth_secondary_dns.attr) |
221 | return boot_kobj->is_visible(boot_kobj->data, |
222 | ISCSI_BOOT_ETH_SECONDARY_DNS); |
223 | else if (attr == &iscsi_boot_attr_eth_dhcp.attr) |
224 | return boot_kobj->is_visible(boot_kobj->data, |
225 | ISCSI_BOOT_ETH_DHCP); |
226 | else if (attr == &iscsi_boot_attr_eth_vlan.attr) |
227 | return boot_kobj->is_visible(boot_kobj->data, |
228 | ISCSI_BOOT_ETH_VLAN); |
229 | else if (attr == &iscsi_boot_attr_eth_mac.attr) |
230 | return boot_kobj->is_visible(boot_kobj->data, |
231 | ISCSI_BOOT_ETH_MAC); |
232 | else if (attr == &iscsi_boot_attr_eth_hostname.attr) |
233 | return boot_kobj->is_visible(boot_kobj->data, |
234 | ISCSI_BOOT_ETH_HOSTNAME); |
235 | return 0; |
236 | } |
237 | |
238 | static struct attribute_group iscsi_boot_ethernet_attr_group = { |
239 | .attrs = ethernet_attrs, |
240 | .is_visible = iscsi_boot_eth_attr_is_visible, |
241 | }; |
242 | |
243 | /* Initiator attrs */ |
244 | iscsi_boot_rd_attr(ini_index, index, ISCSI_BOOT_INI_INDEX); |
245 | iscsi_boot_rd_attr(ini_flags, flags, ISCSI_BOOT_INI_FLAGS); |
246 | iscsi_boot_rd_attr(ini_isns, isns-server, ISCSI_BOOT_INI_ISNS_SERVER); |
247 | iscsi_boot_rd_attr(ini_slp, slp-server, ISCSI_BOOT_INI_SLP_SERVER); |
248 | iscsi_boot_rd_attr(ini_primary_radius, pri-radius-server, |
249 | ISCSI_BOOT_INI_PRI_RADIUS_SERVER); |
250 | iscsi_boot_rd_attr(ini_secondary_radius, sec-radius-server, |
251 | ISCSI_BOOT_INI_SEC_RADIUS_SERVER); |
252 | iscsi_boot_rd_attr(ini_name, initiator-name, ISCSI_BOOT_INI_INITIATOR_NAME); |
253 | |
254 | static struct attribute *initiator_attrs[] = { |
255 | &iscsi_boot_attr_ini_index.attr, |
256 | &iscsi_boot_attr_ini_flags.attr, |
257 | &iscsi_boot_attr_ini_isns.attr, |
258 | &iscsi_boot_attr_ini_slp.attr, |
259 | &iscsi_boot_attr_ini_primary_radius.attr, |
260 | &iscsi_boot_attr_ini_secondary_radius.attr, |
261 | &iscsi_boot_attr_ini_name.attr, |
262 | NULL |
263 | }; |
264 | |
265 | static umode_t iscsi_boot_ini_attr_is_visible(struct kobject *kobj, |
266 | struct attribute *attr, int i) |
267 | { |
268 | struct iscsi_boot_kobj *boot_kobj = |
269 | container_of(kobj, struct iscsi_boot_kobj, kobj); |
270 | |
271 | if (attr == &iscsi_boot_attr_ini_index.attr) |
272 | return boot_kobj->is_visible(boot_kobj->data, |
273 | ISCSI_BOOT_INI_INDEX); |
274 | if (attr == &iscsi_boot_attr_ini_flags.attr) |
275 | return boot_kobj->is_visible(boot_kobj->data, |
276 | ISCSI_BOOT_INI_FLAGS); |
277 | if (attr == &iscsi_boot_attr_ini_isns.attr) |
278 | return boot_kobj->is_visible(boot_kobj->data, |
279 | ISCSI_BOOT_INI_ISNS_SERVER); |
280 | if (attr == &iscsi_boot_attr_ini_slp.attr) |
281 | return boot_kobj->is_visible(boot_kobj->data, |
282 | ISCSI_BOOT_INI_SLP_SERVER); |
283 | if (attr == &iscsi_boot_attr_ini_primary_radius.attr) |
284 | return boot_kobj->is_visible(boot_kobj->data, |
285 | ISCSI_BOOT_INI_PRI_RADIUS_SERVER); |
286 | if (attr == &iscsi_boot_attr_ini_secondary_radius.attr) |
287 | return boot_kobj->is_visible(boot_kobj->data, |
288 | ISCSI_BOOT_INI_SEC_RADIUS_SERVER); |
289 | if (attr == &iscsi_boot_attr_ini_name.attr) |
290 | return boot_kobj->is_visible(boot_kobj->data, |
291 | ISCSI_BOOT_INI_INITIATOR_NAME); |
292 | |
293 | return 0; |
294 | } |
295 | |
296 | static struct attribute_group iscsi_boot_initiator_attr_group = { |
297 | .attrs = initiator_attrs, |
298 | .is_visible = iscsi_boot_ini_attr_is_visible, |
299 | }; |
300 | |
301 | /* iBFT ACPI Table attributes */ |
302 | iscsi_boot_rd_attr(acpitbl_signature, signature, ISCSI_BOOT_ACPITBL_SIGNATURE); |
303 | iscsi_boot_rd_attr(acpitbl_oem_id, oem_id, ISCSI_BOOT_ACPITBL_OEM_ID); |
304 | iscsi_boot_rd_attr(acpitbl_oem_table_id, oem_table_id, |
305 | ISCSI_BOOT_ACPITBL_OEM_TABLE_ID); |
306 | |
307 | static struct attribute *acpitbl_attrs[] = { |
308 | &iscsi_boot_attr_acpitbl_signature.attr, |
309 | &iscsi_boot_attr_acpitbl_oem_id.attr, |
310 | &iscsi_boot_attr_acpitbl_oem_table_id.attr, |
311 | NULL |
312 | }; |
313 | |
314 | static umode_t iscsi_boot_acpitbl_attr_is_visible(struct kobject *kobj, |
315 | struct attribute *attr, int i) |
316 | { |
317 | struct iscsi_boot_kobj *boot_kobj = |
318 | container_of(kobj, struct iscsi_boot_kobj, kobj); |
319 | |
320 | if (attr == &iscsi_boot_attr_acpitbl_signature.attr) |
321 | return boot_kobj->is_visible(boot_kobj->data, |
322 | ISCSI_BOOT_ACPITBL_SIGNATURE); |
323 | if (attr == &iscsi_boot_attr_acpitbl_oem_id.attr) |
324 | return boot_kobj->is_visible(boot_kobj->data, |
325 | ISCSI_BOOT_ACPITBL_OEM_ID); |
326 | if (attr == &iscsi_boot_attr_acpitbl_oem_table_id.attr) |
327 | return boot_kobj->is_visible(boot_kobj->data, |
328 | ISCSI_BOOT_ACPITBL_OEM_TABLE_ID); |
329 | return 0; |
330 | } |
331 | |
332 | static struct attribute_group iscsi_boot_acpitbl_attr_group = { |
333 | .attrs = acpitbl_attrs, |
334 | .is_visible = iscsi_boot_acpitbl_attr_is_visible, |
335 | }; |
336 | |
337 | static struct iscsi_boot_kobj * |
338 | iscsi_boot_create_kobj(struct iscsi_boot_kset *boot_kset, |
339 | struct attribute_group *attr_group, |
340 | const char *name, int index, void *data, |
341 | ssize_t (*show) (void *data, int type, char *buf), |
342 | umode_t (*is_visible) (void *data, int type), |
343 | void (*release) (void *data)) |
344 | { |
345 | struct iscsi_boot_kobj *boot_kobj; |
346 | |
347 | boot_kobj = kzalloc(size: sizeof(*boot_kobj), GFP_KERNEL); |
348 | if (!boot_kobj) |
349 | return NULL; |
350 | INIT_LIST_HEAD(list: &boot_kobj->list); |
351 | |
352 | boot_kobj->kobj.kset = boot_kset->kset; |
353 | if (kobject_init_and_add(kobj: &boot_kobj->kobj, ktype: &iscsi_boot_ktype, |
354 | NULL, fmt: name, index)) { |
355 | kobject_put(kobj: &boot_kobj->kobj); |
356 | return NULL; |
357 | } |
358 | boot_kobj->data = data; |
359 | boot_kobj->show = show; |
360 | boot_kobj->is_visible = is_visible; |
361 | boot_kobj->release = release; |
362 | |
363 | if (sysfs_create_group(kobj: &boot_kobj->kobj, grp: attr_group)) { |
364 | /* |
365 | * We do not want to free this because the caller |
366 | * will assume that since the creation call failed |
367 | * the boot kobj was not setup and the normal release |
368 | * path is not being run. |
369 | */ |
370 | boot_kobj->release = NULL; |
371 | kobject_put(kobj: &boot_kobj->kobj); |
372 | return NULL; |
373 | } |
374 | boot_kobj->attr_group = attr_group; |
375 | |
376 | kobject_uevent(kobj: &boot_kobj->kobj, action: KOBJ_ADD); |
377 | /* Nothing broke so lets add it to the list. */ |
378 | list_add_tail(new: &boot_kobj->list, head: &boot_kset->kobj_list); |
379 | return boot_kobj; |
380 | } |
381 | |
382 | static void iscsi_boot_remove_kobj(struct iscsi_boot_kobj *boot_kobj) |
383 | { |
384 | list_del(entry: &boot_kobj->list); |
385 | sysfs_remove_group(kobj: &boot_kobj->kobj, grp: boot_kobj->attr_group); |
386 | kobject_put(kobj: &boot_kobj->kobj); |
387 | } |
388 | |
389 | /** |
390 | * iscsi_boot_create_target() - create boot target sysfs dir |
391 | * @boot_kset: boot kset |
392 | * @index: the target id |
393 | * @data: driver specific data for target |
394 | * @show: attr show function |
395 | * @is_visible: attr visibility function |
396 | * @release: release function |
397 | * |
398 | * Note: The boot sysfs lib will free the data passed in for the caller |
399 | * when all refs to the target kobject have been released. |
400 | */ |
401 | struct iscsi_boot_kobj * |
402 | iscsi_boot_create_target(struct iscsi_boot_kset *boot_kset, int index, |
403 | void *data, |
404 | ssize_t (*show) (void *data, int type, char *buf), |
405 | umode_t (*is_visible) (void *data, int type), |
406 | void (*release) (void *data)) |
407 | { |
408 | return iscsi_boot_create_kobj(boot_kset, attr_group: &iscsi_boot_target_attr_group, |
409 | name: "target%d" , index, data, show, is_visible, |
410 | release); |
411 | } |
412 | EXPORT_SYMBOL_GPL(iscsi_boot_create_target); |
413 | |
414 | /** |
415 | * iscsi_boot_create_initiator() - create boot initiator sysfs dir |
416 | * @boot_kset: boot kset |
417 | * @index: the initiator id |
418 | * @data: driver specific data |
419 | * @show: attr show function |
420 | * @is_visible: attr visibility function |
421 | * @release: release function |
422 | * |
423 | * Note: The boot sysfs lib will free the data passed in for the caller |
424 | * when all refs to the initiator kobject have been released. |
425 | */ |
426 | struct iscsi_boot_kobj * |
427 | iscsi_boot_create_initiator(struct iscsi_boot_kset *boot_kset, int index, |
428 | void *data, |
429 | ssize_t (*show) (void *data, int type, char *buf), |
430 | umode_t (*is_visible) (void *data, int type), |
431 | void (*release) (void *data)) |
432 | { |
433 | return iscsi_boot_create_kobj(boot_kset, |
434 | attr_group: &iscsi_boot_initiator_attr_group, |
435 | name: "initiator" , index, data, show, |
436 | is_visible, release); |
437 | } |
438 | EXPORT_SYMBOL_GPL(iscsi_boot_create_initiator); |
439 | |
440 | /** |
441 | * iscsi_boot_create_ethernet() - create boot ethernet sysfs dir |
442 | * @boot_kset: boot kset |
443 | * @index: the ethernet device id |
444 | * @data: driver specific data |
445 | * @show: attr show function |
446 | * @is_visible: attr visibility function |
447 | * @release: release function |
448 | * |
449 | * Note: The boot sysfs lib will free the data passed in for the caller |
450 | * when all refs to the ethernet kobject have been released. |
451 | */ |
452 | struct iscsi_boot_kobj * |
453 | iscsi_boot_create_ethernet(struct iscsi_boot_kset *boot_kset, int index, |
454 | void *data, |
455 | ssize_t (*show) (void *data, int type, char *buf), |
456 | umode_t (*is_visible) (void *data, int type), |
457 | void (*release) (void *data)) |
458 | { |
459 | return iscsi_boot_create_kobj(boot_kset, |
460 | attr_group: &iscsi_boot_ethernet_attr_group, |
461 | name: "ethernet%d" , index, data, show, |
462 | is_visible, release); |
463 | } |
464 | EXPORT_SYMBOL_GPL(iscsi_boot_create_ethernet); |
465 | |
466 | /** |
467 | * iscsi_boot_create_acpitbl() - create boot acpi table sysfs dir |
468 | * @boot_kset: boot kset |
469 | * @index: not used |
470 | * @data: driver specific data |
471 | * @show: attr show function |
472 | * @is_visible: attr visibility function |
473 | * @release: release function |
474 | * |
475 | * Note: The boot sysfs lib will free the data passed in for the caller |
476 | * when all refs to the acpitbl kobject have been released. |
477 | */ |
478 | struct iscsi_boot_kobj * |
479 | iscsi_boot_create_acpitbl(struct iscsi_boot_kset *boot_kset, int index, |
480 | void *data, |
481 | ssize_t (*show)(void *data, int type, char *buf), |
482 | umode_t (*is_visible)(void *data, int type), |
483 | void (*release)(void *data)) |
484 | { |
485 | return iscsi_boot_create_kobj(boot_kset, |
486 | attr_group: &iscsi_boot_acpitbl_attr_group, |
487 | name: "acpi_header" , index, data, show, |
488 | is_visible, release); |
489 | } |
490 | EXPORT_SYMBOL_GPL(iscsi_boot_create_acpitbl); |
491 | |
492 | /** |
493 | * iscsi_boot_create_kset() - creates root sysfs tree |
494 | * @set_name: name of root dir |
495 | */ |
496 | struct iscsi_boot_kset *iscsi_boot_create_kset(const char *set_name) |
497 | { |
498 | struct iscsi_boot_kset *boot_kset; |
499 | |
500 | boot_kset = kzalloc(size: sizeof(*boot_kset), GFP_KERNEL); |
501 | if (!boot_kset) |
502 | return NULL; |
503 | |
504 | boot_kset->kset = kset_create_and_add(name: set_name, NULL, parent_kobj: firmware_kobj); |
505 | if (!boot_kset->kset) { |
506 | kfree(objp: boot_kset); |
507 | return NULL; |
508 | } |
509 | |
510 | INIT_LIST_HEAD(list: &boot_kset->kobj_list); |
511 | return boot_kset; |
512 | } |
513 | EXPORT_SYMBOL_GPL(iscsi_boot_create_kset); |
514 | |
515 | /** |
516 | * iscsi_boot_create_host_kset() - creates root sysfs tree for a scsi host |
517 | * @hostno: host number of scsi host |
518 | */ |
519 | struct iscsi_boot_kset *iscsi_boot_create_host_kset(unsigned int hostno) |
520 | { |
521 | struct iscsi_boot_kset *boot_kset; |
522 | char *set_name; |
523 | |
524 | set_name = kasprintf(GFP_KERNEL, fmt: "iscsi_boot%u" , hostno); |
525 | if (!set_name) |
526 | return NULL; |
527 | |
528 | boot_kset = iscsi_boot_create_kset(set_name); |
529 | kfree(objp: set_name); |
530 | return boot_kset; |
531 | } |
532 | EXPORT_SYMBOL_GPL(iscsi_boot_create_host_kset); |
533 | |
534 | /** |
535 | * iscsi_boot_destroy_kset() - destroy kset and kobjects under it |
536 | * @boot_kset: boot kset |
537 | * |
538 | * This will remove the kset and kobjects and attrs under it. |
539 | */ |
540 | void iscsi_boot_destroy_kset(struct iscsi_boot_kset *boot_kset) |
541 | { |
542 | struct iscsi_boot_kobj *boot_kobj, *tmp_kobj; |
543 | |
544 | if (!boot_kset) |
545 | return; |
546 | |
547 | list_for_each_entry_safe(boot_kobj, tmp_kobj, |
548 | &boot_kset->kobj_list, list) |
549 | iscsi_boot_remove_kobj(boot_kobj); |
550 | |
551 | kset_unregister(kset: boot_kset->kset); |
552 | kfree(objp: boot_kset); |
553 | } |
554 | EXPORT_SYMBOL_GPL(iscsi_boot_destroy_kset); |
555 | |