diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-10-06 16:38:31 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-10-06 16:38:31 -0400 |
| commit | 13caa8ed93513dd216ede0c84d833e39fc2e3e2a (patch) | |
| tree | 4519ba290cbc69a9a2f701f4a9405d294ec535eb | |
| parent | 831ae3c1df085ad0d0815042469c6f77d42c3754 (diff) | |
| parent | 9e20ae339721d614a1b0768c48bd73b456ff7905 (diff) | |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending
Pull SCSI target fixes from Nicholas Bellinger:
"Here are the outstanding target fixes queued up for v3.12-rc4 code.
The highlights include:
- Make vhost/scsi tag percpu_ida_alloc() use GFP_ATOMIC
- Allow sess_cmd_map allocation failure fallback to use vzalloc
- Fix COMPARE_AND_WRITE se_cmd->data_length bug with FILEIO backends
- Fixes for COMPARE_AND_WRITE callback recursive failure OOPs + non
zero scsi_status bug
- Make iscsi-target do acknowledgement tag release from RX context
- Setup iscsi-target with extra (cmdsn_depth / 2) percpu_ida tags
Also included is a iscsi-target patch CC'ed for v3.10+ that avoids
legacy wait_for_task=true release during fast-past StatSN
acknowledgement, and two other SRP target related patches that address
long-standing issues that are CC'ed for v3.3+.
Extra thanks to Thomas Glanzmann for his testing feedback with
COMPARE_AND_WRITE + EXTENDED_COPY VAAI logic"
* git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending:
iscsi-target; Allow an extra tag_num / 2 number of percpu_ida tags
iscsi-target: Perform release of acknowledged tags from RX context
iscsi-target: Only perform wait_for_tasks when performing shutdown
target: Fail on non zero scsi_status in compare_and_write_callback
target: Fix recursive COMPARE_AND_WRITE callback failure
target: Reset data_length for COMPARE_AND_WRITE to NoLB * block_size
ib_srpt: always set response for task management
target: Fall back to vzalloc upon ->sess_cmd_map kzalloc failure
vhost/scsi: Use GFP_ATOMIC with percpu_ida_alloc for obtaining tag
ib_srpt: Destroy cm_id before destroying QP.
target: Fix xop->dbl assignment in target_xcopy_parse_segdesc_02
| -rw-r--r-- | drivers/infiniband/ulp/srpt/ib_srpt.c | 14 | ||||
| -rw-r--r-- | drivers/target/iscsi/iscsi_target.c | 13 | ||||
| -rw-r--r-- | drivers/target/iscsi/iscsi_target_nego.c | 2 | ||||
| -rw-r--r-- | drivers/target/iscsi/iscsi_target_util.c | 4 | ||||
| -rw-r--r-- | drivers/target/target_core_sbc.c | 28 | ||||
| -rw-r--r-- | drivers/target/target_core_transport.c | 20 | ||||
| -rw-r--r-- | drivers/target/target_core_xcopy.c | 4 | ||||
| -rw-r--r-- | drivers/vhost/scsi.c | 7 |
8 files changed, 67 insertions, 25 deletions
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 653ac6bfc57a..6c923c7039a1 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c | |||
| @@ -1588,7 +1588,7 @@ static int srpt_build_tskmgmt_rsp(struct srpt_rdma_ch *ch, | |||
| 1588 | int resp_data_len; | 1588 | int resp_data_len; |
| 1589 | int resp_len; | 1589 | int resp_len; |
| 1590 | 1590 | ||
| 1591 | resp_data_len = (rsp_code == SRP_TSK_MGMT_SUCCESS) ? 0 : 4; | 1591 | resp_data_len = 4; |
| 1592 | resp_len = sizeof(*srp_rsp) + resp_data_len; | 1592 | resp_len = sizeof(*srp_rsp) + resp_data_len; |
| 1593 | 1593 | ||
| 1594 | srp_rsp = ioctx->ioctx.buf; | 1594 | srp_rsp = ioctx->ioctx.buf; |
| @@ -1600,11 +1600,9 @@ static int srpt_build_tskmgmt_rsp(struct srpt_rdma_ch *ch, | |||
| 1600 | + atomic_xchg(&ch->req_lim_delta, 0)); | 1600 | + atomic_xchg(&ch->req_lim_delta, 0)); |
| 1601 | srp_rsp->tag = tag; | 1601 | srp_rsp->tag = tag; |
| 1602 | 1602 | ||
| 1603 | if (rsp_code != SRP_TSK_MGMT_SUCCESS) { | 1603 | srp_rsp->flags |= SRP_RSP_FLAG_RSPVALID; |
| 1604 | srp_rsp->flags |= SRP_RSP_FLAG_RSPVALID; | 1604 | srp_rsp->resp_data_len = cpu_to_be32(resp_data_len); |
| 1605 | srp_rsp->resp_data_len = cpu_to_be32(resp_data_len); | 1605 | srp_rsp->data[3] = rsp_code; |
| 1606 | srp_rsp->data[3] = rsp_code; | ||
| 1607 | } | ||
| 1608 | 1606 | ||
| 1609 | return resp_len; | 1607 | return resp_len; |
| 1610 | } | 1608 | } |
| @@ -2358,6 +2356,8 @@ static void srpt_release_channel_work(struct work_struct *w) | |||
| 2358 | transport_deregister_session(se_sess); | 2356 | transport_deregister_session(se_sess); |
| 2359 | ch->sess = NULL; | 2357 | ch->sess = NULL; |
| 2360 | 2358 | ||
| 2359 | ib_destroy_cm_id(ch->cm_id); | ||
| 2360 | |||
| 2361 | srpt_destroy_ch_ib(ch); | 2361 | srpt_destroy_ch_ib(ch); |
| 2362 | 2362 | ||
| 2363 | srpt_free_ioctx_ring((struct srpt_ioctx **)ch->ioctx_ring, | 2363 | srpt_free_ioctx_ring((struct srpt_ioctx **)ch->ioctx_ring, |
| @@ -2368,8 +2368,6 @@ static void srpt_release_channel_work(struct work_struct *w) | |||
| 2368 | list_del(&ch->list); | 2368 | list_del(&ch->list); |
| 2369 | spin_unlock_irq(&sdev->spinlock); | 2369 | spin_unlock_irq(&sdev->spinlock); |
| 2370 | 2370 | ||
| 2371 | ib_destroy_cm_id(ch->cm_id); | ||
| 2372 | |||
| 2373 | if (ch->release_done) | 2371 | if (ch->release_done) |
| 2374 | complete(ch->release_done); | 2372 | complete(ch->release_done); |
| 2375 | 2373 | ||
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 35b61f7d6c63..38e44b9abf0f 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c | |||
| @@ -753,7 +753,8 @@ static void iscsit_unmap_iovec(struct iscsi_cmd *cmd) | |||
| 753 | 753 | ||
| 754 | static void iscsit_ack_from_expstatsn(struct iscsi_conn *conn, u32 exp_statsn) | 754 | static void iscsit_ack_from_expstatsn(struct iscsi_conn *conn, u32 exp_statsn) |
| 755 | { | 755 | { |
| 756 | struct iscsi_cmd *cmd; | 756 | LIST_HEAD(ack_list); |
| 757 | struct iscsi_cmd *cmd, *cmd_p; | ||
| 757 | 758 | ||
| 758 | conn->exp_statsn = exp_statsn; | 759 | conn->exp_statsn = exp_statsn; |
| 759 | 760 | ||
| @@ -761,19 +762,23 @@ static void iscsit_ack_from_expstatsn(struct iscsi_conn *conn, u32 exp_statsn) | |||
| 761 | return; | 762 | return; |
| 762 | 763 | ||
| 763 | spin_lock_bh(&conn->cmd_lock); | 764 | spin_lock_bh(&conn->cmd_lock); |
| 764 | list_for_each_entry(cmd, &conn->conn_cmd_list, i_conn_node) { | 765 | list_for_each_entry_safe(cmd, cmd_p, &conn->conn_cmd_list, i_conn_node) { |
| 765 | spin_lock(&cmd->istate_lock); | 766 | spin_lock(&cmd->istate_lock); |
| 766 | if ((cmd->i_state == ISTATE_SENT_STATUS) && | 767 | if ((cmd->i_state == ISTATE_SENT_STATUS) && |
| 767 | iscsi_sna_lt(cmd->stat_sn, exp_statsn)) { | 768 | iscsi_sna_lt(cmd->stat_sn, exp_statsn)) { |
| 768 | cmd->i_state = ISTATE_REMOVE; | 769 | cmd->i_state = ISTATE_REMOVE; |
| 769 | spin_unlock(&cmd->istate_lock); | 770 | spin_unlock(&cmd->istate_lock); |
| 770 | iscsit_add_cmd_to_immediate_queue(cmd, conn, | 771 | list_move_tail(&cmd->i_conn_node, &ack_list); |
| 771 | cmd->i_state); | ||
| 772 | continue; | 772 | continue; |
| 773 | } | 773 | } |
| 774 | spin_unlock(&cmd->istate_lock); | 774 | spin_unlock(&cmd->istate_lock); |
| 775 | } | 775 | } |
| 776 | spin_unlock_bh(&conn->cmd_lock); | 776 | spin_unlock_bh(&conn->cmd_lock); |
| 777 | |||
| 778 | list_for_each_entry_safe(cmd, cmd_p, &ack_list, i_conn_node) { | ||
| 779 | list_del(&cmd->i_conn_node); | ||
| 780 | iscsit_free_cmd(cmd, false); | ||
| 781 | } | ||
| 777 | } | 782 | } |
| 778 | 783 | ||
| 779 | static int iscsit_allocate_iovecs(struct iscsi_cmd *cmd) | 784 | static int iscsit_allocate_iovecs(struct iscsi_cmd *cmd) |
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index 14d1aed5af1d..ef6d836a4d09 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c | |||
| @@ -1192,7 +1192,7 @@ get_target: | |||
| 1192 | */ | 1192 | */ |
| 1193 | alloc_tags: | 1193 | alloc_tags: |
| 1194 | tag_num = max_t(u32, ISCSIT_MIN_TAGS, queue_depth); | 1194 | tag_num = max_t(u32, ISCSIT_MIN_TAGS, queue_depth); |
| 1195 | tag_num += ISCSIT_EXTRA_TAGS; | 1195 | tag_num += (tag_num / 2) + ISCSIT_EXTRA_TAGS; |
| 1196 | tag_size = sizeof(struct iscsi_cmd) + conn->conn_transport->priv_size; | 1196 | tag_size = sizeof(struct iscsi_cmd) + conn->conn_transport->priv_size; |
| 1197 | 1197 | ||
| 1198 | ret = transport_alloc_session_tags(sess->se_sess, tag_num, tag_size); | 1198 | ret = transport_alloc_session_tags(sess->se_sess, tag_num, tag_size); |
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index f2de28e178fd..b0cac0c342e1 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c | |||
| @@ -736,7 +736,7 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd, bool shutdown) | |||
| 736 | * Fallthrough | 736 | * Fallthrough |
| 737 | */ | 737 | */ |
| 738 | case ISCSI_OP_SCSI_TMFUNC: | 738 | case ISCSI_OP_SCSI_TMFUNC: |
| 739 | rc = transport_generic_free_cmd(&cmd->se_cmd, 1); | 739 | rc = transport_generic_free_cmd(&cmd->se_cmd, shutdown); |
| 740 | if (!rc && shutdown && se_cmd && se_cmd->se_sess) { | 740 | if (!rc && shutdown && se_cmd && se_cmd->se_sess) { |
| 741 | __iscsit_free_cmd(cmd, true, shutdown); | 741 | __iscsit_free_cmd(cmd, true, shutdown); |
| 742 | target_put_sess_cmd(se_cmd->se_sess, se_cmd); | 742 | target_put_sess_cmd(se_cmd->se_sess, se_cmd); |
| @@ -752,7 +752,7 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd, bool shutdown) | |||
| 752 | se_cmd = &cmd->se_cmd; | 752 | se_cmd = &cmd->se_cmd; |
| 753 | __iscsit_free_cmd(cmd, true, shutdown); | 753 | __iscsit_free_cmd(cmd, true, shutdown); |
| 754 | 754 | ||
| 755 | rc = transport_generic_free_cmd(&cmd->se_cmd, 1); | 755 | rc = transport_generic_free_cmd(&cmd->se_cmd, shutdown); |
| 756 | if (!rc && shutdown && se_cmd->se_sess) { | 756 | if (!rc && shutdown && se_cmd->se_sess) { |
| 757 | __iscsit_free_cmd(cmd, true, shutdown); | 757 | __iscsit_free_cmd(cmd, true, shutdown); |
| 758 | target_put_sess_cmd(se_cmd->se_sess, se_cmd); | 758 | target_put_sess_cmd(se_cmd->se_sess, se_cmd); |
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 6c17295e8d7c..4714c6f8da4b 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c | |||
| @@ -349,7 +349,16 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd) | |||
| 349 | { | 349 | { |
| 350 | struct se_device *dev = cmd->se_dev; | 350 | struct se_device *dev = cmd->se_dev; |
| 351 | 351 | ||
| 352 | cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST; | 352 | /* |
| 353 | * Only set SCF_COMPARE_AND_WRITE_POST to force a response fall-through | ||
| 354 | * within target_complete_ok_work() if the command was successfully | ||
| 355 | * sent to the backend driver. | ||
| 356 | */ | ||
| 357 | spin_lock_irq(&cmd->t_state_lock); | ||
| 358 | if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status) | ||
| 359 | cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST; | ||
| 360 | spin_unlock_irq(&cmd->t_state_lock); | ||
| 361 | |||
| 353 | /* | 362 | /* |
| 354 | * Unlock ->caw_sem originally obtained during sbc_compare_and_write() | 363 | * Unlock ->caw_sem originally obtained during sbc_compare_and_write() |
| 355 | * before the original READ I/O submission. | 364 | * before the original READ I/O submission. |
| @@ -363,7 +372,7 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd) | |||
| 363 | { | 372 | { |
| 364 | struct se_device *dev = cmd->se_dev; | 373 | struct se_device *dev = cmd->se_dev; |
| 365 | struct scatterlist *write_sg = NULL, *sg; | 374 | struct scatterlist *write_sg = NULL, *sg; |
| 366 | unsigned char *buf, *addr; | 375 | unsigned char *buf = NULL, *addr; |
| 367 | struct sg_mapping_iter m; | 376 | struct sg_mapping_iter m; |
| 368 | unsigned int offset = 0, len; | 377 | unsigned int offset = 0, len; |
| 369 | unsigned int nlbas = cmd->t_task_nolb; | 378 | unsigned int nlbas = cmd->t_task_nolb; |
| @@ -378,6 +387,15 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd) | |||
| 378 | */ | 387 | */ |
| 379 | if (!cmd->t_data_sg || !cmd->t_bidi_data_sg) | 388 | if (!cmd->t_data_sg || !cmd->t_bidi_data_sg) |
| 380 | return TCM_NO_SENSE; | 389 | return TCM_NO_SENSE; |
| 390 | /* | ||
| 391 | * Immediately exit + release dev->caw_sem if command has already | ||
| 392 | * been failed with a non-zero SCSI status. | ||
| 393 | */ | ||
| 394 | if (cmd->scsi_status) { | ||
| 395 | pr_err("compare_and_write_callback: non zero scsi_status:" | ||
| 396 | " 0x%02x\n", cmd->scsi_status); | ||
| 397 | goto out; | ||
| 398 | } | ||
| 381 | 399 | ||
| 382 | buf = kzalloc(cmd->data_length, GFP_KERNEL); | 400 | buf = kzalloc(cmd->data_length, GFP_KERNEL); |
| 383 | if (!buf) { | 401 | if (!buf) { |
| @@ -508,6 +526,12 @@ sbc_compare_and_write(struct se_cmd *cmd) | |||
| 508 | cmd->transport_complete_callback = NULL; | 526 | cmd->transport_complete_callback = NULL; |
| 509 | return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | 527 | return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; |
| 510 | } | 528 | } |
| 529 | /* | ||
| 530 | * Reset cmd->data_length to individual block_size in order to not | ||
| 531 | * confuse backend drivers that depend on this value matching the | ||
| 532 | * size of the I/O being submitted. | ||
| 533 | */ | ||
| 534 | cmd->data_length = cmd->t_task_nolb * dev->dev_attrib.block_size; | ||
| 511 | 535 | ||
| 512 | ret = cmd->execute_rw(cmd, cmd->t_bidi_data_sg, cmd->t_bidi_data_nents, | 536 | ret = cmd->execute_rw(cmd, cmd->t_bidi_data_sg, cmd->t_bidi_data_nents, |
| 513 | DMA_FROM_DEVICE); | 537 | DMA_FROM_DEVICE); |
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 84747cc1aac0..81e945eefbbd 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c | |||
| @@ -236,17 +236,24 @@ int transport_alloc_session_tags(struct se_session *se_sess, | |||
| 236 | { | 236 | { |
| 237 | int rc; | 237 | int rc; |
| 238 | 238 | ||
| 239 | se_sess->sess_cmd_map = kzalloc(tag_num * tag_size, GFP_KERNEL); | 239 | se_sess->sess_cmd_map = kzalloc(tag_num * tag_size, |
| 240 | GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT); | ||
| 240 | if (!se_sess->sess_cmd_map) { | 241 | if (!se_sess->sess_cmd_map) { |
| 241 | pr_err("Unable to allocate se_sess->sess_cmd_map\n"); | 242 | se_sess->sess_cmd_map = vzalloc(tag_num * tag_size); |
| 242 | return -ENOMEM; | 243 | if (!se_sess->sess_cmd_map) { |
| 244 | pr_err("Unable to allocate se_sess->sess_cmd_map\n"); | ||
| 245 | return -ENOMEM; | ||
| 246 | } | ||
| 243 | } | 247 | } |
| 244 | 248 | ||
| 245 | rc = percpu_ida_init(&se_sess->sess_tag_pool, tag_num); | 249 | rc = percpu_ida_init(&se_sess->sess_tag_pool, tag_num); |
| 246 | if (rc < 0) { | 250 | if (rc < 0) { |
| 247 | pr_err("Unable to init se_sess->sess_tag_pool," | 251 | pr_err("Unable to init se_sess->sess_tag_pool," |
| 248 | " tag_num: %u\n", tag_num); | 252 | " tag_num: %u\n", tag_num); |
| 249 | kfree(se_sess->sess_cmd_map); | 253 | if (is_vmalloc_addr(se_sess->sess_cmd_map)) |
| 254 | vfree(se_sess->sess_cmd_map); | ||
| 255 | else | ||
| 256 | kfree(se_sess->sess_cmd_map); | ||
| 250 | se_sess->sess_cmd_map = NULL; | 257 | se_sess->sess_cmd_map = NULL; |
| 251 | return -ENOMEM; | 258 | return -ENOMEM; |
| 252 | } | 259 | } |
| @@ -412,7 +419,10 @@ void transport_free_session(struct se_session *se_sess) | |||
| 412 | { | 419 | { |
| 413 | if (se_sess->sess_cmd_map) { | 420 | if (se_sess->sess_cmd_map) { |
| 414 | percpu_ida_destroy(&se_sess->sess_tag_pool); | 421 | percpu_ida_destroy(&se_sess->sess_tag_pool); |
| 415 | kfree(se_sess->sess_cmd_map); | 422 | if (is_vmalloc_addr(se_sess->sess_cmd_map)) |
| 423 | vfree(se_sess->sess_cmd_map); | ||
| 424 | else | ||
| 425 | kfree(se_sess->sess_cmd_map); | ||
| 416 | } | 426 | } |
| 417 | kmem_cache_free(se_sess_cache, se_sess); | 427 | kmem_cache_free(se_sess_cache, se_sess); |
| 418 | } | 428 | } |
diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c index 4d22e7d2adca..3da4fd10b9f8 100644 --- a/drivers/target/target_core_xcopy.c +++ b/drivers/target/target_core_xcopy.c | |||
| @@ -298,8 +298,8 @@ static int target_xcopy_parse_segdesc_02(struct se_cmd *se_cmd, struct xcopy_op | |||
| 298 | (unsigned long long)xop->dst_lba); | 298 | (unsigned long long)xop->dst_lba); |
| 299 | 299 | ||
| 300 | if (dc != 0) { | 300 | if (dc != 0) { |
| 301 | xop->dbl = (desc[29] << 16) & 0xff; | 301 | xop->dbl = (desc[29] & 0xff) << 16; |
| 302 | xop->dbl |= (desc[30] << 8) & 0xff; | 302 | xop->dbl |= (desc[30] & 0xff) << 8; |
| 303 | xop->dbl |= desc[31] & 0xff; | 303 | xop->dbl |= desc[31] & 0xff; |
| 304 | 304 | ||
| 305 | pr_debug("XCOPY seg desc 0x02: DC=1 w/ dbl: %u\n", xop->dbl); | 305 | pr_debug("XCOPY seg desc 0x02: DC=1 w/ dbl: %u\n", xop->dbl); |
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 592b31698fc8..ce5221fa393a 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c | |||
| @@ -728,7 +728,12 @@ vhost_scsi_get_tag(struct vhost_virtqueue *vq, | |||
| 728 | } | 728 | } |
| 729 | se_sess = tv_nexus->tvn_se_sess; | 729 | se_sess = tv_nexus->tvn_se_sess; |
| 730 | 730 | ||
| 731 | tag = percpu_ida_alloc(&se_sess->sess_tag_pool, GFP_KERNEL); | 731 | tag = percpu_ida_alloc(&se_sess->sess_tag_pool, GFP_ATOMIC); |
| 732 | if (tag < 0) { | ||
| 733 | pr_err("Unable to obtain tag for tcm_vhost_cmd\n"); | ||
| 734 | return ERR_PTR(-ENOMEM); | ||
| 735 | } | ||
| 736 | |||
| 732 | cmd = &((struct tcm_vhost_cmd *)se_sess->sess_cmd_map)[tag]; | 737 | cmd = &((struct tcm_vhost_cmd *)se_sess->sess_cmd_map)[tag]; |
| 733 | sg = cmd->tvc_sgl; | 738 | sg = cmd->tvc_sgl; |
| 734 | pages = cmd->tvc_upages; | 739 | pages = cmd->tvc_upages; |
