diff options
Diffstat (limited to 'drivers/target/target_core_user.c')
-rw-r--r-- | drivers/target/target_core_user.c | 447 |
1 files changed, 397 insertions, 50 deletions
diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index beb5f098f32d..80ee130f8253 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c | |||
@@ -87,6 +87,8 @@ | |||
87 | /* Default maximum of the global data blocks(512K * PAGE_SIZE) */ | 87 | /* Default maximum of the global data blocks(512K * PAGE_SIZE) */ |
88 | #define TCMU_GLOBAL_MAX_BLOCKS (512 * 1024) | 88 | #define TCMU_GLOBAL_MAX_BLOCKS (512 * 1024) |
89 | 89 | ||
90 | static u8 tcmu_kern_cmd_reply_supported; | ||
91 | |||
90 | static struct device *tcmu_root_device; | 92 | static struct device *tcmu_root_device; |
91 | 93 | ||
92 | struct tcmu_hba { | 94 | struct tcmu_hba { |
@@ -95,6 +97,13 @@ struct tcmu_hba { | |||
95 | 97 | ||
96 | #define TCMU_CONFIG_LEN 256 | 98 | #define TCMU_CONFIG_LEN 256 |
97 | 99 | ||
100 | struct tcmu_nl_cmd { | ||
101 | /* wake up thread waiting for reply */ | ||
102 | struct completion complete; | ||
103 | int cmd; | ||
104 | int status; | ||
105 | }; | ||
106 | |||
98 | struct tcmu_dev { | 107 | struct tcmu_dev { |
99 | struct list_head node; | 108 | struct list_head node; |
100 | struct kref kref; | 109 | struct kref kref; |
@@ -135,6 +144,11 @@ struct tcmu_dev { | |||
135 | struct timer_list timeout; | 144 | struct timer_list timeout; |
136 | unsigned int cmd_time_out; | 145 | unsigned int cmd_time_out; |
137 | 146 | ||
147 | spinlock_t nl_cmd_lock; | ||
148 | struct tcmu_nl_cmd curr_nl_cmd; | ||
149 | /* wake up threads waiting on curr_nl_cmd */ | ||
150 | wait_queue_head_t nl_cmd_wq; | ||
151 | |||
138 | char dev_config[TCMU_CONFIG_LEN]; | 152 | char dev_config[TCMU_CONFIG_LEN]; |
139 | }; | 153 | }; |
140 | 154 | ||
@@ -178,16 +192,128 @@ static const struct genl_multicast_group tcmu_mcgrps[] = { | |||
178 | [TCMU_MCGRP_CONFIG] = { .name = "config", }, | 192 | [TCMU_MCGRP_CONFIG] = { .name = "config", }, |
179 | }; | 193 | }; |
180 | 194 | ||
195 | static struct nla_policy tcmu_attr_policy[TCMU_ATTR_MAX+1] = { | ||
196 | [TCMU_ATTR_DEVICE] = { .type = NLA_STRING }, | ||
197 | [TCMU_ATTR_MINOR] = { .type = NLA_U32 }, | ||
198 | [TCMU_ATTR_CMD_STATUS] = { .type = NLA_S32 }, | ||
199 | [TCMU_ATTR_DEVICE_ID] = { .type = NLA_U32 }, | ||
200 | [TCMU_ATTR_SUPP_KERN_CMD_REPLY] = { .type = NLA_U8 }, | ||
201 | }; | ||
202 | |||
203 | static int tcmu_genl_cmd_done(struct genl_info *info, int completed_cmd) | ||
204 | { | ||
205 | struct se_device *dev; | ||
206 | struct tcmu_dev *udev; | ||
207 | struct tcmu_nl_cmd *nl_cmd; | ||
208 | int dev_id, rc, ret = 0; | ||
209 | bool is_removed = (completed_cmd == TCMU_CMD_REMOVED_DEVICE); | ||
210 | |||
211 | if (!info->attrs[TCMU_ATTR_CMD_STATUS] || | ||
212 | !info->attrs[TCMU_ATTR_DEVICE_ID]) { | ||
213 | printk(KERN_ERR "TCMU_ATTR_CMD_STATUS or TCMU_ATTR_DEVICE_ID not set, doing nothing\n"); | ||
214 | return -EINVAL; | ||
215 | } | ||
216 | |||
217 | dev_id = nla_get_u32(info->attrs[TCMU_ATTR_DEVICE_ID]); | ||
218 | rc = nla_get_s32(info->attrs[TCMU_ATTR_CMD_STATUS]); | ||
219 | |||
220 | dev = target_find_device(dev_id, !is_removed); | ||
221 | if (!dev) { | ||
222 | printk(KERN_ERR "tcmu nl cmd %u/%u completion could not find device with dev id %u.\n", | ||
223 | completed_cmd, rc, dev_id); | ||
224 | return -ENODEV; | ||
225 | } | ||
226 | udev = TCMU_DEV(dev); | ||
227 | |||
228 | spin_lock(&udev->nl_cmd_lock); | ||
229 | nl_cmd = &udev->curr_nl_cmd; | ||
230 | |||
231 | pr_debug("genl cmd done got id %d curr %d done %d rc %d\n", dev_id, | ||
232 | nl_cmd->cmd, completed_cmd, rc); | ||
233 | |||
234 | if (nl_cmd->cmd != completed_cmd) { | ||
235 | printk(KERN_ERR "Mismatched commands (Expecting reply for %d. Current %d).\n", | ||
236 | completed_cmd, nl_cmd->cmd); | ||
237 | ret = -EINVAL; | ||
238 | } else { | ||
239 | nl_cmd->status = rc; | ||
240 | } | ||
241 | |||
242 | spin_unlock(&udev->nl_cmd_lock); | ||
243 | if (!is_removed) | ||
244 | target_undepend_item(&dev->dev_group.cg_item); | ||
245 | if (!ret) | ||
246 | complete(&nl_cmd->complete); | ||
247 | return ret; | ||
248 | } | ||
249 | |||
250 | static int tcmu_genl_rm_dev_done(struct sk_buff *skb, struct genl_info *info) | ||
251 | { | ||
252 | return tcmu_genl_cmd_done(info, TCMU_CMD_REMOVED_DEVICE); | ||
253 | } | ||
254 | |||
255 | static int tcmu_genl_add_dev_done(struct sk_buff *skb, struct genl_info *info) | ||
256 | { | ||
257 | return tcmu_genl_cmd_done(info, TCMU_CMD_ADDED_DEVICE); | ||
258 | } | ||
259 | |||
260 | static int tcmu_genl_reconfig_dev_done(struct sk_buff *skb, | ||
261 | struct genl_info *info) | ||
262 | { | ||
263 | return tcmu_genl_cmd_done(info, TCMU_CMD_RECONFIG_DEVICE); | ||
264 | } | ||
265 | |||
266 | static int tcmu_genl_set_features(struct sk_buff *skb, struct genl_info *info) | ||
267 | { | ||
268 | if (info->attrs[TCMU_ATTR_SUPP_KERN_CMD_REPLY]) { | ||
269 | tcmu_kern_cmd_reply_supported = | ||
270 | nla_get_u8(info->attrs[TCMU_ATTR_SUPP_KERN_CMD_REPLY]); | ||
271 | printk(KERN_INFO "tcmu daemon: command reply support %u.\n", | ||
272 | tcmu_kern_cmd_reply_supported); | ||
273 | } | ||
274 | |||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | static const struct genl_ops tcmu_genl_ops[] = { | ||
279 | { | ||
280 | .cmd = TCMU_CMD_SET_FEATURES, | ||
281 | .flags = GENL_ADMIN_PERM, | ||
282 | .policy = tcmu_attr_policy, | ||
283 | .doit = tcmu_genl_set_features, | ||
284 | }, | ||
285 | { | ||
286 | .cmd = TCMU_CMD_ADDED_DEVICE_DONE, | ||
287 | .flags = GENL_ADMIN_PERM, | ||
288 | .policy = tcmu_attr_policy, | ||
289 | .doit = tcmu_genl_add_dev_done, | ||
290 | }, | ||
291 | { | ||
292 | .cmd = TCMU_CMD_REMOVED_DEVICE_DONE, | ||
293 | .flags = GENL_ADMIN_PERM, | ||
294 | .policy = tcmu_attr_policy, | ||
295 | .doit = tcmu_genl_rm_dev_done, | ||
296 | }, | ||
297 | { | ||
298 | .cmd = TCMU_CMD_RECONFIG_DEVICE_DONE, | ||
299 | .flags = GENL_ADMIN_PERM, | ||
300 | .policy = tcmu_attr_policy, | ||
301 | .doit = tcmu_genl_reconfig_dev_done, | ||
302 | }, | ||
303 | }; | ||
304 | |||
181 | /* Our generic netlink family */ | 305 | /* Our generic netlink family */ |
182 | static struct genl_family tcmu_genl_family __ro_after_init = { | 306 | static struct genl_family tcmu_genl_family __ro_after_init = { |
183 | .module = THIS_MODULE, | 307 | .module = THIS_MODULE, |
184 | .hdrsize = 0, | 308 | .hdrsize = 0, |
185 | .name = "TCM-USER", | 309 | .name = "TCM-USER", |
186 | .version = 1, | 310 | .version = 2, |
187 | .maxattr = TCMU_ATTR_MAX, | 311 | .maxattr = TCMU_ATTR_MAX, |
188 | .mcgrps = tcmu_mcgrps, | 312 | .mcgrps = tcmu_mcgrps, |
189 | .n_mcgrps = ARRAY_SIZE(tcmu_mcgrps), | 313 | .n_mcgrps = ARRAY_SIZE(tcmu_mcgrps), |
190 | .netnsok = true, | 314 | .netnsok = true, |
315 | .ops = tcmu_genl_ops, | ||
316 | .n_ops = ARRAY_SIZE(tcmu_genl_ops), | ||
191 | }; | 317 | }; |
192 | 318 | ||
193 | #define tcmu_cmd_set_dbi_cur(cmd, index) ((cmd)->dbi_cur = (index)) | 319 | #define tcmu_cmd_set_dbi_cur(cmd, index) ((cmd)->dbi_cur = (index)) |
@@ -216,7 +342,6 @@ static inline bool tcmu_get_empty_block(struct tcmu_dev *udev, | |||
216 | 342 | ||
217 | page = radix_tree_lookup(&udev->data_blocks, dbi); | 343 | page = radix_tree_lookup(&udev->data_blocks, dbi); |
218 | if (!page) { | 344 | if (!page) { |
219 | |||
220 | if (atomic_add_return(1, &global_db_count) > | 345 | if (atomic_add_return(1, &global_db_count) > |
221 | TCMU_GLOBAL_MAX_BLOCKS) { | 346 | TCMU_GLOBAL_MAX_BLOCKS) { |
222 | atomic_dec(&global_db_count); | 347 | atomic_dec(&global_db_count); |
@@ -226,14 +351,11 @@ static inline bool tcmu_get_empty_block(struct tcmu_dev *udev, | |||
226 | /* try to get new page from the mm */ | 351 | /* try to get new page from the mm */ |
227 | page = alloc_page(GFP_KERNEL); | 352 | page = alloc_page(GFP_KERNEL); |
228 | if (!page) | 353 | if (!page) |
229 | return false; | 354 | goto err_alloc; |
230 | 355 | ||
231 | ret = radix_tree_insert(&udev->data_blocks, dbi, page); | 356 | ret = radix_tree_insert(&udev->data_blocks, dbi, page); |
232 | if (ret) { | 357 | if (ret) |
233 | __free_page(page); | 358 | goto err_insert; |
234 | return false; | ||
235 | } | ||
236 | |||
237 | } | 359 | } |
238 | 360 | ||
239 | if (dbi > udev->dbi_max) | 361 | if (dbi > udev->dbi_max) |
@@ -243,6 +365,11 @@ static inline bool tcmu_get_empty_block(struct tcmu_dev *udev, | |||
243 | tcmu_cmd_set_dbi(tcmu_cmd, dbi); | 365 | tcmu_cmd_set_dbi(tcmu_cmd, dbi); |
244 | 366 | ||
245 | return true; | 367 | return true; |
368 | err_insert: | ||
369 | __free_page(page); | ||
370 | err_alloc: | ||
371 | atomic_dec(&global_db_count); | ||
372 | return false; | ||
246 | } | 373 | } |
247 | 374 | ||
248 | static bool tcmu_get_empty_blocks(struct tcmu_dev *udev, | 375 | static bool tcmu_get_empty_blocks(struct tcmu_dev *udev, |
@@ -401,7 +528,7 @@ static inline size_t get_block_offset_user(struct tcmu_dev *dev, | |||
401 | DATA_BLOCK_SIZE - remaining; | 528 | DATA_BLOCK_SIZE - remaining; |
402 | } | 529 | } |
403 | 530 | ||
404 | static inline size_t iov_tail(struct tcmu_dev *udev, struct iovec *iov) | 531 | static inline size_t iov_tail(struct iovec *iov) |
405 | { | 532 | { |
406 | return (size_t)iov->iov_base + iov->iov_len; | 533 | return (size_t)iov->iov_base + iov->iov_len; |
407 | } | 534 | } |
@@ -437,10 +564,10 @@ static int scatter_data_area(struct tcmu_dev *udev, | |||
437 | to_offset = get_block_offset_user(udev, dbi, | 564 | to_offset = get_block_offset_user(udev, dbi, |
438 | block_remaining); | 565 | block_remaining); |
439 | offset = DATA_BLOCK_SIZE - block_remaining; | 566 | offset = DATA_BLOCK_SIZE - block_remaining; |
440 | to = (void *)(unsigned long)to + offset; | 567 | to += offset; |
441 | 568 | ||
442 | if (*iov_cnt != 0 && | 569 | if (*iov_cnt != 0 && |
443 | to_offset == iov_tail(udev, *iov)) { | 570 | to_offset == iov_tail(*iov)) { |
444 | (*iov)->iov_len += copy_bytes; | 571 | (*iov)->iov_len += copy_bytes; |
445 | } else { | 572 | } else { |
446 | new_iov(iov, iov_cnt, udev); | 573 | new_iov(iov, iov_cnt, udev); |
@@ -510,7 +637,7 @@ static void gather_data_area(struct tcmu_dev *udev, struct tcmu_cmd *cmd, | |||
510 | copy_bytes = min_t(size_t, sg_remaining, | 637 | copy_bytes = min_t(size_t, sg_remaining, |
511 | block_remaining); | 638 | block_remaining); |
512 | offset = DATA_BLOCK_SIZE - block_remaining; | 639 | offset = DATA_BLOCK_SIZE - block_remaining; |
513 | from = (void *)(unsigned long)from + offset; | 640 | from += offset; |
514 | tcmu_flush_dcache_range(from, copy_bytes); | 641 | tcmu_flush_dcache_range(from, copy_bytes); |
515 | memcpy(to + sg->length - sg_remaining, from, | 642 | memcpy(to + sg->length - sg_remaining, from, |
516 | copy_bytes); | 643 | copy_bytes); |
@@ -596,10 +723,7 @@ static bool is_ring_space_avail(struct tcmu_dev *udev, struct tcmu_cmd *cmd, | |||
596 | } | 723 | } |
597 | } | 724 | } |
598 | 725 | ||
599 | if (!tcmu_get_empty_blocks(udev, cmd)) | 726 | return tcmu_get_empty_blocks(udev, cmd); |
600 | return false; | ||
601 | |||
602 | return true; | ||
603 | } | 727 | } |
604 | 728 | ||
605 | static inline size_t tcmu_cmd_get_base_cmd_size(size_t iov_cnt) | 729 | static inline size_t tcmu_cmd_get_base_cmd_size(size_t iov_cnt) |
@@ -699,25 +823,24 @@ tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) | |||
699 | size_t pad_size = head_to_end(cmd_head, udev->cmdr_size); | 823 | size_t pad_size = head_to_end(cmd_head, udev->cmdr_size); |
700 | 824 | ||
701 | entry = (void *) mb + CMDR_OFF + cmd_head; | 825 | entry = (void *) mb + CMDR_OFF + cmd_head; |
702 | tcmu_flush_dcache_range(entry, sizeof(*entry)); | ||
703 | tcmu_hdr_set_op(&entry->hdr.len_op, TCMU_OP_PAD); | 826 | tcmu_hdr_set_op(&entry->hdr.len_op, TCMU_OP_PAD); |
704 | tcmu_hdr_set_len(&entry->hdr.len_op, pad_size); | 827 | tcmu_hdr_set_len(&entry->hdr.len_op, pad_size); |
705 | entry->hdr.cmd_id = 0; /* not used for PAD */ | 828 | entry->hdr.cmd_id = 0; /* not used for PAD */ |
706 | entry->hdr.kflags = 0; | 829 | entry->hdr.kflags = 0; |
707 | entry->hdr.uflags = 0; | 830 | entry->hdr.uflags = 0; |
831 | tcmu_flush_dcache_range(entry, sizeof(*entry)); | ||
708 | 832 | ||
709 | UPDATE_HEAD(mb->cmd_head, pad_size, udev->cmdr_size); | 833 | UPDATE_HEAD(mb->cmd_head, pad_size, udev->cmdr_size); |
834 | tcmu_flush_dcache_range(mb, sizeof(*mb)); | ||
710 | 835 | ||
711 | cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ | 836 | cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ |
712 | WARN_ON(cmd_head != 0); | 837 | WARN_ON(cmd_head != 0); |
713 | } | 838 | } |
714 | 839 | ||
715 | entry = (void *) mb + CMDR_OFF + cmd_head; | 840 | entry = (void *) mb + CMDR_OFF + cmd_head; |
716 | tcmu_flush_dcache_range(entry, sizeof(*entry)); | 841 | memset(entry, 0, command_size); |
717 | tcmu_hdr_set_op(&entry->hdr.len_op, TCMU_OP_CMD); | 842 | tcmu_hdr_set_op(&entry->hdr.len_op, TCMU_OP_CMD); |
718 | entry->hdr.cmd_id = tcmu_cmd->cmd_id; | 843 | entry->hdr.cmd_id = tcmu_cmd->cmd_id; |
719 | entry->hdr.kflags = 0; | ||
720 | entry->hdr.uflags = 0; | ||
721 | 844 | ||
722 | /* Handle allocating space from the data area */ | 845 | /* Handle allocating space from the data area */ |
723 | tcmu_cmd_reset_dbi_cur(tcmu_cmd); | 846 | tcmu_cmd_reset_dbi_cur(tcmu_cmd); |
@@ -736,11 +859,10 @@ tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) | |||
736 | return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | 859 | return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; |
737 | } | 860 | } |
738 | entry->req.iov_cnt = iov_cnt; | 861 | entry->req.iov_cnt = iov_cnt; |
739 | entry->req.iov_dif_cnt = 0; | ||
740 | 862 | ||
741 | /* Handle BIDI commands */ | 863 | /* Handle BIDI commands */ |
864 | iov_cnt = 0; | ||
742 | if (se_cmd->se_cmd_flags & SCF_BIDI) { | 865 | if (se_cmd->se_cmd_flags & SCF_BIDI) { |
743 | iov_cnt = 0; | ||
744 | iov++; | 866 | iov++; |
745 | ret = scatter_data_area(udev, tcmu_cmd, | 867 | ret = scatter_data_area(udev, tcmu_cmd, |
746 | se_cmd->t_bidi_data_sg, | 868 | se_cmd->t_bidi_data_sg, |
@@ -753,8 +875,8 @@ tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) | |||
753 | pr_err("tcmu: alloc and scatter bidi data failed\n"); | 875 | pr_err("tcmu: alloc and scatter bidi data failed\n"); |
754 | return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | 876 | return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; |
755 | } | 877 | } |
756 | entry->req.iov_bidi_cnt = iov_cnt; | ||
757 | } | 878 | } |
879 | entry->req.iov_bidi_cnt = iov_cnt; | ||
758 | 880 | ||
759 | /* | 881 | /* |
760 | * Recalaulate the command's base size and size according | 882 | * Recalaulate the command's base size and size according |
@@ -830,8 +952,7 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry * | |||
830 | cmd->se_cmd); | 952 | cmd->se_cmd); |
831 | entry->rsp.scsi_status = SAM_STAT_CHECK_CONDITION; | 953 | entry->rsp.scsi_status = SAM_STAT_CHECK_CONDITION; |
832 | } else if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) { | 954 | } else if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) { |
833 | memcpy(se_cmd->sense_buffer, entry->rsp.sense_buffer, | 955 | transport_copy_sense_to_cmd(se_cmd, entry->rsp.sense_buffer); |
834 | se_cmd->scsi_sense_length); | ||
835 | } else if (se_cmd->se_cmd_flags & SCF_BIDI) { | 956 | } else if (se_cmd->se_cmd_flags & SCF_BIDI) { |
836 | /* Get Data-In buffer before clean up */ | 957 | /* Get Data-In buffer before clean up */ |
837 | gather_data_area(udev, cmd, true); | 958 | gather_data_area(udev, cmd, true); |
@@ -989,6 +1110,9 @@ static struct se_device *tcmu_alloc_device(struct se_hba *hba, const char *name) | |||
989 | setup_timer(&udev->timeout, tcmu_device_timedout, | 1110 | setup_timer(&udev->timeout, tcmu_device_timedout, |
990 | (unsigned long)udev); | 1111 | (unsigned long)udev); |
991 | 1112 | ||
1113 | init_waitqueue_head(&udev->nl_cmd_wq); | ||
1114 | spin_lock_init(&udev->nl_cmd_lock); | ||
1115 | |||
992 | return &udev->se_dev; | 1116 | return &udev->se_dev; |
993 | } | 1117 | } |
994 | 1118 | ||
@@ -1140,6 +1264,7 @@ static int tcmu_open(struct uio_info *info, struct inode *inode) | |||
1140 | return -EBUSY; | 1264 | return -EBUSY; |
1141 | 1265 | ||
1142 | udev->inode = inode; | 1266 | udev->inode = inode; |
1267 | kref_get(&udev->kref); | ||
1143 | 1268 | ||
1144 | pr_debug("open\n"); | 1269 | pr_debug("open\n"); |
1145 | 1270 | ||
@@ -1171,12 +1296,59 @@ static int tcmu_release(struct uio_info *info, struct inode *inode) | |||
1171 | clear_bit(TCMU_DEV_BIT_OPEN, &udev->flags); | 1296 | clear_bit(TCMU_DEV_BIT_OPEN, &udev->flags); |
1172 | 1297 | ||
1173 | pr_debug("close\n"); | 1298 | pr_debug("close\n"); |
1174 | /* release ref from configure */ | 1299 | /* release ref from open */ |
1175 | kref_put(&udev->kref, tcmu_dev_kref_release); | 1300 | kref_put(&udev->kref, tcmu_dev_kref_release); |
1176 | return 0; | 1301 | return 0; |
1177 | } | 1302 | } |
1178 | 1303 | ||
1179 | static int tcmu_netlink_event(enum tcmu_genl_cmd cmd, const char *name, int minor) | 1304 | static void tcmu_init_genl_cmd_reply(struct tcmu_dev *udev, int cmd) |
1305 | { | ||
1306 | struct tcmu_nl_cmd *nl_cmd = &udev->curr_nl_cmd; | ||
1307 | |||
1308 | if (!tcmu_kern_cmd_reply_supported) | ||
1309 | return; | ||
1310 | relock: | ||
1311 | spin_lock(&udev->nl_cmd_lock); | ||
1312 | |||
1313 | if (nl_cmd->cmd != TCMU_CMD_UNSPEC) { | ||
1314 | spin_unlock(&udev->nl_cmd_lock); | ||
1315 | pr_debug("sleeping for open nl cmd\n"); | ||
1316 | wait_event(udev->nl_cmd_wq, (nl_cmd->cmd == TCMU_CMD_UNSPEC)); | ||
1317 | goto relock; | ||
1318 | } | ||
1319 | |||
1320 | memset(nl_cmd, 0, sizeof(*nl_cmd)); | ||
1321 | nl_cmd->cmd = cmd; | ||
1322 | init_completion(&nl_cmd->complete); | ||
1323 | |||
1324 | spin_unlock(&udev->nl_cmd_lock); | ||
1325 | } | ||
1326 | |||
1327 | static int tcmu_wait_genl_cmd_reply(struct tcmu_dev *udev) | ||
1328 | { | ||
1329 | struct tcmu_nl_cmd *nl_cmd = &udev->curr_nl_cmd; | ||
1330 | int ret; | ||
1331 | DEFINE_WAIT(__wait); | ||
1332 | |||
1333 | if (!tcmu_kern_cmd_reply_supported) | ||
1334 | return 0; | ||
1335 | |||
1336 | pr_debug("sleeping for nl reply\n"); | ||
1337 | wait_for_completion(&nl_cmd->complete); | ||
1338 | |||
1339 | spin_lock(&udev->nl_cmd_lock); | ||
1340 | nl_cmd->cmd = TCMU_CMD_UNSPEC; | ||
1341 | ret = nl_cmd->status; | ||
1342 | nl_cmd->status = 0; | ||
1343 | spin_unlock(&udev->nl_cmd_lock); | ||
1344 | |||
1345 | wake_up_all(&udev->nl_cmd_wq); | ||
1346 | |||
1347 | return ret;; | ||
1348 | } | ||
1349 | |||
1350 | static int tcmu_netlink_event(struct tcmu_dev *udev, enum tcmu_genl_cmd cmd, | ||
1351 | int reconfig_attr, const void *reconfig_data) | ||
1180 | { | 1352 | { |
1181 | struct sk_buff *skb; | 1353 | struct sk_buff *skb; |
1182 | void *msg_header; | 1354 | void *msg_header; |
@@ -1190,22 +1362,51 @@ static int tcmu_netlink_event(enum tcmu_genl_cmd cmd, const char *name, int mino | |||
1190 | if (!msg_header) | 1362 | if (!msg_header) |
1191 | goto free_skb; | 1363 | goto free_skb; |
1192 | 1364 | ||
1193 | ret = nla_put_string(skb, TCMU_ATTR_DEVICE, name); | 1365 | ret = nla_put_string(skb, TCMU_ATTR_DEVICE, udev->uio_info.name); |
1366 | if (ret < 0) | ||
1367 | goto free_skb; | ||
1368 | |||
1369 | ret = nla_put_u32(skb, TCMU_ATTR_MINOR, udev->uio_info.uio_dev->minor); | ||
1194 | if (ret < 0) | 1370 | if (ret < 0) |
1195 | goto free_skb; | 1371 | goto free_skb; |
1196 | 1372 | ||
1197 | ret = nla_put_u32(skb, TCMU_ATTR_MINOR, minor); | 1373 | ret = nla_put_u32(skb, TCMU_ATTR_DEVICE_ID, udev->se_dev.dev_index); |
1198 | if (ret < 0) | 1374 | if (ret < 0) |
1199 | goto free_skb; | 1375 | goto free_skb; |
1200 | 1376 | ||
1377 | if (cmd == TCMU_CMD_RECONFIG_DEVICE) { | ||
1378 | switch (reconfig_attr) { | ||
1379 | case TCMU_ATTR_DEV_CFG: | ||
1380 | ret = nla_put_string(skb, reconfig_attr, reconfig_data); | ||
1381 | break; | ||
1382 | case TCMU_ATTR_DEV_SIZE: | ||
1383 | ret = nla_put_u64_64bit(skb, reconfig_attr, | ||
1384 | *((u64 *)reconfig_data), | ||
1385 | TCMU_ATTR_PAD); | ||
1386 | break; | ||
1387 | case TCMU_ATTR_WRITECACHE: | ||
1388 | ret = nla_put_u8(skb, reconfig_attr, | ||
1389 | *((u8 *)reconfig_data)); | ||
1390 | break; | ||
1391 | default: | ||
1392 | BUG(); | ||
1393 | } | ||
1394 | |||
1395 | if (ret < 0) | ||
1396 | goto free_skb; | ||
1397 | } | ||
1398 | |||
1201 | genlmsg_end(skb, msg_header); | 1399 | genlmsg_end(skb, msg_header); |
1202 | 1400 | ||
1401 | tcmu_init_genl_cmd_reply(udev, cmd); | ||
1402 | |||
1203 | ret = genlmsg_multicast_allns(&tcmu_genl_family, skb, 0, | 1403 | ret = genlmsg_multicast_allns(&tcmu_genl_family, skb, 0, |
1204 | TCMU_MCGRP_CONFIG, GFP_KERNEL); | 1404 | TCMU_MCGRP_CONFIG, GFP_KERNEL); |
1205 | |||
1206 | /* We don't care if no one is listening */ | 1405 | /* We don't care if no one is listening */ |
1207 | if (ret == -ESRCH) | 1406 | if (ret == -ESRCH) |
1208 | ret = 0; | 1407 | ret = 0; |
1408 | if (!ret) | ||
1409 | ret = tcmu_wait_genl_cmd_reply(udev); | ||
1209 | 1410 | ||
1210 | return ret; | 1411 | return ret; |
1211 | free_skb: | 1412 | free_skb: |
@@ -1213,19 +1414,14 @@ free_skb: | |||
1213 | return ret; | 1414 | return ret; |
1214 | } | 1415 | } |
1215 | 1416 | ||
1216 | static int tcmu_configure_device(struct se_device *dev) | 1417 | static int tcmu_update_uio_info(struct tcmu_dev *udev) |
1217 | { | 1418 | { |
1218 | struct tcmu_dev *udev = TCMU_DEV(dev); | ||
1219 | struct tcmu_hba *hba = udev->hba->hba_ptr; | 1419 | struct tcmu_hba *hba = udev->hba->hba_ptr; |
1220 | struct uio_info *info; | 1420 | struct uio_info *info; |
1221 | struct tcmu_mailbox *mb; | 1421 | size_t size, used; |
1222 | size_t size; | ||
1223 | size_t used; | ||
1224 | int ret = 0; | ||
1225 | char *str; | 1422 | char *str; |
1226 | 1423 | ||
1227 | info = &udev->uio_info; | 1424 | info = &udev->uio_info; |
1228 | |||
1229 | size = snprintf(NULL, 0, "tcm-user/%u/%s/%s", hba->host_id, udev->name, | 1425 | size = snprintf(NULL, 0, "tcm-user/%u/%s/%s", hba->host_id, udev->name, |
1230 | udev->dev_config); | 1426 | udev->dev_config); |
1231 | size += 1; /* for \0 */ | 1427 | size += 1; /* for \0 */ |
@@ -1234,12 +1430,27 @@ static int tcmu_configure_device(struct se_device *dev) | |||
1234 | return -ENOMEM; | 1430 | return -ENOMEM; |
1235 | 1431 | ||
1236 | used = snprintf(str, size, "tcm-user/%u/%s", hba->host_id, udev->name); | 1432 | used = snprintf(str, size, "tcm-user/%u/%s", hba->host_id, udev->name); |
1237 | |||
1238 | if (udev->dev_config[0]) | 1433 | if (udev->dev_config[0]) |
1239 | snprintf(str + used, size - used, "/%s", udev->dev_config); | 1434 | snprintf(str + used, size - used, "/%s", udev->dev_config); |
1240 | 1435 | ||
1241 | info->name = str; | 1436 | info->name = str; |
1242 | 1437 | ||
1438 | return 0; | ||
1439 | } | ||
1440 | |||
1441 | static int tcmu_configure_device(struct se_device *dev) | ||
1442 | { | ||
1443 | struct tcmu_dev *udev = TCMU_DEV(dev); | ||
1444 | struct uio_info *info; | ||
1445 | struct tcmu_mailbox *mb; | ||
1446 | int ret = 0; | ||
1447 | |||
1448 | ret = tcmu_update_uio_info(udev); | ||
1449 | if (ret) | ||
1450 | return ret; | ||
1451 | |||
1452 | info = &udev->uio_info; | ||
1453 | |||
1243 | udev->mb_addr = vzalloc(CMDR_SIZE); | 1454 | udev->mb_addr = vzalloc(CMDR_SIZE); |
1244 | if (!udev->mb_addr) { | 1455 | if (!udev->mb_addr) { |
1245 | ret = -ENOMEM; | 1456 | ret = -ENOMEM; |
@@ -1290,6 +1501,8 @@ static int tcmu_configure_device(struct se_device *dev) | |||
1290 | /* Other attributes can be configured in userspace */ | 1501 | /* Other attributes can be configured in userspace */ |
1291 | if (!dev->dev_attrib.hw_max_sectors) | 1502 | if (!dev->dev_attrib.hw_max_sectors) |
1292 | dev->dev_attrib.hw_max_sectors = 128; | 1503 | dev->dev_attrib.hw_max_sectors = 128; |
1504 | if (!dev->dev_attrib.emulate_write_cache) | ||
1505 | dev->dev_attrib.emulate_write_cache = 0; | ||
1293 | dev->dev_attrib.hw_queue_depth = 128; | 1506 | dev->dev_attrib.hw_queue_depth = 128; |
1294 | 1507 | ||
1295 | /* | 1508 | /* |
@@ -1298,8 +1511,7 @@ static int tcmu_configure_device(struct se_device *dev) | |||
1298 | */ | 1511 | */ |
1299 | kref_get(&udev->kref); | 1512 | kref_get(&udev->kref); |
1300 | 1513 | ||
1301 | ret = tcmu_netlink_event(TCMU_CMD_ADDED_DEVICE, udev->uio_info.name, | 1514 | ret = tcmu_netlink_event(udev, TCMU_CMD_ADDED_DEVICE, 0, NULL); |
1302 | udev->uio_info.uio_dev->minor); | ||
1303 | if (ret) | 1515 | if (ret) |
1304 | goto err_netlink; | 1516 | goto err_netlink; |
1305 | 1517 | ||
@@ -1355,6 +1567,14 @@ static void tcmu_blocks_release(struct tcmu_dev *udev) | |||
1355 | static void tcmu_free_device(struct se_device *dev) | 1567 | static void tcmu_free_device(struct se_device *dev) |
1356 | { | 1568 | { |
1357 | struct tcmu_dev *udev = TCMU_DEV(dev); | 1569 | struct tcmu_dev *udev = TCMU_DEV(dev); |
1570 | |||
1571 | /* release ref from init */ | ||
1572 | kref_put(&udev->kref, tcmu_dev_kref_release); | ||
1573 | } | ||
1574 | |||
1575 | static void tcmu_destroy_device(struct se_device *dev) | ||
1576 | { | ||
1577 | struct tcmu_dev *udev = TCMU_DEV(dev); | ||
1358 | struct tcmu_cmd *cmd; | 1578 | struct tcmu_cmd *cmd; |
1359 | bool all_expired = true; | 1579 | bool all_expired = true; |
1360 | int i; | 1580 | int i; |
@@ -1379,14 +1599,11 @@ static void tcmu_free_device(struct se_device *dev) | |||
1379 | 1599 | ||
1380 | tcmu_blocks_release(udev); | 1600 | tcmu_blocks_release(udev); |
1381 | 1601 | ||
1382 | if (tcmu_dev_configured(udev)) { | 1602 | tcmu_netlink_event(udev, TCMU_CMD_REMOVED_DEVICE, 0, NULL); |
1383 | tcmu_netlink_event(TCMU_CMD_REMOVED_DEVICE, udev->uio_info.name, | ||
1384 | udev->uio_info.uio_dev->minor); | ||
1385 | 1603 | ||
1386 | uio_unregister_device(&udev->uio_info); | 1604 | uio_unregister_device(&udev->uio_info); |
1387 | } | ||
1388 | 1605 | ||
1389 | /* release ref from init */ | 1606 | /* release ref from configure */ |
1390 | kref_put(&udev->kref, tcmu_dev_kref_release); | 1607 | kref_put(&udev->kref, tcmu_dev_kref_release); |
1391 | } | 1608 | } |
1392 | 1609 | ||
@@ -1546,6 +1763,129 @@ static ssize_t tcmu_cmd_time_out_store(struct config_item *item, const char *pag | |||
1546 | } | 1763 | } |
1547 | CONFIGFS_ATTR(tcmu_, cmd_time_out); | 1764 | CONFIGFS_ATTR(tcmu_, cmd_time_out); |
1548 | 1765 | ||
1766 | static ssize_t tcmu_dev_config_show(struct config_item *item, char *page) | ||
1767 | { | ||
1768 | struct se_dev_attrib *da = container_of(to_config_group(item), | ||
1769 | struct se_dev_attrib, da_group); | ||
1770 | struct tcmu_dev *udev = TCMU_DEV(da->da_dev); | ||
1771 | |||
1772 | return snprintf(page, PAGE_SIZE, "%s\n", udev->dev_config); | ||
1773 | } | ||
1774 | |||
1775 | static ssize_t tcmu_dev_config_store(struct config_item *item, const char *page, | ||
1776 | size_t count) | ||
1777 | { | ||
1778 | struct se_dev_attrib *da = container_of(to_config_group(item), | ||
1779 | struct se_dev_attrib, da_group); | ||
1780 | struct tcmu_dev *udev = TCMU_DEV(da->da_dev); | ||
1781 | int ret, len; | ||
1782 | |||
1783 | len = strlen(page); | ||
1784 | if (!len || len > TCMU_CONFIG_LEN - 1) | ||
1785 | return -EINVAL; | ||
1786 | |||
1787 | /* Check if device has been configured before */ | ||
1788 | if (tcmu_dev_configured(udev)) { | ||
1789 | ret = tcmu_netlink_event(udev, TCMU_CMD_RECONFIG_DEVICE, | ||
1790 | TCMU_ATTR_DEV_CFG, page); | ||
1791 | if (ret) { | ||
1792 | pr_err("Unable to reconfigure device\n"); | ||
1793 | return ret; | ||
1794 | } | ||
1795 | strlcpy(udev->dev_config, page, TCMU_CONFIG_LEN); | ||
1796 | |||
1797 | ret = tcmu_update_uio_info(udev); | ||
1798 | if (ret) | ||
1799 | return ret; | ||
1800 | return count; | ||
1801 | } | ||
1802 | strlcpy(udev->dev_config, page, TCMU_CONFIG_LEN); | ||
1803 | |||
1804 | return count; | ||
1805 | } | ||
1806 | CONFIGFS_ATTR(tcmu_, dev_config); | ||
1807 | |||
1808 | static ssize_t tcmu_dev_size_show(struct config_item *item, char *page) | ||
1809 | { | ||
1810 | struct se_dev_attrib *da = container_of(to_config_group(item), | ||
1811 | struct se_dev_attrib, da_group); | ||
1812 | struct tcmu_dev *udev = TCMU_DEV(da->da_dev); | ||
1813 | |||
1814 | return snprintf(page, PAGE_SIZE, "%zu\n", udev->dev_size); | ||
1815 | } | ||
1816 | |||
1817 | static ssize_t tcmu_dev_size_store(struct config_item *item, const char *page, | ||
1818 | size_t count) | ||
1819 | { | ||
1820 | struct se_dev_attrib *da = container_of(to_config_group(item), | ||
1821 | struct se_dev_attrib, da_group); | ||
1822 | struct tcmu_dev *udev = TCMU_DEV(da->da_dev); | ||
1823 | u64 val; | ||
1824 | int ret; | ||
1825 | |||
1826 | ret = kstrtou64(page, 0, &val); | ||
1827 | if (ret < 0) | ||
1828 | return ret; | ||
1829 | |||
1830 | /* Check if device has been configured before */ | ||
1831 | if (tcmu_dev_configured(udev)) { | ||
1832 | ret = tcmu_netlink_event(udev, TCMU_CMD_RECONFIG_DEVICE, | ||
1833 | TCMU_ATTR_DEV_SIZE, &val); | ||
1834 | if (ret) { | ||
1835 | pr_err("Unable to reconfigure device\n"); | ||
1836 | return ret; | ||
1837 | } | ||
1838 | } | ||
1839 | udev->dev_size = val; | ||
1840 | return count; | ||
1841 | } | ||
1842 | CONFIGFS_ATTR(tcmu_, dev_size); | ||
1843 | |||
1844 | static ssize_t tcmu_emulate_write_cache_show(struct config_item *item, | ||
1845 | char *page) | ||
1846 | { | ||
1847 | struct se_dev_attrib *da = container_of(to_config_group(item), | ||
1848 | struct se_dev_attrib, da_group); | ||
1849 | |||
1850 | return snprintf(page, PAGE_SIZE, "%i\n", da->emulate_write_cache); | ||
1851 | } | ||
1852 | |||
1853 | static ssize_t tcmu_emulate_write_cache_store(struct config_item *item, | ||
1854 | const char *page, size_t count) | ||
1855 | { | ||
1856 | struct se_dev_attrib *da = container_of(to_config_group(item), | ||
1857 | struct se_dev_attrib, da_group); | ||
1858 | struct tcmu_dev *udev = TCMU_DEV(da->da_dev); | ||
1859 | u8 val; | ||
1860 | int ret; | ||
1861 | |||
1862 | ret = kstrtou8(page, 0, &val); | ||
1863 | if (ret < 0) | ||
1864 | return ret; | ||
1865 | |||
1866 | /* Check if device has been configured before */ | ||
1867 | if (tcmu_dev_configured(udev)) { | ||
1868 | ret = tcmu_netlink_event(udev, TCMU_CMD_RECONFIG_DEVICE, | ||
1869 | TCMU_ATTR_WRITECACHE, &val); | ||
1870 | if (ret) { | ||
1871 | pr_err("Unable to reconfigure device\n"); | ||
1872 | return ret; | ||
1873 | } | ||
1874 | } | ||
1875 | |||
1876 | da->emulate_write_cache = val; | ||
1877 | return count; | ||
1878 | } | ||
1879 | CONFIGFS_ATTR(tcmu_, emulate_write_cache); | ||
1880 | |||
1881 | static struct configfs_attribute *tcmu_attrib_attrs[] = { | ||
1882 | &tcmu_attr_cmd_time_out, | ||
1883 | &tcmu_attr_dev_config, | ||
1884 | &tcmu_attr_dev_size, | ||
1885 | &tcmu_attr_emulate_write_cache, | ||
1886 | NULL, | ||
1887 | }; | ||
1888 | |||
1549 | static struct configfs_attribute **tcmu_attrs; | 1889 | static struct configfs_attribute **tcmu_attrs; |
1550 | 1890 | ||
1551 | static struct target_backend_ops tcmu_ops = { | 1891 | static struct target_backend_ops tcmu_ops = { |
@@ -1556,6 +1896,7 @@ static struct target_backend_ops tcmu_ops = { | |||
1556 | .detach_hba = tcmu_detach_hba, | 1896 | .detach_hba = tcmu_detach_hba, |
1557 | .alloc_device = tcmu_alloc_device, | 1897 | .alloc_device = tcmu_alloc_device, |
1558 | .configure_device = tcmu_configure_device, | 1898 | .configure_device = tcmu_configure_device, |
1899 | .destroy_device = tcmu_destroy_device, | ||
1559 | .free_device = tcmu_free_device, | 1900 | .free_device = tcmu_free_device, |
1560 | .parse_cdb = tcmu_parse_cdb, | 1901 | .parse_cdb = tcmu_parse_cdb, |
1561 | .set_configfs_dev_params = tcmu_set_configfs_dev_params, | 1902 | .set_configfs_dev_params = tcmu_set_configfs_dev_params, |
@@ -1573,7 +1914,7 @@ static int unmap_thread_fn(void *data) | |||
1573 | struct page *page; | 1914 | struct page *page; |
1574 | int i; | 1915 | int i; |
1575 | 1916 | ||
1576 | while (1) { | 1917 | while (!kthread_should_stop()) { |
1577 | DEFINE_WAIT(__wait); | 1918 | DEFINE_WAIT(__wait); |
1578 | 1919 | ||
1579 | prepare_to_wait(&unmap_wait, &__wait, TASK_INTERRUPTIBLE); | 1920 | prepare_to_wait(&unmap_wait, &__wait, TASK_INTERRUPTIBLE); |
@@ -1645,7 +1986,7 @@ static int unmap_thread_fn(void *data) | |||
1645 | 1986 | ||
1646 | static int __init tcmu_module_init(void) | 1987 | static int __init tcmu_module_init(void) |
1647 | { | 1988 | { |
1648 | int ret, i, len = 0; | 1989 | int ret, i, k, len = 0; |
1649 | 1990 | ||
1650 | BUILD_BUG_ON((sizeof(struct tcmu_cmd_entry) % TCMU_OP_ALIGN_SIZE) != 0); | 1991 | BUILD_BUG_ON((sizeof(struct tcmu_cmd_entry) % TCMU_OP_ALIGN_SIZE) != 0); |
1651 | 1992 | ||
@@ -1670,7 +2011,10 @@ static int __init tcmu_module_init(void) | |||
1670 | for (i = 0; passthrough_attrib_attrs[i] != NULL; i++) { | 2011 | for (i = 0; passthrough_attrib_attrs[i] != NULL; i++) { |
1671 | len += sizeof(struct configfs_attribute *); | 2012 | len += sizeof(struct configfs_attribute *); |
1672 | } | 2013 | } |
1673 | len += sizeof(struct configfs_attribute *) * 2; | 2014 | for (i = 0; tcmu_attrib_attrs[i] != NULL; i++) { |
2015 | len += sizeof(struct configfs_attribute *); | ||
2016 | } | ||
2017 | len += sizeof(struct configfs_attribute *); | ||
1674 | 2018 | ||
1675 | tcmu_attrs = kzalloc(len, GFP_KERNEL); | 2019 | tcmu_attrs = kzalloc(len, GFP_KERNEL); |
1676 | if (!tcmu_attrs) { | 2020 | if (!tcmu_attrs) { |
@@ -1681,7 +2025,10 @@ static int __init tcmu_module_init(void) | |||
1681 | for (i = 0; passthrough_attrib_attrs[i] != NULL; i++) { | 2025 | for (i = 0; passthrough_attrib_attrs[i] != NULL; i++) { |
1682 | tcmu_attrs[i] = passthrough_attrib_attrs[i]; | 2026 | tcmu_attrs[i] = passthrough_attrib_attrs[i]; |
1683 | } | 2027 | } |
1684 | tcmu_attrs[i] = &tcmu_attr_cmd_time_out; | 2028 | for (k = 0; tcmu_attrib_attrs[k] != NULL; k++) { |
2029 | tcmu_attrs[i] = tcmu_attrib_attrs[k]; | ||
2030 | i++; | ||
2031 | } | ||
1685 | tcmu_ops.tb_dev_attrib_attrs = tcmu_attrs; | 2032 | tcmu_ops.tb_dev_attrib_attrs = tcmu_attrs; |
1686 | 2033 | ||
1687 | ret = transport_backend_register(&tcmu_ops); | 2034 | ret = transport_backend_register(&tcmu_ops); |