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

CAPI: Rework locking of controller data structures

This patch applies the mutex so far only protecting the controller list
to (almost) all accesses of controller data structures. It also reworks
waiting on state changes in old_capi_manufacturer so that it no longer
poll and holds a module reference to the controller owner while waiting
(the latter was partly done already). Modification and checking of the
blocked state remains racy by design, the caller is responsible for
dealing with this.

Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Jan Kiszka and committed by
David S. Miller
0ca3a017 ef69bb2e

+221 -93
+209 -90
drivers/isdn/capi/kcapi.c
··· 61 61 LIST_HEAD(capi_drivers); 62 62 DEFINE_MUTEX(capi_drivers_lock); 63 63 64 + struct capi_ctr *capi_controller[CAPI_MAXCONTR]; 65 + DEFINE_MUTEX(capi_controller_lock); 66 + 64 67 static DEFINE_RWLOCK(application_lock); 65 - static DEFINE_MUTEX(controller_mutex); 66 68 67 69 struct capi20_appl *capi_applications[CAPI_MAXAPPL]; 68 - struct capi_ctr *capi_controller[CAPI_MAXCONTR]; 69 70 70 71 static int ncontrollers; 71 72 ··· 172 171 struct capi_ctr *ctr; 173 172 u16 applid; 174 173 174 + mutex_lock(&capi_controller_lock); 175 + 175 176 if (showcapimsgs & 1) 176 177 printk(KERN_DEBUG "kcapi: notify up contr %d\n", contr); 177 178 178 179 ctr = get_capi_ctr_by_nr(contr); 179 180 if (ctr) { 180 181 if (ctr->state == CAPI_CTR_RUNNING) 181 - return; 182 + goto unlock_out; 182 183 183 184 ctr->state = CAPI_CTR_RUNNING; 184 185 ··· 190 187 continue; 191 188 register_appl(ctr, applid, &ap->rparam); 192 189 } 190 + 191 + wake_up_interruptible_all(&ctr->state_wait_queue); 193 192 } else 194 193 printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr); 194 + 195 + unlock_out: 196 + mutex_unlock(&capi_controller_lock); 195 197 } 196 198 197 - static void ctr_down(struct capi_ctr *ctr) 199 + static void ctr_down(struct capi_ctr *ctr, int new_state) 198 200 { 199 201 struct capi20_appl *ap; 200 202 u16 applid; 201 203 202 - if (ctr->state == CAPI_CTR_DETECTED) 204 + if (ctr->state == CAPI_CTR_DETECTED || ctr->state == CAPI_CTR_DETACHED) 203 205 return; 204 206 205 - ctr->state = CAPI_CTR_DETECTED; 207 + ctr->state = new_state; 206 208 207 209 memset(ctr->manu, 0, sizeof(ctr->manu)); 208 210 memset(&ctr->version, 0, sizeof(ctr->version)); ··· 219 211 if (ap && !ap->release_in_progress) 220 212 capi_ctr_put(ctr); 221 213 } 214 + 215 + wake_up_interruptible_all(&ctr->state_wait_queue); 222 216 } 223 217 224 218 static void notify_down(u32 contr) 225 219 { 226 220 struct capi_ctr *ctr; 227 221 222 + mutex_lock(&capi_controller_lock); 223 + 228 224 if (showcapimsgs & 1) 229 225 printk(KERN_DEBUG "kcapi: notify down contr %d\n", contr); 230 226 231 227 ctr = get_capi_ctr_by_nr(contr); 232 228 if (ctr) 233 - ctr_down(ctr); 229 + ctr_down(ctr, CAPI_CTR_DETECTED); 234 230 else 235 231 printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr); 232 + 233 + mutex_unlock(&capi_controller_lock); 236 234 } 237 235 238 236 static int ··· 450 436 * @ctr: controller descriptor structure. 451 437 * 452 438 * Called by hardware driver to stop data flow. 439 + * 440 + * Note: The caller is responsible for synchronizing concurrent state changes 441 + * as well as invocations of capi_ctr_handle_message. 453 442 */ 454 443 455 444 void capi_ctr_suspend_output(struct capi_ctr *ctr) ··· 471 454 * @ctr: controller descriptor structure. 472 455 * 473 456 * Called by hardware driver to resume data flow. 457 + * 458 + * Note: The caller is responsible for synchronizing concurrent state changes 459 + * as well as invocations of capi_ctr_handle_message. 474 460 */ 475 461 476 462 void capi_ctr_resume_output(struct capi_ctr *ctr) ··· 501 481 { 502 482 int i; 503 483 504 - mutex_lock(&controller_mutex); 484 + mutex_lock(&capi_controller_lock); 505 485 506 486 for (i = 0; i < CAPI_MAXCONTR; i++) { 507 487 if (!capi_controller[i]) 508 488 break; 509 489 } 510 490 if (i == CAPI_MAXCONTR) { 511 - mutex_unlock(&controller_mutex); 491 + mutex_unlock(&capi_controller_lock); 512 492 printk(KERN_ERR "kcapi: out of controller slots\n"); 513 493 return -EBUSY; 514 494 } 515 495 capi_controller[i] = ctr; 516 - 517 - mutex_unlock(&controller_mutex); 518 496 519 497 ctr->nrecvctlpkt = 0; 520 498 ctr->nrecvdatapkt = 0; ··· 522 504 ctr->state = CAPI_CTR_DETECTED; 523 505 ctr->blocked = 0; 524 506 ctr->traceflag = showcapimsgs; 507 + init_waitqueue_head(&ctr->state_wait_queue); 525 508 526 509 sprintf(ctr->procfn, "capi/controllers/%d", ctr->cnr); 527 510 ctr->procent = proc_create_data(ctr->procfn, 0, NULL, ctr->proc_fops, ctr); 528 511 529 512 ncontrollers++; 513 + 514 + mutex_unlock(&capi_controller_lock); 515 + 530 516 printk(KERN_NOTICE "kcapi: controller [%03d]: %s attached\n", 531 517 ctr->cnr, ctr->name); 532 518 return 0; ··· 549 527 550 528 int detach_capi_ctr(struct capi_ctr *ctr) 551 529 { 552 - ctr_down(ctr); 530 + int err = 0; 553 531 554 - ncontrollers--; 532 + mutex_lock(&capi_controller_lock); 555 533 556 - if (ctr->procent) { 557 - remove_proc_entry(ctr->procfn, NULL); 558 - ctr->procent = NULL; 534 + ctr_down(ctr, CAPI_CTR_DETACHED); 535 + 536 + if (capi_controller[ctr->cnr - 1] != ctr) { 537 + err = -EINVAL; 538 + goto unlock_out; 559 539 } 560 540 capi_controller[ctr->cnr - 1] = NULL; 541 + ncontrollers--; 542 + 543 + if (ctr->procent) 544 + remove_proc_entry(ctr->procfn, NULL); 545 + 561 546 printk(KERN_NOTICE "kcapi: controller [%03d]: %s unregistered\n", 562 547 ctr->cnr, ctr->name); 563 548 564 - return 0; 549 + unlock_out: 550 + mutex_unlock(&capi_controller_lock); 551 + 552 + return err; 565 553 } 566 554 567 555 EXPORT_SYMBOL(detach_capi_ctr); ··· 621 589 622 590 u16 capi20_isinstalled(void) 623 591 { 592 + u16 ret = CAPI_REGNOTINSTALLED; 624 593 int i; 625 - for (i = 0; i < CAPI_MAXCONTR; i++) { 594 + 595 + mutex_lock(&capi_controller_lock); 596 + 597 + for (i = 0; i < CAPI_MAXCONTR; i++) 626 598 if (capi_controller[i] && 627 - capi_controller[i]->state == CAPI_CTR_RUNNING) 628 - return CAPI_NOERROR; 629 - } 630 - return CAPI_REGNOTINSTALLED; 599 + capi_controller[i]->state == CAPI_CTR_RUNNING) { 600 + ret = CAPI_NOERROR; 601 + break; 602 + } 603 + 604 + mutex_unlock(&capi_controller_lock); 605 + 606 + return ret; 631 607 } 632 608 633 609 EXPORT_SYMBOL(capi20_isinstalled); ··· 688 648 689 649 write_unlock_irqrestore(&application_lock, flags); 690 650 691 - mutex_lock(&controller_mutex); 651 + mutex_lock(&capi_controller_lock); 652 + 692 653 for (i = 0; i < CAPI_MAXCONTR; i++) { 693 654 if (!capi_controller[i] || 694 655 capi_controller[i]->state != CAPI_CTR_RUNNING) 695 656 continue; 696 657 register_appl(capi_controller[i], applid, &ap->rparam); 697 658 } 698 - mutex_unlock(&controller_mutex); 659 + 660 + mutex_unlock(&capi_controller_lock); 699 661 700 662 if (showcapimsgs & 1) { 701 663 printk(KERN_DEBUG "kcapi: appl %d up\n", applid); ··· 730 688 capi_applications[ap->applid - 1] = NULL; 731 689 write_unlock_irqrestore(&application_lock, flags); 732 690 733 - mutex_lock(&controller_mutex); 691 + mutex_lock(&capi_controller_lock); 692 + 734 693 for (i = 0; i < CAPI_MAXCONTR; i++) { 735 694 if (!capi_controller[i] || 736 695 capi_controller[i]->state != CAPI_CTR_RUNNING) 737 696 continue; 738 697 release_appl(capi_controller[i], ap->applid); 739 698 } 740 - mutex_unlock(&controller_mutex); 699 + 700 + mutex_unlock(&capi_controller_lock); 741 701 742 702 flush_scheduled_work(); 743 703 skb_queue_purge(&ap->recv_queue); ··· 778 734 || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data)) 779 735 || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data))) 780 736 return CAPI_ILLCMDORSUBCMDORMSGTOSMALL; 737 + 738 + /* 739 + * The controller reference is protected by the existence of the 740 + * application passed to us. We assume that the caller properly 741 + * synchronizes this service with capi20_release. 742 + */ 781 743 ctr = get_capi_ctr_by_nr(CAPIMSG_CONTROLLER(skb->data)); 782 744 if (!ctr || ctr->state != CAPI_CTR_RUNNING) { 783 745 ctr = get_capi_ctr_by_nr(1); /* XXX why? */ ··· 848 798 u16 capi20_get_manufacturer(u32 contr, u8 *buf) 849 799 { 850 800 struct capi_ctr *ctr; 801 + u16 ret; 851 802 852 803 if (contr == 0) { 853 804 strlcpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN); 854 805 return CAPI_NOERROR; 855 806 } 807 + 808 + mutex_lock(&capi_controller_lock); 809 + 856 810 ctr = get_capi_ctr_by_nr(contr); 857 - if (!ctr || ctr->state != CAPI_CTR_RUNNING) 858 - return CAPI_REGNOTINSTALLED; 859 - strlcpy(buf, ctr->manu, CAPI_MANUFACTURER_LEN); 860 - return CAPI_NOERROR; 811 + if (ctr && ctr->state == CAPI_CTR_RUNNING) { 812 + strlcpy(buf, ctr->manu, CAPI_MANUFACTURER_LEN); 813 + ret = CAPI_NOERROR; 814 + } else 815 + ret = CAPI_REGNOTINSTALLED; 816 + 817 + mutex_unlock(&capi_controller_lock); 818 + return ret; 861 819 } 862 820 863 821 EXPORT_SYMBOL(capi20_get_manufacturer); ··· 883 825 u16 capi20_get_version(u32 contr, struct capi_version *verp) 884 826 { 885 827 struct capi_ctr *ctr; 828 + u16 ret; 886 829 887 830 if (contr == 0) { 888 831 *verp = driver_version; 889 832 return CAPI_NOERROR; 890 833 } 891 - ctr = get_capi_ctr_by_nr(contr); 892 - if (!ctr || ctr->state != CAPI_CTR_RUNNING) 893 - return CAPI_REGNOTINSTALLED; 894 834 895 - memcpy(verp, &ctr->version, sizeof(capi_version)); 896 - return CAPI_NOERROR; 835 + mutex_lock(&capi_controller_lock); 836 + 837 + ctr = get_capi_ctr_by_nr(contr); 838 + if (ctr && ctr->state == CAPI_CTR_RUNNING) { 839 + memcpy(verp, &ctr->version, sizeof(capi_version)); 840 + ret = CAPI_NOERROR; 841 + } else 842 + ret = CAPI_REGNOTINSTALLED; 843 + 844 + mutex_unlock(&capi_controller_lock); 845 + return ret; 897 846 } 898 847 899 848 EXPORT_SYMBOL(capi20_get_version); ··· 918 853 u16 capi20_get_serial(u32 contr, u8 *serial) 919 854 { 920 855 struct capi_ctr *ctr; 856 + u16 ret; 921 857 922 858 if (contr == 0) { 923 859 strlcpy(serial, driver_serial, CAPI_SERIAL_LEN); 924 860 return CAPI_NOERROR; 925 861 } 926 - ctr = get_capi_ctr_by_nr(contr); 927 - if (!ctr || ctr->state != CAPI_CTR_RUNNING) 928 - return CAPI_REGNOTINSTALLED; 929 862 930 - strlcpy(serial, ctr->serial, CAPI_SERIAL_LEN); 931 - return CAPI_NOERROR; 863 + mutex_lock(&capi_controller_lock); 864 + 865 + ctr = get_capi_ctr_by_nr(contr); 866 + if (ctr && ctr->state == CAPI_CTR_RUNNING) { 867 + strlcpy(serial, ctr->serial, CAPI_SERIAL_LEN); 868 + ret = CAPI_NOERROR; 869 + } else 870 + ret = CAPI_REGNOTINSTALLED; 871 + 872 + mutex_unlock(&capi_controller_lock); 873 + return ret; 932 874 } 933 875 934 876 EXPORT_SYMBOL(capi20_get_serial); ··· 953 881 u16 capi20_get_profile(u32 contr, struct capi_profile *profp) 954 882 { 955 883 struct capi_ctr *ctr; 884 + u16 ret; 956 885 957 886 if (contr == 0) { 958 887 profp->ncontroller = ncontrollers; 959 888 return CAPI_NOERROR; 960 889 } 961 - ctr = get_capi_ctr_by_nr(contr); 962 - if (!ctr || ctr->state != CAPI_CTR_RUNNING) 963 - return CAPI_REGNOTINSTALLED; 964 890 965 - memcpy(profp, &ctr->profile, sizeof(struct capi_profile)); 966 - return CAPI_NOERROR; 891 + mutex_lock(&capi_controller_lock); 892 + 893 + ctr = get_capi_ctr_by_nr(contr); 894 + if (ctr && ctr->state == CAPI_CTR_RUNNING) { 895 + memcpy(profp, &ctr->profile, sizeof(struct capi_profile)); 896 + ret = CAPI_NOERROR; 897 + } else 898 + ret = CAPI_REGNOTINSTALLED; 899 + 900 + mutex_unlock(&capi_controller_lock); 901 + return ret; 967 902 } 968 903 969 904 EXPORT_SYMBOL(capi20_get_profile); 905 + 906 + /* Must be called with capi_controller_lock held. */ 907 + static int wait_on_ctr_state(struct capi_ctr *ctr, unsigned int state) 908 + { 909 + DEFINE_WAIT(wait); 910 + int retval = 0; 911 + 912 + ctr = capi_ctr_get(ctr); 913 + if (!ctr) 914 + return -ESRCH; 915 + 916 + for (;;) { 917 + prepare_to_wait(&ctr->state_wait_queue, &wait, 918 + TASK_INTERRUPTIBLE); 919 + 920 + if (ctr->state == state) 921 + break; 922 + if (ctr->state == CAPI_CTR_DETACHED) { 923 + retval = -ESRCH; 924 + break; 925 + } 926 + if (signal_pending(current)) { 927 + retval = -EINTR; 928 + break; 929 + } 930 + 931 + mutex_unlock(&capi_controller_lock); 932 + schedule(); 933 + mutex_lock(&capi_controller_lock); 934 + } 935 + finish_wait(&ctr->state_wait_queue, &wait); 936 + 937 + capi_ctr_put(ctr); 938 + 939 + return retval; 940 + } 970 941 971 942 #ifdef AVMB1_COMPAT 972 943 static int old_capi_manufacturer(unsigned int cmd, void __user *data) ··· 1088 973 sizeof(avmb1_loadandconfigdef))) 1089 974 return -EFAULT; 1090 975 } 976 + 977 + mutex_lock(&capi_controller_lock); 978 + 1091 979 ctr = get_capi_ctr_by_nr(ldef.contr); 1092 - if (!ctr) 1093 - return -EINVAL; 1094 - ctr = capi_ctr_get(ctr); 1095 - if (!ctr) 1096 - return -ESRCH; 980 + if (!ctr) { 981 + retval = -EINVAL; 982 + goto load_unlock_out; 983 + } 984 + 1097 985 if (ctr->load_firmware == NULL) { 1098 986 printk(KERN_DEBUG "kcapi: load: no load function\n"); 1099 - capi_ctr_put(ctr); 1100 - return -ESRCH; 987 + retval = -ESRCH; 988 + goto load_unlock_out; 1101 989 } 1102 990 1103 991 if (ldef.t4file.len <= 0) { 1104 992 printk(KERN_DEBUG "kcapi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len); 1105 - capi_ctr_put(ctr); 1106 - return -EINVAL; 993 + retval = -EINVAL; 994 + goto load_unlock_out; 1107 995 } 1108 996 if (ldef.t4file.data == NULL) { 1109 997 printk(KERN_DEBUG "kcapi: load: invalid parameter: dataptr is 0\n"); 1110 - capi_ctr_put(ctr); 1111 - return -EINVAL; 998 + retval = -EINVAL; 999 + goto load_unlock_out; 1112 1000 } 1113 1001 1114 1002 ldata.firmware.user = 1; ··· 1123 1005 1124 1006 if (ctr->state != CAPI_CTR_DETECTED) { 1125 1007 printk(KERN_INFO "kcapi: load: contr=%d not in detect state\n", ldef.contr); 1126 - capi_ctr_put(ctr); 1127 - return -EBUSY; 1008 + retval = -EBUSY; 1009 + goto load_unlock_out; 1128 1010 } 1129 1011 ctr->state = CAPI_CTR_LOADING; 1130 1012 1131 1013 retval = ctr->load_firmware(ctr, &ldata); 1132 - 1133 1014 if (retval) { 1134 1015 ctr->state = CAPI_CTR_DETECTED; 1135 - capi_ctr_put(ctr); 1136 - return retval; 1016 + goto load_unlock_out; 1137 1017 } 1138 1018 1139 - while (ctr->state != CAPI_CTR_RUNNING) { 1019 + retval = wait_on_ctr_state(ctr, CAPI_CTR_RUNNING); 1140 1020 1141 - msleep_interruptible(100); /* 0.1 sec */ 1142 - 1143 - if (signal_pending(current)) { 1144 - capi_ctr_put(ctr); 1145 - return -EINTR; 1146 - } 1147 - } 1148 - capi_ctr_put(ctr); 1149 - return 0; 1021 + load_unlock_out: 1022 + mutex_unlock(&capi_controller_lock); 1023 + return retval; 1150 1024 1151 1025 case AVMB1_RESETCARD: 1152 1026 if (copy_from_user(&rdef, data, sizeof(avmb1_resetdef))) 1153 1027 return -EFAULT; 1028 + 1029 + retval = 0; 1030 + 1031 + mutex_lock(&capi_controller_lock); 1032 + 1154 1033 ctr = get_capi_ctr_by_nr(rdef.contr); 1155 - if (!ctr) 1156 - return -ESRCH; 1034 + if (!ctr) { 1035 + retval = -ESRCH; 1036 + goto reset_unlock_out; 1037 + } 1157 1038 1158 1039 if (ctr->state == CAPI_CTR_DETECTED) 1159 - return 0; 1040 + goto reset_unlock_out; 1160 1041 1161 1042 ctr->reset_ctr(ctr); 1162 1043 1163 - while (ctr->state > CAPI_CTR_DETECTED) { 1044 + retval = wait_on_ctr_state(ctr, CAPI_CTR_DETECTED); 1164 1045 1165 - msleep_interruptible(100); /* 0.1 sec */ 1166 - 1167 - if (signal_pending(current)) 1168 - return -EINTR; 1169 - } 1170 - return 0; 1171 - 1046 + reset_unlock_out: 1047 + mutex_unlock(&capi_controller_lock); 1048 + return retval; 1172 1049 } 1173 1050 return -EINVAL; 1174 1051 } ··· 1181 1068 int capi20_manufacturer(unsigned int cmd, void __user *data) 1182 1069 { 1183 1070 struct capi_ctr *ctr; 1071 + int retval; 1184 1072 1185 1073 switch (cmd) { 1186 1074 #ifdef AVMB1_COMPAT ··· 1199 1085 if (copy_from_user(&fdef, data, sizeof(kcapi_flagdef))) 1200 1086 return -EFAULT; 1201 1087 1202 - ctr = get_capi_ctr_by_nr(fdef.contr); 1203 - if (!ctr) 1204 - return -ESRCH; 1088 + mutex_lock(&capi_controller_lock); 1205 1089 1206 - ctr->traceflag = fdef.flag; 1207 - printk(KERN_INFO "kcapi: contr [%03d] set trace=%d\n", 1208 - ctr->cnr, ctr->traceflag); 1209 - return 0; 1090 + ctr = get_capi_ctr_by_nr(fdef.contr); 1091 + if (ctr) { 1092 + ctr->traceflag = fdef.flag; 1093 + printk(KERN_INFO "kcapi: contr [%03d] set trace=%d\n", 1094 + ctr->cnr, ctr->traceflag); 1095 + retval = 0; 1096 + } else 1097 + retval = -ESRCH; 1098 + 1099 + mutex_unlock(&capi_controller_lock); 1100 + 1101 + return retval; 1210 1102 } 1211 1103 case KCAPI_CMD_ADDCARD: 1212 1104 { ··· 1220 1100 struct capi_driver *driver = NULL; 1221 1101 capicardparams cparams; 1222 1102 kcapi_carddef cdef; 1223 - int retval; 1224 1103 1225 1104 if ((retval = copy_from_user(&cdef, data, sizeof(cdef)))) 1226 1105 return retval;
+4 -1
drivers/isdn/capi/kcapi.h
··· 24 24 #endif 25 25 26 26 enum { 27 + CAPI_CTR_DETACHED = 0, 27 28 CAPI_CTR_DETECTED = 1, 28 29 CAPI_CTR_LOADING = 2, 29 30 CAPI_CTR_RUNNING = 3, ··· 33 32 extern struct list_head capi_drivers; 34 33 extern struct mutex capi_drivers_lock; 35 34 36 - extern struct capi20_appl *capi_applications[CAPI_MAXAPPL]; 37 35 extern struct capi_ctr *capi_controller[CAPI_MAXCONTR]; 36 + extern struct mutex capi_controller_lock; 37 + 38 + extern struct capi20_appl *capi_applications[CAPI_MAXAPPL]; 38 39 39 40 #ifdef CONFIG_PROC_FS 40 41
+5
drivers/isdn/capi/kcapi_proc.c
··· 35 35 // --------------------------------------------------------------------------- 36 36 37 37 static void *controller_start(struct seq_file *seq, loff_t *pos) 38 + __acquires(capi_controller_lock) 38 39 { 40 + mutex_lock(&capi_controller_lock); 41 + 39 42 if (*pos < CAPI_MAXCONTR) 40 43 return &capi_controller[*pos]; 41 44 ··· 55 52 } 56 53 57 54 static void controller_stop(struct seq_file *seq, void *v) 55 + __releases(capi_controller_lock) 58 56 { 57 + mutex_unlock(&capi_controller_lock); 59 58 } 60 59 61 60 static int controller_show(struct seq_file *seq, void *v)
+3 -2
include/linux/isdn/capilli.h
··· 66 66 unsigned long nsentdatapkt; 67 67 68 68 int cnr; /* controller number */ 69 - volatile unsigned short state; /* controller state */ 70 - volatile int blocked; /* output blocked */ 69 + unsigned short state; /* controller state */ 70 + int blocked; /* output blocked */ 71 71 int traceflag; /* capi trace */ 72 + wait_queue_head_t state_wait_queue; 72 73 73 74 struct proc_dir_entry *procent; 74 75 char procfn[128];